Files
ofdgo/ofdgo_sign_ses.go
T
xiaoqidun b23ee90d31
build.yaml / build (push) Successful in 38s
feat(签名验证): 完善签名验证功能
2026-07-15 12:23:28 +08:00

743 lines
21 KiB
Go

// Copyright 2025-2026 肖其顿 (XIAO QI DUN)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ofdgo
import (
"bytes"
"encoding/asn1"
"fmt"
"math/big"
"strings"
"time"
)
const (
signDigestSM3 = "1.2.156.10197.1.401"
signDigestSM3NoKey = "1.2.156.10197.1.401.1"
signDigestSM3Key = "1.2.156.10197.1.401.2"
signMethodSM2SM3 = "1.2.156.10197.1.501"
signMethodSM2SM3B = "1.2.156.10197.501"
signMethodSM2Sign = "1.2.156.10197.1.301.1"
signCurveSM2P256 = "1.2.156.10197.1.301"
signECPublicKey = "1.2.840.10045.2.1"
signASN1Sequence = 16
signPublicKeySize = 65
)
// sesSignature SES签章值
type sesSignature struct {
ToSign []byte
Cert []byte
SignAlg string
Signature []byte
DataHash []byte
Time time.Time
Seal *sesSeal
}
// sesSeal SES电子印章
type sesSeal struct {
Raw []byte
SignData []byte
Cert []byte
SignAlg string
Signature []byte
PicType string
PicData []byte
CertList sesCertList
Info SignatureSealInfo
}
// sesCertList SES印章证书列表
type sesCertList struct {
Certs [][]byte
Digests []sesCertDigest
}
// sesCertDigest SES印章证书摘要
type sesCertDigest struct {
Method string
Value []byte
}
// sesVerifyResult SES签章验证结果
type sesVerifyResult struct {
DataHashOK bool
SignedOK bool
SealOK bool
CertOK bool
SignCert SignatureCertInfo
SealCert SignatureCertInfo
SignCertRaw []byte
SealCertRaw []byte
SealRaw []byte
Certs [][]byte
SealType string
SealInfo SignatureSealInfo
SignatureTime time.Time
}
// parseSESSignature 解析SES签章值
// 入参: data 签章值数据
// 返回: *sesSignature SES签章值, error 错误信息
func parseSESSignature(data []byte) (*sesSignature, error) {
var root asn1.RawValue
rest, err := asn1.Unmarshal(data, &root)
if err != nil {
return nil, err
}
if len(rest) != 0 || root.Tag != signASN1Sequence || !root.IsCompound {
return nil, fmt.Errorf("invalid ses signature")
}
items, ok := asn1Children(root.Bytes)
if !ok {
return nil, fmt.Errorf("invalid ses signature items")
}
if len(items) == 2 {
return parseSESSignatureV1(items)
}
if len(items) < 4 || len(items) > 5 {
return nil, fmt.Errorf("invalid ses signature items")
}
cert, err := asn1OctetString(items[1])
if err != nil {
return nil, err
}
alg, err := asn1OIDString(items[2])
if err != nil {
return nil, err
}
signature, err := asn1BitStringBytes(items[3])
if err != nil {
return nil, err
}
tbsItems, ok := asn1Children(items[0].Bytes)
if !ok || len(tbsItems) < 5 || len(tbsItems) > 6 {
return nil, fmt.Errorf("invalid ses toSign")
}
seal, err := parseSESSeal(tbsItems[1])
if err != nil {
return nil, err
}
version, err := asn1Integer(tbsItems[0])
if err != nil || version != seal.Info.Version {
return nil, fmt.Errorf("invalid ses version")
}
signatureTime, err := asn1Time(tbsItems[2])
if err != nil {
return nil, err
}
dataHash, err := asn1BitOrOctetBytes(tbsItems[3])
if err != nil {
return nil, err
}
return &sesSignature{
ToSign: append([]byte(nil), items[0].FullBytes...),
Cert: cert,
SignAlg: alg,
Signature: signature,
DataHash: dataHash,
Time: signatureTime,
Seal: seal,
}, nil
}
// parseSESSignatureV1 解析SES V1签章值
// 入参: items ASN.1子元素
// 返回: *sesSignature SES签章值, error 错误信息
func parseSESSignatureV1(items []asn1.RawValue) (*sesSignature, error) {
signature, err := asn1BitStringBytes(items[1])
if err != nil {
return nil, err
}
tbsItems, ok := asn1Children(items[0].Bytes)
if !ok || len(tbsItems) != 7 {
return nil, fmt.Errorf("invalid ses v1 toSign")
}
seal, err := parseSESSeal(tbsItems[1])
if err != nil {
return nil, err
}
version, err := asn1Integer(tbsItems[0])
if err != nil || version != seal.Info.Version {
return nil, fmt.Errorf("invalid ses version")
}
signatureTime := parseSESSignatureTimeV1(tbsItems[2])
dataHash, err := asn1BitOrOctetBytes(tbsItems[3])
if err != nil {
return nil, err
}
cert, err := asn1OctetString(tbsItems[5])
if err != nil {
return nil, err
}
alg, err := asn1OIDString(tbsItems[6])
if err != nil {
return nil, err
}
return &sesSignature{
ToSign: append([]byte(nil), items[0].FullBytes...),
Cert: cert,
SignAlg: alg,
Signature: signature,
DataHash: dataHash,
Time: signatureTime,
Seal: seal,
}, nil
}
// parseSESSignatureTimeV1 解析SES V1签名时间
// 入参: raw 签名时间或时间戳数据
// 返回: time.Time 签名时间
func parseSESSignatureTimeV1(raw asn1.RawValue) time.Time {
data, err := asn1BitOrOctetBytes(raw)
if err != nil {
return time.Time{}
}
if t := parseSignatureDateTime(string(data)); !t.IsZero() {
return t
}
var value asn1.RawValue
rest, err := asn1.Unmarshal(data, &value)
if err != nil || len(rest) != 0 {
return time.Time{}
}
t, _ := asn1Time(value)
return t
}
// verifySESSignature 验证SES签章值
// 入参: data 签章值数据, signedData 被签名数据原文, options 验证选项
// 返回: *sesVerifyResult 验证结果, error 错误信息
func verifySESSignature(data, signedData []byte, options *signatureVerifyOptions) (*sesVerifyResult, error) {
sig, err := parseSESSignature(data)
if err != nil {
return nil, err
}
if !isSM2SignatureMethod(sig.SignAlg) || !isSM2SignatureMethod(sig.Seal.SignAlg) {
return nil, fmt.Errorf("unsupported signature method")
}
result := &sesVerifyResult{}
result.SignCert = signatureCertInfo(sig.Cert)
result.SealCert = signatureCertInfo(sig.Seal.Cert)
result.SignCertRaw = sig.Cert
result.SealCertRaw = sig.Seal.Cert
result.SealRaw = sig.Seal.Raw
result.Certs = append(result.Certs, sig.Seal.CertList.Certs...)
result.Certs = append(result.Certs, options.SignCerts...)
result.SealType = sig.Seal.PicType
result.SealInfo = sig.Seal.Info
result.SignatureTime = sig.Time
result.DataHashOK = bytes.Equal(sig.DataHash, signSM3(signedData))
signPub, err := parseSM2PublicKeyFromCert(sig.Cert)
if err != nil {
return result, err
}
sealPub, err := parseSM2PublicKeyFromCert(sig.Seal.Cert)
if err != nil {
return result, err
}
result.SignedOK = sm2VerifySignature(signPub, nil, sig.ToSign, sig.Signature)
result.SealOK = sm2VerifySignature(sealPub, nil, sig.Seal.SignData, sig.Seal.Signature)
result.CertOK = sesCertInList(sig.Cert, sig.Seal.CertList)
return result, nil
}
// parseSESSeal 解析SES电子印章
// 入参: raw ASN.1原始值
// 返回: *sesSeal SES电子印章, error 错误信息
func parseSESSeal(raw asn1.RawValue) (*sesSeal, error) {
items, ok := asn1Children(raw.Bytes)
if !ok {
return nil, fmt.Errorf("invalid ses seal")
}
if len(items) == 2 {
return parseSESSealV1(raw, items)
}
if len(items) < 4 || len(items) > 5 {
return nil, fmt.Errorf("invalid ses seal")
}
cert, err := asn1OctetString(items[1])
if err != nil {
return nil, err
}
alg, err := asn1OIDString(items[2])
if err != nil {
return nil, err
}
signature, err := asn1BitStringBytes(items[3])
if err != nil {
return nil, err
}
infoItems, ok := asn1Children(items[0].Bytes)
if !ok || len(infoItems) < 4 || len(infoItems) > 5 {
return nil, fmt.Errorf("invalid ses seal info")
}
info, certList, err := parseSESSealInfo(infoItems)
if err != nil {
return nil, err
}
picType, picData, err := parseSESPicture(infoItems[3])
if err != nil {
return nil, err
}
return &sesSeal{
Raw: append([]byte(nil), raw.FullBytes...),
SignData: append([]byte(nil), items[0].FullBytes...),
Cert: cert,
SignAlg: alg,
Signature: signature,
PicType: picType,
PicData: picData,
CertList: certList,
Info: info,
}, nil
}
// parseSESSealV1 解析SES V1电子印章
// 入参: raw ASN.1原始值, items ASN.1子元素
// 返回: *sesSeal SES电子印章, error 错误信息
func parseSESSealV1(raw asn1.RawValue, items []asn1.RawValue) (*sesSeal, error) {
infoItems, ok := asn1Children(items[0].Bytes)
if !ok || len(infoItems) < 4 || len(infoItems) > 5 {
return nil, fmt.Errorf("invalid ses v1 seal info")
}
signItems, ok := asn1Children(items[1].Bytes)
if !ok || len(signItems) != 3 {
return nil, fmt.Errorf("invalid ses v1 sign info")
}
cert, err := asn1OctetString(signItems[0])
if err != nil {
return nil, err
}
alg, err := asn1OIDString(signItems[1])
if err != nil {
return nil, err
}
signature, err := asn1BitStringBytes(signItems[2])
if err != nil {
return nil, err
}
info, certList, err := parseSESSealInfo(infoItems)
if err != nil {
return nil, err
}
picType, picData, err := parseSESPicture(infoItems[3])
if err != nil {
return nil, err
}
signData := asn1SequenceBytes(items[0].FullBytes, signItems[0].FullBytes, signItems[1].FullBytes)
return &sesSeal{
Raw: append([]byte(nil), raw.FullBytes...),
SignData: signData,
Cert: cert,
SignAlg: alg,
Signature: signature,
PicType: picType,
PicData: picData,
CertList: certList,
Info: info,
}, nil
}
// parseSESSealInfo 解析电子印章信息
// 入参: items 印章信息ASN.1子元素
// 返回: SignatureSealInfo 印章信息, sesCertList 证书列表, error 错误信息
func parseSESSealInfo(items []asn1.RawValue) (SignatureSealInfo, sesCertList, error) {
if len(items) < 4 {
return SignatureSealInfo{}, sesCertList{}, fmt.Errorf("invalid ses seal info")
}
header, ok := asn1Children(items[0].Bytes)
if !ok || len(header) != 3 {
return SignatureSealInfo{}, sesCertList{}, fmt.Errorf("invalid ses header")
}
if asn1String(header[0]) != "ES" {
return SignatureSealInfo{}, sesCertList{}, fmt.Errorf("invalid ses header")
}
version, err := asn1Integer(header[1])
if err != nil {
return SignatureSealInfo{}, sesCertList{}, err
}
property, ok := asn1Children(items[2].Bytes)
if !ok {
return SignatureSealInfo{}, sesCertList{}, fmt.Errorf("invalid ses property")
}
certIndex := 2
timeIndex := 3
if version >= 4 {
certIndex = 3
timeIndex = 4
}
if len(property) != timeIndex+3 {
return SignatureSealInfo{}, sesCertList{}, fmt.Errorf("invalid ses property")
}
typeValue, err := asn1Integer(property[0])
if err != nil {
return SignatureSealInfo{}, sesCertList{}, err
}
certList, err := parseSESCertList(property, version, certIndex)
if err != nil {
return SignatureSealInfo{}, sesCertList{}, err
}
createTime, err := asn1Time(property[timeIndex])
if err != nil {
return SignatureSealInfo{}, sesCertList{}, err
}
validStart, err := asn1Time(property[timeIndex+1])
if err != nil {
return SignatureSealInfo{}, sesCertList{}, err
}
validEnd, err := asn1Time(property[timeIndex+2])
if err != nil {
return SignatureSealInfo{}, sesCertList{}, err
}
return SignatureSealInfo{
Version: version,
ID: strings.TrimSpace(asn1String(items[1])),
VendorID: strings.TrimSpace(asn1String(header[2])),
Type: typeValue,
Name: strings.TrimSpace(asn1String(property[1])),
CreateTime: createTime,
ValidStart: validStart,
ValidEnd: validEnd,
}, certList, nil
}
// parseSESCertList 解析印章证书列表
// 入参: property 印章属性, version 印章版本, certIndex 证书列表位置
// 返回: sesCertList 证书列表, error 错误信息
func parseSESCertList(property []asn1.RawValue, version, certIndex int) (sesCertList, error) {
if version == 1 {
return parseSESCertListV1(property[certIndex])
}
if version < 4 {
return parseSESCertInfoList(property[certIndex])
}
listType, err := asn1Integer(property[certIndex-1])
if err != nil {
return sesCertList{}, err
}
switch listType {
case 1:
return parseSESCertInfoList(property[certIndex])
case 2:
return parseSESCertDigestList(property[certIndex])
default:
return sesCertList{}, fmt.Errorf("unsupported ses cert list type")
}
}
// parseSESCertListV1 解析SES V1证书列表
// 入参: raw 证书列表ASN.1值
// 返回: sesCertList 证书列表, error 错误信息
func parseSESCertListV1(raw asn1.RawValue) (sesCertList, error) {
certs, ok := asn1Children(raw.Bytes)
if !ok || len(certs) == 0 {
return sesCertList{}, fmt.Errorf("invalid ses cert list")
}
list := sesCertList{Certs: make([][]byte, 0, len(certs))}
for _, item := range certs {
list.Certs = append(list.Certs, append([]byte(nil), item.FullBytes...))
}
return list, nil
}
// parseSESCertInfoList 解析SES证书信息列表
// 入参: raw 证书信息列表ASN.1值
// 返回: sesCertList 证书列表, error 错误信息
func parseSESCertInfoList(raw asn1.RawValue) (sesCertList, error) {
certs, ok := asn1Children(raw.Bytes)
if !ok || len(certs) == 0 {
return sesCertList{}, fmt.Errorf("invalid ses cert list")
}
list := sesCertList{Certs: make([][]byte, 0, len(certs))}
for _, item := range certs {
cert, err := asn1OctetString(item)
if err != nil {
return sesCertList{}, err
}
list.Certs = append(list.Certs, cert)
}
return list, nil
}
// parseSESCertDigestList 解析SES证书摘要列表
// 入参: raw 证书摘要列表ASN.1值
// 返回: sesCertList 证书列表, error 错误信息
func parseSESCertDigestList(raw asn1.RawValue) (sesCertList, error) {
items, ok := asn1Children(raw.Bytes)
if !ok || len(items) == 0 {
return sesCertList{}, fmt.Errorf("invalid ses cert digest list")
}
list := sesCertList{Digests: make([]sesCertDigest, 0, len(items))}
for _, item := range items {
fields, ok := asn1Children(item.Bytes)
if !ok || len(fields) != 2 {
return sesCertList{}, fmt.Errorf("invalid ses cert digest")
}
method := strings.TrimSpace(asn1String(fields[0]))
if method == "" {
return sesCertList{}, fmt.Errorf("invalid ses cert digest")
}
digest, err := asn1OctetString(fields[1])
if err != nil {
return sesCertList{}, err
}
list.Digests = append(list.Digests, sesCertDigest{Method: method, Value: digest})
}
return list, nil
}
// parseSESPicture 解析印章图片
// 入参: raw 印章图片信息
// 返回: string 图片类型, []byte 图片数据, error 错误信息
func parseSESPicture(raw asn1.RawValue) (string, []byte, error) {
items, ok := asn1Children(raw.Bytes)
if !ok || len(items) < 4 {
return "", nil, fmt.Errorf("invalid ses picture")
}
picType := normalizeSealType(strings.ToLower(strings.TrimSpace(asn1String(items[0]))))
picData, err := asn1OctetString(items[1])
if err != nil {
return "", nil, err
}
if picType == "ofd" {
if data := trimOFDPackage(picData); len(data) > 0 {
picData = data
}
}
return picType, picData, nil
}
// parseSM2PublicKeyFromCert 从证书解析SM2公钥
// 入参: data DER编码证书
// 返回: sm2PublicKey SM2公钥, error 错误信息
func parseSM2PublicKeyFromCert(data []byte) (sm2PublicKey, error) {
cert, err := parseSignatureCertificate(data)
if err != nil {
return sm2PublicKey{}, err
}
return parseSM2PublicKeyInfo(cert.PublicKey)
}
// parseSM2PublicKeyInfo 解析SM2公钥信息
// 入参: raw SubjectPublicKeyInfo原始值
// 返回: sm2PublicKey SM2公钥, error 错误信息
func parseSM2PublicKeyInfo(raw asn1.RawValue) (sm2PublicKey, error) {
var spki struct {
Algorithm asn1.RawValue
SubjectPublicKey asn1.BitString
}
rest, err := asn1.Unmarshal(raw.FullBytes, &spki)
if err != nil {
return sm2PublicKey{}, err
}
if len(rest) != 0 {
return sm2PublicKey{}, fmt.Errorf("invalid subject public key info")
}
algItems, ok := asn1Children(spki.Algorithm.Bytes)
if !ok || len(algItems) < 2 {
return sm2PublicKey{}, fmt.Errorf("invalid public key algorithm")
}
alg, err := asn1OIDString(algItems[0])
if err != nil {
return sm2PublicKey{}, err
}
curve, err := asn1OIDString(algItems[1])
if err != nil {
return sm2PublicKey{}, err
}
if alg != signECPublicKey || curve != signCurveSM2P256 {
return sm2PublicKey{}, fmt.Errorf("unsupported public key algorithm")
}
key := spki.SubjectPublicKey.Bytes
if len(key) != signPublicKeySize || key[0] != 4 {
return sm2PublicKey{}, fmt.Errorf("invalid sm2 public key")
}
return sm2PublicKey{
X: new(big.Int).SetBytes(key[1:33]),
Y: new(big.Int).SetBytes(key[33:65]),
}, nil
}
// signSM3 计算SM3摘要
// 入参: data 原文数据
// 返回: []byte 摘要值
func signSM3(data []byte) []byte {
h := newSM3()
h.Write(data)
return h.Sum(nil)
}
// isSM2SignatureMethod 判断是否为SM2签名算法
// 入参: method 算法标识
// 返回: bool 是否为SM2签名算法
func isSM2SignatureMethod(method string) bool {
switch signatureMethodText(method) {
case signMethodSM2SM3, signMethodSM2SM3B, signMethodSM2Sign, "SM2", "SM2SM3", "SM3SM2", "SM2WITHSM3", "SM3WITHSM2":
return true
default:
return false
}
}
// isSM3DigestMethod 判断是否为SM3摘要算法
// 入参: method 算法标识
// 返回: bool 是否为SM3摘要算法
func isSM3DigestMethod(method string) bool {
switch signatureMethodText(method) {
case signDigestSM3, signDigestSM3NoKey, signDigestSM3Key, "SM3":
return true
default:
return false
}
}
// sesCertInList 判断证书是否在印章证书列表中
// 入参: cert DER编码证书, list 证书列表
// 返回: bool 是否存在
func sesCertInList(cert []byte, list sesCertList) bool {
for _, item := range list.Certs {
if bytes.Equal(cert, item) {
return true
}
}
for _, item := range list.Digests {
digest, err := signatureDigest(item.Method, cert)
if err != nil {
continue
}
if bytes.Equal(digest, item.Value) {
return true
}
}
return false
}
// asn1OctetString 解析ASN.1八位字符串
// 入参: raw ASN.1原始值
// 返回: []byte 字节数据, error 错误信息
func asn1OctetString(raw asn1.RawValue) ([]byte, error) {
var out []byte
rest, err := asn1.Unmarshal(raw.FullBytes, &out)
if err != nil || len(rest) != 0 {
return nil, fmt.Errorf("invalid octet string")
}
return append([]byte(nil), out...), nil
}
// asn1BitStringBytes 解析ASN.1位字符串
// 入参: raw ASN.1原始值
// 返回: []byte 字节数据, error 错误信息
func asn1BitStringBytes(raw asn1.RawValue) ([]byte, error) {
var bits asn1.BitString
rest, err := asn1.Unmarshal(raw.FullBytes, &bits)
if err != nil || len(rest) != 0 || bits.BitLength%8 != 0 {
return nil, fmt.Errorf("invalid bit string")
}
return append([]byte(nil), bits.Bytes...), nil
}
// asn1BitOrOctetBytes 解析ASN.1位字符串或八位字符串
// 入参: raw ASN.1原始值
// 返回: []byte 字节数据, error 错误信息
func asn1BitOrOctetBytes(raw asn1.RawValue) ([]byte, error) {
if raw.Tag == asn1.TagOctetString {
return asn1OctetString(raw)
}
return asn1BitStringBytes(raw)
}
// asn1OIDString 解析ASN.1对象标识符
// 入参: raw ASN.1原始值
// 返回: string OID字符串, error 错误信息
func asn1OIDString(raw asn1.RawValue) (string, error) {
var oid asn1.ObjectIdentifier
rest, err := asn1.Unmarshal(raw.FullBytes, &oid)
if err != nil || len(rest) != 0 {
return "", fmt.Errorf("invalid oid")
}
return oid.String(), nil
}
// asn1Integer 解析ASN.1整数
// 入参: raw ASN.1原始值
// 返回: int 整数值, error 错误信息
func asn1Integer(raw asn1.RawValue) (int, error) {
var out int
rest, err := asn1.Unmarshal(raw.FullBytes, &out)
if err != nil || len(rest) != 0 {
return 0, fmt.Errorf("invalid integer")
}
return out, nil
}
// asn1String 解析ASN.1字符串
// 入参: raw ASN.1原始值
// 返回: string 字符串
func asn1String(raw asn1.RawValue) string {
var s string
if _, err := asn1.Unmarshal(raw.FullBytes, &s); err != nil {
return strings.TrimSpace(string(raw.Bytes))
}
return s
}
// asn1SequenceBytes 编码ASN.1序列
// 入参: parts 序列元素DER数据
// 返回: []byte ASN.1序列DER数据
func asn1SequenceBytes(parts ...[]byte) []byte {
var content []byte
for _, part := range parts {
content = append(content, part...)
}
return asn1Wrap(0x30, content)
}
// asn1SetBytes 编码ASN.1集合
// 入参: content 集合内容DER数据
// 返回: []byte ASN.1集合DER数据
func asn1SetBytes(content []byte) []byte {
return asn1Wrap(0x31, content)
}
// asn1Wrap 包装ASN.1标签
// 入参: tag ASN.1标签, content 内容DER数据
// 返回: []byte ASN.1 DER数据
func asn1Wrap(tag byte, content []byte) []byte {
out := []byte{tag}
out = append(out, asn1LengthBytes(len(content))...)
out = append(out, content...)
return out
}
// asn1LengthBytes 编码ASN.1长度
// 入参: n 内容长度
// 返回: []byte ASN.1长度编码
func asn1LengthBytes(n int) []byte {
if n < 128 {
return []byte{byte(n)}
}
var buf [8]byte
i := len(buf)
for n > 0 {
i--
buf[i] = byte(n)
n >>= 8
}
out := []byte{0x80 | byte(len(buf)-i)}
return append(out, buf[i:]...)
}