feat(签名验签): 完善签名验签功能
build.yaml / build (push) Successful in 33s

This commit is contained in:
2026-07-08 13:02:08 +08:00
parent 6bfb11df71
commit 06c5cea4d1
4 changed files with 617 additions and 58 deletions
+38 -2
View File
@@ -19,6 +19,8 @@ import (
"encoding/asn1" "encoding/asn1"
"encoding/binary" "encoding/binary"
"encoding/xml" "encoding/xml"
"fmt"
"strconv"
"strings" "strings"
) )
@@ -113,10 +115,17 @@ func (r *Reader) SignatureStampPositions(stamps []SignatureStamp) ([]SignatureSt
pages := signaturePageNumbers(doc) pages := signaturePageNumbers(doc)
positions := make([]SignatureStampPosition, 0, len(stamps)) positions := make([]SignatureStampPosition, 0, len(stamps))
for _, stamp := range 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{ positions = append(positions, SignatureStampPosition{
ID: stamp.ID, ID: stamp.ID,
Page: pages[stamp.PageRef], Page: page,
PageID: stamp.PageRef, PageID: stamp.PageRef,
Boundary: stamp.Boundary, Boundary: stamp.Boundary,
Box: box, Box: box,
@@ -125,6 +134,33 @@ func (r *Reader) SignatureStampPositions(stamps []SignatureStamp) ([]SignatureSt
return positions, nil 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 解析签名文件 // parseSignatures 解析签名文件
// 入参: doc 文档结构 // 入参: doc 文档结构
// 返回: error 错误信息 // 返回: error 错误信息
+70 -14
View File
@@ -32,6 +32,9 @@ type digitalVerifyResult struct {
DataHashOK bool DataHashOK bool
SignedOK bool SignedOK bool
CertOK bool CertOK bool
Cert []byte
SignerCerts [][]byte
Certs [][]byte
CertInfo SignatureCertInfo CertInfo SignatureCertInfo
} }
@@ -61,17 +64,20 @@ type gbtSignerInfo struct {
} }
// verifyDigitalSignature 验证OFD数字签名 // verifyDigitalSignature 验证OFD数字签名
// 入参: method 签名算法, signedValue 签名值, signedData 被签名原文, options 验证选项 // 入参: method 签名算法, digestMethod 摘要算法, signedValue 签名值, signedData 被签名原文, options 验证选项
// 返回: *digitalVerifyResult 验证结果, error 错误信息 // 返回: *digitalVerifyResult 验证结果, error 错误信息
func verifyDigitalSignature(method string, signedValue, signedData []byte, options *signatureVerifyOptions) (*digitalVerifyResult, error) { func verifyDigitalSignature(method, digestMethod string, signedValue, signedData []byte, options *signatureVerifyOptions) (*digitalVerifyResult, error) {
if !isSM2SignatureMethod(method) {
return nil, fmt.Errorf("unsupported signature method")
}
if isGBT35275SignedValue(signedValue) { if isGBT35275SignedValue(signedValue) {
return verifyGBT35275SignedData(signedValue, signedData, options) return verifyGBT35275SignedData(signedValue, signedData, options)
} }
if isSM2SignatureMethod(method) {
return verifyRawDigitalSignature(signedValue, signedData, options) 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数字签名 // verifyRawDigitalSignature 验证裸SM2数字签名
// 入参: signedValue 签名值, signedData 被签名原文, options 验证选项 // 入参: signedValue 签名值, signedData 被签名原文, options 验证选项
@@ -87,6 +93,8 @@ func verifyRawDigitalSignature(signedValue, signedData []byte, options *signatur
continue continue
} }
result.CertOK = true result.CertOK = true
result.Cert = cert
result.SignerCerts = [][]byte{cert}
result.CertInfo = signatureCertInfo(cert) result.CertInfo = signatureCertInfo(cert)
if sm2VerifySignature(pub, nil, signedData, signedValue) { if sm2VerifySignature(pub, nil, signedData, signedValue) {
result.SignedOK = true result.SignedOK = true
@@ -96,6 +104,31 @@ func verifyRawDigitalSignature(signedValue, signedData []byte, options *signatur
return result, nil 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签名值 // verifyGBT35275SignedData 验证GB/T 35275 SignedData签名值
// 入参: signedValue 签名值, signedData 被签名原文, options 验证选项 // 入参: signedValue 签名值, signedData 被签名原文, options 验证选项
// 返回: *digitalVerifyResult 验证结果, error 错误信息 // 返回: *digitalVerifyResult 验证结果, error 错误信息
@@ -113,16 +146,16 @@ func verifyGBT35275SignedData(signedValue, signedData []byte, options *signature
if len(sd.Signers) == 0 { if len(sd.Signers) == 0 {
return nil, fmt.Errorf("invalid signed data signer info") return nil, fmt.Errorf("invalid signed data signer info")
} }
result := &digitalVerifyResult{} result := &digitalVerifyResult{Certs: sd.rawCerts()}
digest := signSM3(signedData) for _, signer := range sd.Signers {
digest, err := signatureDigest(signer.DigestAlg, signedData)
if err != nil {
return nil, err
}
if len(sd.ContentDigest) != 0 && !bytes.Equal(sd.ContentDigest, digest) { if len(sd.ContentDigest) != 0 && !bytes.Equal(sd.ContentDigest, digest) {
return result, nil return result, nil
} }
result.DataHashOK = true result.DataHashOK = true
for _, signer := range sd.Signers {
if !isSM3DigestMethod(signer.DigestAlg) {
return nil, fmt.Errorf("unsupported digest method")
}
plain := sd.ContentDigest plain := sd.ContentDigest
if len(signer.AuthAttrs) != 0 { if len(signer.AuthAttrs) != 0 {
if !bytes.Equal(signer.AttrDigest, digest) { if !bytes.Equal(signer.AttrDigest, digest) {
@@ -138,10 +171,10 @@ func verifyGBT35275SignedData(signedValue, signedData []byte, options *signature
if cert == nil { if cert == nil {
return result, nil return result, nil
} }
result.Cert = cert.Raw
result.SignerCerts = append(result.SignerCerts, cert.Raw)
result.CertInfo = signatureCertInfo(cert.Raw) result.CertInfo = signatureCertInfo(cert.Raw)
if !isSM2SignatureMethod(signer.SignatureAlg) { if isSM2SignatureMethod(signer.SignatureAlg) {
return nil, fmt.Errorf("unsupported signature method")
}
pub, err := parseSM2PublicKeyFromCert(cert.Raw) pub, err := parseSM2PublicKeyFromCert(cert.Raw)
if err != nil { if err != nil {
return result, err return result, err
@@ -150,6 +183,19 @@ func verifyGBT35275SignedData(signedValue, signedData []byte, options *signature
if !sm2VerifySignature(pub, nil, plain, signer.Signature) { if !sm2VerifySignature(pub, nil, plain, signer.Signature) {
return result, nil return result, nil
} }
continue
}
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 !ok {
return result, nil
}
} }
result.SignedOK = true result.SignedOK = true
return result, nil return result, nil
@@ -449,6 +495,16 @@ func (sd *gbtSignedData) findCert(issuer []byte, serial *big.Int) *gbtCertificat
return nil 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 解析显式标签内容 // asn1Explicit 解析显式标签内容
// 入参: raw ASN.1原始值 // 入参: raw ASN.1原始值
// 返回: asn1.RawValue 标签内容, error 错误信息 // 返回: asn1.RawValue 标签内容, error 错误信息
+22 -7
View File
@@ -71,6 +71,9 @@ type sesVerifyResult struct {
CertOK bool CertOK bool
SignCert SignatureCertInfo SignCert SignatureCertInfo
SealCert SignatureCertInfo SealCert SignatureCertInfo
SignCertRaw []byte
SealCertRaw []byte
Certs [][]byte
SealType string SealType string
} }
@@ -169,9 +172,9 @@ func parseSESSignatureV1(items []asn1.RawValue) (*sesSignature, error) {
} }
// verifySESSignature 验证SES签章值 // verifySESSignature 验证SES签章值
// 入参: data 签章值数据, signedData 被签名数据原文 // 入参: data 签章值数据, signedData 被签名数据原文, options 验证选项
// 返回: *sesVerifyResult 验证结果, error 错误信息 // 返回: *sesVerifyResult 验证结果, error 错误信息
func verifySESSignature(data, signedData []byte) (*sesVerifyResult, error) { func verifySESSignature(data, signedData []byte, options *signatureVerifyOptions) (*sesVerifyResult, error) {
sig, err := parseSESSignature(data) sig, err := parseSESSignature(data)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -182,6 +185,10 @@ func verifySESSignature(data, signedData []byte) (*sesVerifyResult, error) {
result := &sesVerifyResult{} result := &sesVerifyResult{}
result.SignCert = signatureCertInfo(sig.Cert) result.SignCert = signatureCertInfo(sig.Cert)
result.SealCert = signatureCertInfo(sig.Seal.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.SealType = sig.Seal.PicType
result.DataHashOK = bytes.Equal(sig.DataHash, signSM3(signedData)) result.DataHashOK = bytes.Equal(sig.DataHash, signSM3(signedData))
signPub, err := parseSM2PublicKeyFromCert(sig.Cert) signPub, err := parseSM2PublicKeyFromCert(sig.Cert)
@@ -309,7 +316,7 @@ func parseSESHeaderVersion(raw asn1.RawValue) (int, error) {
} }
// parseSESCertList 解析印章证书列表 // parseSESCertList 解析印章证书列表
// 入参: raw 印章属性信息 // 入参: raw 印章属性信息, version 印章版本
// 返回: sesCertList 证书列表, error 错误信息 // 返回: sesCertList 证书列表, error 错误信息
func parseSESCertList(raw asn1.RawValue, version int) (sesCertList, error) { func parseSESCertList(raw asn1.RawValue, version int) (sesCertList, error) {
items, ok := asn1Children(raw.Bytes) items, ok := asn1Children(raw.Bytes)
@@ -497,16 +504,24 @@ func signSM3(data []byte) []byte {
// 入参: method 算法标识 // 入参: method 算法标识
// 返回: bool 是否为SM2签名算法 // 返回: bool 是否为SM2签名算法
func isSM2SignatureMethod(method string) bool { func isSM2SignatureMethod(method string) bool {
method = strings.TrimSpace(method) switch signatureMethodText(method) {
return method == signMethodSM2SM3 || method == signMethodSM2SM3B || method == signMethodSM2Sign case signMethodSM2SM3, signMethodSM2SM3B, signMethodSM2Sign, "SM2", "SM2SM3", "SM3SM2", "SM2WITHSM3", "SM3WITHSM2":
return true
default:
return false
}
} }
// isSM3DigestMethod 判断是否为SM3摘要算法 // isSM3DigestMethod 判断是否为SM3摘要算法
// 入参: method 算法标识 // 入参: method 算法标识
// 返回: bool 是否为SM3摘要算法 // 返回: bool 是否为SM3摘要算法
func isSM3DigestMethod(method string) bool { func isSM3DigestMethod(method string) bool {
method = strings.TrimSpace(method) switch signatureMethodText(method) {
return method == signDigestSM3 || method == signDigestSM3NoKey || method == signDigestSM3Key || method == "SM3" case signDigestSM3, signDigestSM3NoKey, signDigestSM3Key, "SM3":
return true
default:
return false
}
} }
// sesCertInList 判断证书是否在印章证书列表中 // sesCertInList 判断证书是否在印章证书列表中
+471 -19
View File
@@ -16,15 +16,24 @@ package ofdgo
import ( import (
"bytes" "bytes"
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"crypto/subtle" "crypto/subtle"
"crypto/x509"
"encoding/asn1" "encoding/asn1"
"encoding/base64" "encoding/base64"
"encoding/pem" "encoding/pem"
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"io" "io"
"math/big"
"path" "path"
"strings" "strings"
"time"
) )
// SignatureVerifyReport 签名验证报告 // SignatureVerifyReport 签名验证报告
@@ -42,12 +51,17 @@ type SignatureVerifyReport struct {
DigestMethod string DigestMethod string
References []SignatureReferenceVerify References []SignatureReferenceVerify
Stamps []SignatureStamp Stamps []SignatureStamp
StampPositions []SignatureStampPosition
DigestOK bool DigestOK bool
DataHashOK bool DataHashOK bool
SignedValueOK bool SignedValueOK bool
SealOK bool SealOK bool
SealMatchOK bool SealMatchOK bool
CertOK bool CertOK bool
CertTimeChecked bool
CertTimeOK bool
CertTrustChecked bool
CertTrustOK bool
Valid bool Valid bool
Error string Error string
} }
@@ -59,6 +73,8 @@ type SignatureCertInfo struct {
Organization string Organization string
Issuer string Issuer string
SerialNumber string SerialNumber string
NotBefore time.Time
NotAfter time.Time
} }
// SignatureReferenceVerify 签名保护文件验证结果 // SignatureReferenceVerify 签名保护文件验证结果
@@ -74,6 +90,8 @@ type SignatureReferenceVerify struct {
// signatureVerifyOptions 签名验证选项 // signatureVerifyOptions 签名验证选项
type signatureVerifyOptions struct { type signatureVerifyOptions struct {
SignCerts [][]byte SignCerts [][]byte
TrustCerts [][]byte
VerifyTime *time.Time
} }
// SignatureVerifyOption 签名验证选项函数 // 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字节数据签名 // VerifySignaturesBytes 验证OFD字节数据签名
// 入参: data OFD字节数据, opts 签名验证选项 // 入参: data OFD字节数据, opts 签名验证选项
// 返回: []SignatureVerifyReport 签名验证报告, error 错误信息 // 返回: []SignatureVerifyReport 签名验证报告, error 错误信息
@@ -186,6 +233,11 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options *
report.DigestMethod = sigFile.SignedInfo.References.CheckMethod report.DigestMethod = sigFile.SignedInfo.References.CheckMethod
report.References = r.verifySignatureReferences(sigPath, sigFile.SignedInfo.References) report.References = r.verifySignatureReferences(sigPath, sigFile.SignedInfo.References)
report.Stamps = append(report.Stamps, sigFile.SignedInfo.StampAnnot...) 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) report.DigestOK = referencesOK(report.References)
signedValuePath := signatureRefPath(sigPath, sigFile.SignedValue) signedValuePath := signatureRefPath(sigPath, sigFile.SignedValue)
signedValue, err := r.readFileExact(signedValuePath) signedValue, err := r.readFileExact(signedValuePath)
@@ -195,7 +247,7 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options *
} }
switch sigRef.Type { switch sigRef.Type {
case SignTypeSign: case SignTypeSign:
result, err := verifyDigitalSignature(report.SignatureMethod, signedValue, sigData, options) result, err := verifyDigitalSignature(report.SignatureMethod, report.DigestMethod, signedValue, sigData, options)
if err != nil { if err != nil {
report.Error = err.Error() report.Error = err.Error()
return report return report
@@ -206,14 +258,15 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options *
report.CertOK = result.CertOK report.CertOK = result.CertOK
report.SignCert = result.CertInfo report.SignCert = result.CertInfo
report.Signer = result.CertInfo.CommonName 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 return report
case "", SignTypeSeal: case "", SignTypeSeal:
default: default:
report.Error = fmt.Sprintf("unsupported signature type: %s", sigRef.Type) report.Error = fmt.Sprintf("unsupported signature type: %s", sigRef.Type)
return report return report
} }
sesResult, err := verifySESSignature(signedValue, sigData) sesResult, err := verifySESSignature(signedValue, sigData, options)
if sesResult != nil { if sesResult != nil {
report.SignCert = sesResult.SignCert report.SignCert = sesResult.SignCert
report.SealCert = sesResult.SealCert report.SealCert = sesResult.SealCert
@@ -228,6 +281,7 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options *
report.SignedValueOK = sesResult.SignedOK report.SignedValueOK = sesResult.SignedOK
report.SealOK = sesResult.SealOK report.SealOK = sesResult.SealOK
report.CertOK = sesResult.CertOK report.CertOK = sesResult.CertOK
report.applySignatureCertificatePolicy(options, [][]byte{sesResult.SignCertRaw, sesResult.SealCertRaw}, sesResult.Certs)
if sigFile.SignedInfo.Seal.BaseLoc != "" { if sigFile.SignedInfo.Seal.BaseLoc != "" {
sealPath := signatureRefPath(sigPath, sigFile.SignedInfo.Seal.BaseLoc) sealPath := signatureRefPath(sigPath, sigFile.SignedInfo.Seal.BaseLoc)
sealData, err := r.readFileExact(sealPath) 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.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 return report
} }
@@ -321,9 +375,190 @@ func signatureDigest(method string, data []byte) ([]byte, error) {
if isSM3DigestMethod(method) { if isSM3DigestMethod(method) {
return signSM3(data), nil return signSM3(data), nil
} }
if h, ok := signatureDigestHash(method); ok {
return signatureHashBytes(h, data), nil
}
return nil, fmt.Errorf("unsupported digest method: %s", method) 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 解析签名文件引用路径 // signatureRefPath 解析签名文件引用路径
// 入参: basePath 基准路径, refPath 引用路径 // 入参: basePath 基准路径, refPath 引用路径
// 返回: string 包内文件路径 // 返回: string 包内文件路径
@@ -351,6 +586,138 @@ func referencesOK(refs []SignatureReferenceVerify) bool {
return true 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元素原始字节 // xmlElementRaw 提取XML元素原始字节
// 入参: data XML数据, localName 元素名称 // 入参: data XML数据, localName 元素名称
// 返回: []byte 元素原始字节, error 错误信息 // 返回: []byte 元素原始字节, error 错误信息
@@ -395,6 +762,45 @@ func xmlElementRaw(data []byte, localName string) ([]byte, error) {
// 入参: data DER编码证书 // 入参: data DER编码证书
// 返回: SignatureCertInfo 签名证书信息 // 返回: SignatureCertInfo 签名证书信息
func signatureCertInfo(data []byte) 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 { var cert struct {
TBSCertificate asn1.RawValue TBSCertificate asn1.RawValue
SignatureAlgorithm asn1.RawValue SignatureAlgorithm asn1.RawValue
@@ -402,32 +808,78 @@ func signatureCertInfo(data []byte) SignatureCertInfo {
} }
rest, err := asn1.Unmarshal(data, &cert) rest, err := asn1.Unmarshal(data, &cert)
if err != nil || len(rest) != 0 { if err != nil || len(rest) != 0 {
return SignatureCertInfo{} return signatureCertificate{}, fmt.Errorf("invalid certificate")
} }
items, ok := asn1Children(cert.TBSCertificate.Bytes) items, ok := asn1Children(cert.TBSCertificate.Bytes)
if !ok { if !ok {
return SignatureCertInfo{} return signatureCertificate{}, fmt.Errorf("invalid tbs certificate")
} }
idx := 0 idx := 0
if len(items) > 0 && items[0].Class == asn1.ClassContextSpecific && items[0].Tag == 0 { if len(items) > 0 && items[0].Class == asn1.ClassContextSpecific && items[0].Tag == 0 {
idx++ idx++
} }
if len(items) <= idx+4 { if len(items) <= idx+5 {
return SignatureCertInfo{} return signatureCertificate{}, fmt.Errorf("invalid certificate")
} }
serial, _ := asn1IntegerBig(items[idx]) serial, err := asn1IntegerBig(items[idx])
subject := certificateNameValues(items[idx+4]) if err != nil {
issuer := certificateNameValues(items[idx+2]) return signatureCertificate{}, err
info := SignatureCertInfo{
Subject: certificateNameString(subject),
CommonName: certificateNameFirst(subject, "2.5.4.3"),
Organization: certificateNameFirst(subject, "2.5.4.10"),
Issuer: certificateNameString(issuer),
} }
if serial != nil { validity, err := parseCertificateValidity(items[idx+3])
info.SerialNumber = serial.String() 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 解析证书名称字段 // certificateNameValues 解析证书名称字段