From 06c5cea4d16c6bb181de5a64b8f3ed7eda3f116b Mon Sep 17 00:00:00 2001 From: xiaoqidun Date: Wed, 8 Jul 2026 13:02:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E7=AD=BE=E5=90=8D=E9=AA=8C=E7=AD=BE):=20?= =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=AD=BE=E5=90=8D=E9=AA=8C=E7=AD=BE=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ofdgo_sign.go | 40 +++- ofdgo_sign_gbt.go | 100 +++++++-- ofdgo_sign_ses.go | 43 ++-- ofdgo_sign_verify.go | 492 +++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 617 insertions(+), 58 deletions(-) diff --git a/ofdgo_sign.go b/ofdgo_sign.go index 0227716..84a26cd 100644 --- a/ofdgo_sign.go +++ b/ofdgo_sign.go @@ -19,6 +19,8 @@ import ( "encoding/asn1" "encoding/binary" "encoding/xml" + "fmt" + "strconv" "strings" ) @@ -113,10 +115,17 @@ func (r *Reader) SignatureStampPositions(stamps []SignatureStamp) ([]SignatureSt pages := signaturePageNumbers(doc) positions := make([]SignatureStampPosition, 0, len(stamps)) for _, stamp := range stamps { - box, _ := ParseBox(stamp.Boundary) + page, ok := pages[stamp.PageRef] + if !ok { + return nil, fmt.Errorf("signature stamp page not found: %s", stamp.PageRef) + } + box, err := parseSignatureStampBox(stamp.Boundary) + if err != nil { + return nil, err + } positions = append(positions, SignatureStampPosition{ ID: stamp.ID, - Page: pages[stamp.PageRef], + Page: page, PageID: stamp.PageRef, Boundary: stamp.Boundary, Box: box, @@ -125,6 +134,33 @@ func (r *Reader) SignatureStampPositions(stamps []SignatureStamp) ([]SignatureSt return positions, nil } +// parseSignatureStampBox 解析签名外观区域 +// 入参: s 区域字符串 +// 返回: Box 矩形对象, error 错误信息 +func parseSignatureStampBox(s string) (Box, error) { + parts := strings.Fields(s) + if len(parts) != 4 { + return Box{}, fmt.Errorf("invalid signature stamp boundary: %s", s) + } + x, err := strconv.ParseFloat(parts[0], 64) + if err != nil { + return Box{}, fmt.Errorf("invalid signature stamp boundary: %s", s) + } + y, err := strconv.ParseFloat(parts[1], 64) + if err != nil { + return Box{}, fmt.Errorf("invalid signature stamp boundary: %s", s) + } + w, err := strconv.ParseFloat(parts[2], 64) + if err != nil { + return Box{}, fmt.Errorf("invalid signature stamp boundary: %s", s) + } + h, err := strconv.ParseFloat(parts[3], 64) + if err != nil { + return Box{}, fmt.Errorf("invalid signature stamp boundary: %s", s) + } + return Box{X: x, Y: y, W: w, H: h}, nil +} + // parseSignatures 解析签名文件 // 入参: doc 文档结构 // 返回: error 错误信息 diff --git a/ofdgo_sign_gbt.go b/ofdgo_sign_gbt.go index 04bb221..0744993 100644 --- a/ofdgo_sign_gbt.go +++ b/ofdgo_sign_gbt.go @@ -29,10 +29,13 @@ const ( // digitalVerifyResult 数字签名验证结果 type digitalVerifyResult struct { - DataHashOK bool - SignedOK bool - CertOK bool - CertInfo SignatureCertInfo + DataHashOK bool + SignedOK bool + CertOK bool + Cert []byte + SignerCerts [][]byte + Certs [][]byte + CertInfo SignatureCertInfo } // gbtSignedData GB/T 35275 SignedData结构 @@ -61,16 +64,19 @@ type gbtSignerInfo struct { } // verifyDigitalSignature 验证OFD数字签名 -// 入参: method 签名算法, signedValue 签名值, signedData 被签名原文, options 验证选项 +// 入参: method 签名算法, digestMethod 摘要算法, signedValue 签名值, signedData 被签名原文, options 验证选项 // 返回: *digitalVerifyResult 验证结果, error 错误信息 -func verifyDigitalSignature(method string, signedValue, signedData []byte, options *signatureVerifyOptions) (*digitalVerifyResult, error) { - if !isSM2SignatureMethod(method) { - return nil, fmt.Errorf("unsupported signature method") - } +func verifyDigitalSignature(method, digestMethod string, signedValue, signedData []byte, options *signatureVerifyOptions) (*digitalVerifyResult, error) { if isGBT35275SignedValue(signedValue) { return verifyGBT35275SignedData(signedValue, signedData, options) } - return verifyRawDigitalSignature(signedValue, signedData, options) + if isSM2SignatureMethod(method) { + return verifyRawDigitalSignature(signedValue, signedData, options) + } + if isRSASignatureMethod(method) || isECDSASignatureMethod(method) { + return verifyRawPublicKeySignature(method, digestMethod, signedValue, signedData, options) + } + return nil, fmt.Errorf("unsupported signature method: %s", method) } // verifyRawDigitalSignature 验证裸SM2数字签名 @@ -87,6 +93,8 @@ func verifyRawDigitalSignature(signedValue, signedData []byte, options *signatur continue } result.CertOK = true + result.Cert = cert + result.SignerCerts = [][]byte{cert} result.CertInfo = signatureCertInfo(cert) if sm2VerifySignature(pub, nil, signedData, signedValue) { result.SignedOK = true @@ -96,6 +104,31 @@ func verifyRawDigitalSignature(signedValue, signedData []byte, options *signatur return result, nil } +// verifyRawPublicKeySignature 验证裸RSA或ECDSA数字签名 +// 入参: method 签名算法, digestMethod 摘要算法, signedValue 签名值, signedData 被签名原文, options 验证选项 +// 返回: *digitalVerifyResult 验证结果, error 错误信息 +func verifyRawPublicKeySignature(method, digestMethod string, signedValue, signedData []byte, options *signatureVerifyOptions) (*digitalVerifyResult, error) { + if len(options.SignCerts) == 0 { + return nil, fmt.Errorf("signature certificate not found") + } + result := &digitalVerifyResult{DataHashOK: true} + for _, cert := range options.SignCerts { + ok, err := verifyPublicKeySignature(method, digestMethod, cert, signedData, signedValue) + if err != nil { + continue + } + result.CertOK = true + result.Cert = cert + result.SignerCerts = [][]byte{cert} + result.CertInfo = signatureCertInfo(cert) + if ok { + result.SignedOK = true + return result, nil + } + } + return result, nil +} + // verifyGBT35275SignedData 验证GB/T 35275 SignedData签名值 // 入参: signedValue 签名值, signedData 被签名原文, options 验证选项 // 返回: *digitalVerifyResult 验证结果, error 错误信息 @@ -113,16 +146,16 @@ func verifyGBT35275SignedData(signedValue, signedData []byte, options *signature if len(sd.Signers) == 0 { return nil, fmt.Errorf("invalid signed data signer info") } - result := &digitalVerifyResult{} - digest := signSM3(signedData) - if len(sd.ContentDigest) != 0 && !bytes.Equal(sd.ContentDigest, digest) { - return result, nil - } - result.DataHashOK = true + result := &digitalVerifyResult{Certs: sd.rawCerts()} for _, signer := range sd.Signers { - if !isSM3DigestMethod(signer.DigestAlg) { - return nil, fmt.Errorf("unsupported digest method") + digest, err := signatureDigest(signer.DigestAlg, signedData) + if err != nil { + return nil, err } + if len(sd.ContentDigest) != 0 && !bytes.Equal(sd.ContentDigest, digest) { + return result, nil + } + result.DataHashOK = true plain := sd.ContentDigest if len(signer.AuthAttrs) != 0 { if !bytes.Equal(signer.AttrDigest, digest) { @@ -138,16 +171,29 @@ func verifyGBT35275SignedData(signedValue, signedData []byte, options *signature if cert == nil { return result, nil } + result.Cert = cert.Raw + result.SignerCerts = append(result.SignerCerts, cert.Raw) result.CertInfo = signatureCertInfo(cert.Raw) - if !isSM2SignatureMethod(signer.SignatureAlg) { - return nil, fmt.Errorf("unsupported signature method") + if isSM2SignatureMethod(signer.SignatureAlg) { + pub, err := parseSM2PublicKeyFromCert(cert.Raw) + if err != nil { + return result, err + } + result.CertOK = true + if !sm2VerifySignature(pub, nil, plain, signer.Signature) { + return result, nil + } + continue } - pub, err := parseSM2PublicKeyFromCert(cert.Raw) + if !isRSASignatureMethod(signer.SignatureAlg) && !isECDSASignatureMethod(signer.SignatureAlg) { + return nil, fmt.Errorf("unsupported signature method: %s", signer.SignatureAlg) + } + ok, err := verifyPublicKeySignature(signer.SignatureAlg, signer.DigestAlg, cert.Raw, plain, signer.Signature) if err != nil { return result, err } result.CertOK = true - if !sm2VerifySignature(pub, nil, plain, signer.Signature) { + if !ok { return result, nil } } @@ -449,6 +495,16 @@ func (sd *gbtSignedData) findCert(issuer []byte, serial *big.Int) *gbtCertificat return nil } +// rawCerts 获取SignedData证书原文 +// 返回: [][]byte 证书列表 +func (sd *gbtSignedData) rawCerts() [][]byte { + certs := make([][]byte, 0, len(sd.Certs)) + for _, cert := range sd.Certs { + certs = append(certs, cert.Raw) + } + return certs +} + // asn1Explicit 解析显式标签内容 // 入参: raw ASN.1原始值 // 返回: asn1.RawValue 标签内容, error 错误信息 diff --git a/ofdgo_sign_ses.go b/ofdgo_sign_ses.go index 08b48cd..a14e2c3 100644 --- a/ofdgo_sign_ses.go +++ b/ofdgo_sign_ses.go @@ -65,13 +65,16 @@ type sesCertList struct { // sesVerifyResult SES签章验证结果 type sesVerifyResult struct { - DataHashOK bool - SignedOK bool - SealOK bool - CertOK bool - SignCert SignatureCertInfo - SealCert SignatureCertInfo - SealType string + DataHashOK bool + SignedOK bool + SealOK bool + CertOK bool + SignCert SignatureCertInfo + SealCert SignatureCertInfo + SignCertRaw []byte + SealCertRaw []byte + Certs [][]byte + SealType string } // parseSESSignature 解析SES签章值 @@ -169,9 +172,9 @@ func parseSESSignatureV1(items []asn1.RawValue) (*sesSignature, error) { } // verifySESSignature 验证SES签章值 -// 入参: data 签章值数据, signedData 被签名数据原文 +// 入参: data 签章值数据, signedData 被签名数据原文, options 验证选项 // 返回: *sesVerifyResult 验证结果, error 错误信息 -func verifySESSignature(data, signedData []byte) (*sesVerifyResult, error) { +func verifySESSignature(data, signedData []byte, options *signatureVerifyOptions) (*sesVerifyResult, error) { sig, err := parseSESSignature(data) if err != nil { return nil, err @@ -182,6 +185,10 @@ func verifySESSignature(data, signedData []byte) (*sesVerifyResult, error) { result := &sesVerifyResult{} result.SignCert = signatureCertInfo(sig.Cert) result.SealCert = signatureCertInfo(sig.Seal.Cert) + result.SignCertRaw = sig.Cert + result.SealCertRaw = sig.Seal.Cert + result.Certs = append(result.Certs, sig.Seal.CertList.Certs...) + result.Certs = append(result.Certs, options.SignCerts...) result.SealType = sig.Seal.PicType result.DataHashOK = bytes.Equal(sig.DataHash, signSM3(signedData)) signPub, err := parseSM2PublicKeyFromCert(sig.Cert) @@ -309,7 +316,7 @@ func parseSESHeaderVersion(raw asn1.RawValue) (int, error) { } // parseSESCertList 解析印章证书列表 -// 入参: raw 印章属性信息 +// 入参: raw 印章属性信息, version 印章版本 // 返回: sesCertList 证书列表, error 错误信息 func parseSESCertList(raw asn1.RawValue, version int) (sesCertList, error) { items, ok := asn1Children(raw.Bytes) @@ -497,16 +504,24 @@ func signSM3(data []byte) []byte { // 入参: method 算法标识 // 返回: bool 是否为SM2签名算法 func isSM2SignatureMethod(method string) bool { - method = strings.TrimSpace(method) - return method == signMethodSM2SM3 || method == signMethodSM2SM3B || method == signMethodSM2Sign + 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 { - method = strings.TrimSpace(method) - return method == signDigestSM3 || method == signDigestSM3NoKey || method == signDigestSM3Key || method == "SM3" + switch signatureMethodText(method) { + case signDigestSM3, signDigestSM3NoKey, signDigestSM3Key, "SM3": + return true + default: + return false + } } // sesCertInList 判断证书是否在印章证书列表中 diff --git a/ofdgo_sign_verify.go b/ofdgo_sign_verify.go index 61a3dfb..5b1ddc8 100644 --- a/ofdgo_sign_verify.go +++ b/ofdgo_sign_verify.go @@ -16,15 +16,24 @@ package ofdgo import ( "bytes" + "crypto" + "crypto/ecdsa" + "crypto/rsa" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" "crypto/subtle" + "crypto/x509" "encoding/asn1" "encoding/base64" "encoding/pem" "encoding/xml" "fmt" "io" + "math/big" "path" "strings" + "time" ) // SignatureVerifyReport 签名验证报告 @@ -42,12 +51,17 @@ type SignatureVerifyReport struct { DigestMethod string References []SignatureReferenceVerify Stamps []SignatureStamp + StampPositions []SignatureStampPosition DigestOK bool DataHashOK bool SignedValueOK bool SealOK bool SealMatchOK bool CertOK bool + CertTimeChecked bool + CertTimeOK bool + CertTrustChecked bool + CertTrustOK bool Valid bool Error string } @@ -59,6 +73,8 @@ type SignatureCertInfo struct { Organization string Issuer string SerialNumber string + NotBefore time.Time + NotAfter time.Time } // SignatureReferenceVerify 签名保护文件验证结果 @@ -73,7 +89,9 @@ type SignatureReferenceVerify struct { // signatureVerifyOptions 签名验证选项 type signatureVerifyOptions struct { - SignCerts [][]byte + SignCerts [][]byte + TrustCerts [][]byte + VerifyTime *time.Time } // SignatureVerifyOption 签名验证选项函数 @@ -99,6 +117,35 @@ func WithSignatureCerts(certs ...[]byte) SignatureVerifyOption { } } +// WithSignatureTrustCert 添加签名信任证书 +// 入参: cert DER或PEM编码证书 +// 返回: SignatureVerifyOption 签名验证选项 +func WithSignatureTrustCert(cert []byte) SignatureVerifyOption { + return func(o *signatureVerifyOptions) { + o.TrustCerts = append(o.TrustCerts, parseSignatureCerts(cert)...) + } +} + +// WithSignatureTrustCerts 添加多张签名信任证书 +// 入参: certs DER或PEM编码证书列表 +// 返回: SignatureVerifyOption 签名验证选项 +func WithSignatureTrustCerts(certs ...[]byte) SignatureVerifyOption { + return func(o *signatureVerifyOptions) { + for _, cert := range certs { + o.TrustCerts = append(o.TrustCerts, parseSignatureCerts(cert)...) + } + } +} + +// WithSignatureVerifyTime 设置签名证书验证时间 +// 入参: t 验证时间 +// 返回: SignatureVerifyOption 签名验证选项 +func WithSignatureVerifyTime(t time.Time) SignatureVerifyOption { + return func(o *signatureVerifyOptions) { + o.VerifyTime = &t + } +} + // VerifySignaturesBytes 验证OFD字节数据签名 // 入参: data OFD字节数据, opts 签名验证选项 // 返回: []SignatureVerifyReport 签名验证报告, error 错误信息 @@ -186,6 +233,11 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options * report.DigestMethod = sigFile.SignedInfo.References.CheckMethod report.References = r.verifySignatureReferences(sigPath, sigFile.SignedInfo.References) report.Stamps = append(report.Stamps, sigFile.SignedInfo.StampAnnot...) + report.StampPositions, err = r.SignatureStampPositions(report.Stamps) + if err != nil { + report.Error = err.Error() + return report + } report.DigestOK = referencesOK(report.References) signedValuePath := signatureRefPath(sigPath, sigFile.SignedValue) signedValue, err := r.readFileExact(signedValuePath) @@ -195,7 +247,7 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options * } switch sigRef.Type { case SignTypeSign: - result, err := verifyDigitalSignature(report.SignatureMethod, signedValue, sigData, options) + result, err := verifyDigitalSignature(report.SignatureMethod, report.DigestMethod, signedValue, sigData, options) if err != nil { report.Error = err.Error() return report @@ -206,14 +258,15 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options * report.CertOK = result.CertOK report.SignCert = result.CertInfo report.Signer = result.CertInfo.CommonName - report.Valid = report.DigestOK && report.DataHashOK && report.SignedValueOK && report.CertOK + report.applySignatureCertificatePolicy(options, result.SignerCerts, result.Certs) + report.Valid = report.DigestOK && report.DataHashOK && report.SignedValueOK && report.CertOK && report.certificatePolicyOK() return report case "", SignTypeSeal: default: report.Error = fmt.Sprintf("unsupported signature type: %s", sigRef.Type) return report } - sesResult, err := verifySESSignature(signedValue, sigData) + sesResult, err := verifySESSignature(signedValue, sigData, options) if sesResult != nil { report.SignCert = sesResult.SignCert report.SealCert = sesResult.SealCert @@ -228,6 +281,7 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options * report.SignedValueOK = sesResult.SignedOK report.SealOK = sesResult.SealOK report.CertOK = sesResult.CertOK + report.applySignatureCertificatePolicy(options, [][]byte{sesResult.SignCertRaw, sesResult.SealCertRaw}, sesResult.Certs) if sigFile.SignedInfo.Seal.BaseLoc != "" { sealPath := signatureRefPath(sigPath, sigFile.SignedInfo.Seal.BaseLoc) sealData, err := r.readFileExact(sealPath) @@ -242,7 +296,7 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options * } report.SealMatchOK = bytes.Equal(sealData, sig.Seal.Raw) } - report.Valid = report.DigestOK && report.DataHashOK && report.SignedValueOK && report.SealOK && report.SealMatchOK && report.CertOK + report.Valid = report.DigestOK && report.DataHashOK && report.SignedValueOK && report.SealOK && report.SealMatchOK && report.CertOK && report.certificatePolicyOK() return report } @@ -321,9 +375,190 @@ func signatureDigest(method string, data []byte) ([]byte, error) { if isSM3DigestMethod(method) { return signSM3(data), nil } + if h, ok := signatureDigestHash(method); ok { + return signatureHashBytes(h, data), nil + } return nil, fmt.Errorf("unsupported digest method: %s", method) } +// signatureDigestHash 获取摘要算法 +// 入参: method 摘要算法 +// 返回: crypto.Hash 摘要算法, bool 是否支持 +func signatureDigestHash(method string) (crypto.Hash, bool) { + switch signatureMethodText(method) { + case "1.3.14.3.2.26", "SHA1": + return crypto.SHA1, true + case "2.16.840.1.101.3.4.2.4", "SHA224": + return crypto.SHA224, true + case "2.16.840.1.101.3.4.2.1", "SHA256": + return crypto.SHA256, true + case "2.16.840.1.101.3.4.2.2", "SHA384": + return crypto.SHA384, true + case "2.16.840.1.101.3.4.2.3", "SHA512": + return crypto.SHA512, true + case "2.16.840.1.101.3.4.2.5", "SHA512224": + return crypto.SHA512_224, true + case "2.16.840.1.101.3.4.2.6", "SHA512256": + return crypto.SHA512_256, true + default: + return 0, false + } +} + +// signatureMethodHash 获取签名算法对应摘要算法 +// 入参: method 签名算法, digestMethod 摘要算法 +// 返回: crypto.Hash 摘要算法, error 错误信息 +func signatureMethodHash(method, digestMethod string) (crypto.Hash, error) { + switch signatureMethodText(method) { + case "1.2.840.113549.1.1.5", "RSASHA1", "SHA1RSA", "SHA1WITHRSA": + return crypto.SHA1, nil + case "1.2.840.113549.1.1.14", "RSASHA224", "SHA224RSA", "SHA224WITHRSA": + return crypto.SHA224, nil + case "1.2.840.113549.1.1.11", "RSASHA256", "SHA256RSA", "SHA256WITHRSA": + return crypto.SHA256, nil + case "1.2.840.113549.1.1.12", "RSASHA384", "SHA384RSA", "SHA384WITHRSA": + return crypto.SHA384, nil + case "1.2.840.113549.1.1.13", "RSASHA512", "SHA512RSA", "SHA512WITHRSA": + return crypto.SHA512, nil + case "1.2.840.10045.4.1", "ECDSASHA1", "SHA1ECDSA", "SHA1WITHECDSA": + return crypto.SHA1, nil + case "1.2.840.10045.4.3.1", "ECDSASHA224", "SHA224ECDSA", "SHA224WITHECDSA": + return crypto.SHA224, nil + case "1.2.840.10045.4.3.2", "ECDSASHA256", "SHA256ECDSA", "SHA256WITHECDSA": + return crypto.SHA256, nil + case "1.2.840.10045.4.3.3", "ECDSASHA384", "SHA384ECDSA", "SHA384WITHECDSA": + return crypto.SHA384, nil + case "1.2.840.10045.4.3.4", "ECDSASHA512", "SHA512ECDSA", "SHA512WITHECDSA": + return crypto.SHA512, nil + } + if h, ok := signatureDigestHash(digestMethod); ok { + return h, nil + } + return 0, fmt.Errorf("unsupported signature method: %s", method) +} + +// signatureHashBytes 计算摘要 +// 入参: h 摘要算法, data 原文数据 +// 返回: []byte 摘要值 +func signatureHashBytes(h crypto.Hash, data []byte) []byte { + switch h { + case crypto.SHA1: + sum := sha1.Sum(data) + return sum[:] + case crypto.SHA224: + sum := sha256.Sum224(data) + return sum[:] + case crypto.SHA256: + sum := sha256.Sum256(data) + return sum[:] + case crypto.SHA384: + sum := sha512.Sum384(data) + return sum[:] + case crypto.SHA512: + sum := sha512.Sum512(data) + return sum[:] + case crypto.SHA512_224: + sum := sha512.Sum512_224(data) + return sum[:] + case crypto.SHA512_256: + sum := sha512.Sum512_256(data) + return sum[:] + default: + return nil + } +} + +// isRSASignatureMethod 判断是否为RSA签名算法 +// 入参: method 算法标识 +// 返回: bool 是否为RSA签名算法 +func isRSASignatureMethod(method string) bool { + switch signatureMethodText(method) { + case "1.2.840.113549.1.1.1", "1.2.840.113549.1.1.5", "1.2.840.113549.1.1.11", "1.2.840.113549.1.1.12", "1.2.840.113549.1.1.13", "1.2.840.113549.1.1.14", "RSA", "RSASHA1", "RSASHA224", "RSASHA256", "RSASHA384", "RSASHA512", "SHA1RSA", "SHA224RSA", "SHA256RSA", "SHA384RSA", "SHA512RSA", "SHA1WITHRSA", "SHA224WITHRSA", "SHA256WITHRSA", "SHA384WITHRSA", "SHA512WITHRSA": + return true + default: + return false + } +} + +// isECDSASignatureMethod 判断是否为ECDSA签名算法 +// 入参: method 算法标识 +// 返回: bool 是否为ECDSA签名算法 +func isECDSASignatureMethod(method string) bool { + switch signatureMethodText(method) { + case "1.2.840.10045.4.1", "1.2.840.10045.4.3.1", "1.2.840.10045.4.3.2", "1.2.840.10045.4.3.3", "1.2.840.10045.4.3.4", "ECDSA", "ECDSASHA1", "ECDSASHA224", "ECDSASHA256", "ECDSASHA384", "ECDSASHA512", "SHA1ECDSA", "SHA224ECDSA", "SHA256ECDSA", "SHA384ECDSA", "SHA512ECDSA", "SHA1WITHECDSA", "SHA224WITHECDSA", "SHA256WITHECDSA", "SHA384WITHECDSA", "SHA512WITHECDSA": + return true + default: + return false + } +} + +// signatureMethodText 规范化算法标识 +// 入参: method 算法标识 +// 返回: string 规范化算法标识 +func signatureMethodText(method string) string { + method = strings.TrimSpace(method) + if strings.HasPrefix(strings.ToLower(method), "urn:oid:") { + method = method[len("urn:oid:"):] + } + if idx := strings.LastIndexAny(method, "#/"); idx >= 0 && idx+1 < len(method) { + method = method[idx+1:] + } + method = strings.ToUpper(method) + method = strings.NewReplacer("-", "", "_", "", " ", "").Replace(method) + return method +} + +// verifyPublicKeySignature 验证公钥签名 +// 入参: method 签名算法, digestMethod 摘要算法, cert 证书, signedData 被签名数据, signedValue 签名值 +// 返回: bool 是否验证通过, error 错误信息 +func verifyPublicKeySignature(method, digestMethod string, cert, signedData, signedValue []byte) (bool, error) { + if !isRSASignatureMethod(method) && !isECDSASignatureMethod(method) { + return false, fmt.Errorf("unsupported signature method: %s", method) + } + h, err := signatureMethodHash(method, digestMethod) + if err != nil { + return false, err + } + digest := signatureHashBytes(h, signedData) + if len(digest) == 0 { + return false, fmt.Errorf("unsupported digest method") + } + x509Cert, err := x509.ParseCertificate(cert) + if err != nil { + return false, err + } + switch pub := x509Cert.PublicKey.(type) { + case *rsa.PublicKey: + if !isRSASignatureMethod(method) { + return false, nil + } + return rsa.VerifyPKCS1v15(pub, h, digest, signedValue) == nil, nil + case *ecdsa.PublicKey: + if !isECDSASignatureMethod(method) { + return false, nil + } + return verifyECDSASignature(pub, digest, signedValue), nil + default: + return false, fmt.Errorf("unsupported public key algorithm") + } +} + +// verifyECDSASignature 验证ECDSA签名 +// 入参: pub 公钥, digest 摘要, sig 签名值 +// 返回: bool 是否验证通过 +func verifyECDSASignature(pub *ecdsa.PublicKey, digest, sig []byte) bool { + if ecdsa.VerifyASN1(pub, digest, sig) { + return true + } + if len(sig) == 0 || len(sig)%2 != 0 { + return false + } + n := len(sig) / 2 + r := new(big.Int).SetBytes(sig[:n]) + s := new(big.Int).SetBytes(sig[n:]) + return ecdsa.Verify(pub, digest, r, s) +} + // signatureRefPath 解析签名文件引用路径 // 入参: basePath 基准路径, refPath 引用路径 // 返回: string 包内文件路径 @@ -351,6 +586,138 @@ func referencesOK(refs []SignatureReferenceVerify) bool { return true } +// applySignatureCertificatePolicy 应用签名证书策略 +// 入参: options 验证选项, certs 待验证证书, extraCerts 证书池 +func (report *SignatureVerifyReport) applySignatureCertificatePolicy(options *signatureVerifyOptions, certs [][]byte, extraCerts [][]byte) { + certs = compactSignatureCerts(certs) + if options.VerifyTime != nil { + report.CertTimeChecked = true + report.CertTimeOK = signatureCertsValidAt(certs, *options.VerifyTime) + } + if len(options.TrustCerts) != 0 { + report.CertTrustChecked = true + report.CertTrustOK = true + for _, cert := range certs { + if !signatureCertTrusted(cert, options, extraCerts) { + report.CertTrustOK = false + break + } + } + if len(certs) == 0 { + report.CertTrustOK = false + } + } +} + +// certificatePolicyOK 判断证书策略是否通过 +// 返回: bool 是否通过 +func (report SignatureVerifyReport) certificatePolicyOK() bool { + if report.CertTimeChecked && !report.CertTimeOK { + return false + } + if report.CertTrustChecked && !report.CertTrustOK { + return false + } + return true +} + +// signatureCertsValidAt 判断证书是否在指定时间有效 +// 入参: certs 证书列表, t 验证时间 +// 返回: bool 是否有效 +func signatureCertsValidAt(certs [][]byte, t time.Time) bool { + if len(certs) == 0 { + return false + } + for _, cert := range certs { + info, err := parseSignatureCertificate(cert) + if err != nil || t.Before(info.NotBefore) || t.After(info.NotAfter) { + return false + } + } + return true +} + +// signatureCertTrusted 判断证书是否受信任 +// 入参: cert 证书, options 验证选项, extraCerts 额外证书池 +// 返回: bool 是否受信任 +func signatureCertTrusted(cert []byte, options *signatureVerifyOptions, extraCerts [][]byte) bool { + pool := compactSignatureCerts(append(append(append([][]byte{}, options.SignCerts...), extraCerts...), options.TrustCerts...)) + return signatureCertTrustedBy(cert, pool, options.TrustCerts, make(map[string]bool)) +} + +// signatureCertTrustedBy 判断证书是否可链到信任证书 +// 入参: cert 证书, pool 证书池, trusts 信任证书, visited 已访问证书 +// 返回: bool 是否受信任 +func signatureCertTrustedBy(cert []byte, pool, trusts [][]byte, visited map[string]bool) bool { + if len(cert) == 0 { + return false + } + for _, trust := range trusts { + if bytes.Equal(cert, trust) { + return true + } + } + key := string(cert) + if visited[key] { + return false + } + visited[key] = true + c, err := parseSignatureCertificate(cert) + if err != nil { + return false + } + for _, issuerCert := range pool { + if bytes.Equal(cert, issuerCert) { + continue + } + issuer, err := parseSignatureCertificate(issuerCert) + if err != nil || !bytes.Equal(c.Issuer, issuer.Subject) { + continue + } + if ok, err := verifyCertificateSignature(c, issuerCert); err != nil || !ok { + continue + } + if signatureCertTrustedBy(issuerCert, pool, trusts, visited) { + return true + } + } + return false +} + +// verifyCertificateSignature 验证证书签名 +// 入参: cert 证书信息, issuerCert 颁发者证书 +// 返回: bool 是否验证通过, error 错误信息 +func verifyCertificateSignature(cert signatureCertificate, issuerCert []byte) (bool, error) { + if isSM2SignatureMethod(cert.SignatureAlg) { + pub, err := parseSM2PublicKeyFromCert(issuerCert) + if err != nil { + return false, err + } + return sm2VerifySignature(pub, nil, cert.TBS, cert.Signature), nil + } + return verifyPublicKeySignature(cert.SignatureAlg, "", issuerCert, cert.TBS, cert.Signature) +} + +// compactSignatureCerts 清理证书列表 +// 入参: certs 证书列表 +// 返回: [][]byte 清理后的证书列表 +func compactSignatureCerts(certs [][]byte) [][]byte { + out := make([][]byte, 0, len(certs)) + seen := make(map[string]bool) + for _, cert := range certs { + if len(cert) == 0 { + continue + } + key := string(cert) + if seen[key] { + continue + } + seen[key] = true + out = append(out, cert) + } + return out +} + // xmlElementRaw 提取XML元素原始字节 // 入参: data XML数据, localName 元素名称 // 返回: []byte 元素原始字节, error 错误信息 @@ -395,6 +762,45 @@ func xmlElementRaw(data []byte, localName string) ([]byte, error) { // 入参: data DER编码证书 // 返回: SignatureCertInfo 签名证书信息 func signatureCertInfo(data []byte) SignatureCertInfo { + cert, err := parseSignatureCertificate(data) + if err != nil { + return SignatureCertInfo{} + } + subject := certificateNameValues(cert.SubjectValue) + issuer := certificateNameValues(cert.IssuerValue) + info := SignatureCertInfo{ + Subject: certificateNameString(subject), + CommonName: certificateNameFirst(subject, "2.5.4.3"), + Organization: certificateNameFirst(subject, "2.5.4.10"), + Issuer: certificateNameString(issuer), + NotBefore: cert.NotBefore, + NotAfter: cert.NotAfter, + } + if cert.Serial != nil { + info.SerialNumber = cert.Serial.String() + } + return info +} + +// signatureCertificate 签名证书结构 +type signatureCertificate struct { + Raw []byte + TBS []byte + Issuer []byte + IssuerValue asn1.RawValue + Subject []byte + SubjectValue asn1.RawValue + Serial *big.Int + NotBefore time.Time + NotAfter time.Time + SignatureAlg string + Signature []byte +} + +// parseSignatureCertificate 解析签名证书 +// 入参: data DER编码证书 +// 返回: signatureCertificate 签名证书结构, error 错误信息 +func parseSignatureCertificate(data []byte) (signatureCertificate, error) { var cert struct { TBSCertificate asn1.RawValue SignatureAlgorithm asn1.RawValue @@ -402,32 +808,78 @@ func signatureCertInfo(data []byte) SignatureCertInfo { } rest, err := asn1.Unmarshal(data, &cert) if err != nil || len(rest) != 0 { - return SignatureCertInfo{} + return signatureCertificate{}, fmt.Errorf("invalid certificate") } items, ok := asn1Children(cert.TBSCertificate.Bytes) if !ok { - return SignatureCertInfo{} + return signatureCertificate{}, fmt.Errorf("invalid tbs certificate") } idx := 0 if len(items) > 0 && items[0].Class == asn1.ClassContextSpecific && items[0].Tag == 0 { idx++ } - if len(items) <= idx+4 { - return SignatureCertInfo{} + if len(items) <= idx+5 { + return signatureCertificate{}, fmt.Errorf("invalid certificate") } - serial, _ := asn1IntegerBig(items[idx]) - subject := certificateNameValues(items[idx+4]) - issuer := certificateNameValues(items[idx+2]) - info := SignatureCertInfo{ - Subject: certificateNameString(subject), - CommonName: certificateNameFirst(subject, "2.5.4.3"), - Organization: certificateNameFirst(subject, "2.5.4.10"), - Issuer: certificateNameString(issuer), + serial, err := asn1IntegerBig(items[idx]) + if err != nil { + return signatureCertificate{}, err } - if serial != nil { - info.SerialNumber = serial.String() + validity, err := parseCertificateValidity(items[idx+3]) + if err != nil { + return signatureCertificate{}, err } - return info + alg, err := parseGBTAlgorithm(cert.SignatureAlgorithm) + if err != nil { + return signatureCertificate{}, err + } + if cert.SignatureValue.BitLength%8 != 0 { + return signatureCertificate{}, fmt.Errorf("invalid certificate signature") + } + return signatureCertificate{ + Raw: append([]byte(nil), data...), + TBS: append([]byte(nil), cert.TBSCertificate.FullBytes...), + Issuer: append([]byte(nil), items[idx+2].FullBytes...), + IssuerValue: items[idx+2], + Subject: append([]byte(nil), items[idx+4].FullBytes...), + SubjectValue: items[idx+4], + Serial: serial, + NotBefore: validity[0], + NotAfter: validity[1], + SignatureAlg: alg, + Signature: append([]byte(nil), cert.SignatureValue.Bytes...), + }, nil +} + +// parseCertificateValidity 解析证书有效期 +// 入参: raw 证书有效期ASN.1值 +// 返回: [2]time.Time 生效和失效时间, error 错误信息 +func parseCertificateValidity(raw asn1.RawValue) ([2]time.Time, error) { + items, ok := asn1Children(raw.Bytes) + if !ok || len(items) != 2 { + return [2]time.Time{}, fmt.Errorf("invalid certificate validity") + } + notBefore, err := asn1Time(items[0]) + if err != nil { + return [2]time.Time{}, err + } + notAfter, err := asn1Time(items[1]) + if err != nil { + return [2]time.Time{}, err + } + return [2]time.Time{notBefore, notAfter}, nil +} + +// asn1Time 解析ASN.1时间 +// 入参: raw ASN.1原始值 +// 返回: time.Time 时间, error 错误信息 +func asn1Time(raw asn1.RawValue) (time.Time, error) { + var t time.Time + rest, err := asn1.Unmarshal(raw.FullBytes, &t) + if err != nil || len(rest) != 0 { + return time.Time{}, fmt.Errorf("invalid time") + } + return t, nil } // certificateNameValues 解析证书名称字段