// 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" "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 签名验证报告 // Valid表示签名完整性及调用方指定的证书策略均通过 type SignatureVerifyReport struct { ID string BaseLoc string Type SignType Provider SignatureProvider Signer string SignCert SignatureCertInfo SealCert SignatureCertInfo SealType string SignatureMethod string SignatureDateTime string DigestMethod string References []SignatureReferenceVerify Stamps []SignatureStamp StampPositions []SignatureStampPosition StampPositionError string DigestOK bool DataHashOK bool SignedValueOK bool SealOK bool SealMatchOK bool CertOK bool CertTimeChecked bool CertTimeOK bool CertTrustChecked bool CertTrustOK bool Valid bool Error string } // IntegrityValid 判断签名完整性是否有效 // 返回: bool 是否有效 func (report SignatureVerifyReport) IntegrityValid() bool { return report.Error == "" && report.DigestOK && report.DataHashOK && report.SignedValueOK && report.SealOK && report.SealMatchOK && report.CertOK } // TrustedValid 判断签名是否可信有效 // 返回: bool 签名完整性、证书信任及证书有效期是否均验证通过 func (report SignatureVerifyReport) TrustedValid() bool { return report.IntegrityValid() && report.CertTrustChecked && report.CertTrustOK && report.CertTimeChecked && report.CertTimeOK } // SignatureCertInfo 签名证书信息 type SignatureCertInfo struct { Subject string CommonName string Organization string Issuer string SerialNumber string NotBefore time.Time NotAfter time.Time } // SignatureReferenceVerify 签名保护文件验证结果 type SignatureReferenceVerify struct { FileRef string Path string CheckValue []byte Actual []byte OK bool Error string } // signatureVerifyOptions 签名验证选项 type signatureVerifyOptions struct { SignCerts [][]byte TrustCerts [][]byte VerifyTime *time.Time } var signatureMethodReplacer = strings.NewReplacer("-", "", "_", "", " ", "") // SignatureVerifyOption 签名验证选项函数 type SignatureVerifyOption func(*signatureVerifyOptions) // WithSignatureCert 添加数字签名验证证书 // 入参: cert DER或PEM编码证书 // 返回: SignatureVerifyOption 签名验证选项 func WithSignatureCert(cert []byte) SignatureVerifyOption { return func(o *signatureVerifyOptions) { o.SignCerts = appendSignatureCerts(o.SignCerts, cert) } } // WithSignatureCerts 添加多张数字签名验证证书 // 入参: certs DER或PEM编码证书列表 // 返回: SignatureVerifyOption 签名验证选项 func WithSignatureCerts(certs ...[]byte) SignatureVerifyOption { return func(o *signatureVerifyOptions) { o.SignCerts = appendSignatureCerts(o.SignCerts, certs...) } } // WithSignatureTrustCert 添加签名信任证书 // 入参: cert DER或PEM编码证书 // 返回: SignatureVerifyOption 签名验证选项 func WithSignatureTrustCert(cert []byte) SignatureVerifyOption { return func(o *signatureVerifyOptions) { o.TrustCerts = appendSignatureCerts(o.TrustCerts, cert) } } // WithSignatureTrustCerts 添加多张签名信任证书 // 入参: certs DER或PEM编码证书列表 // 返回: SignatureVerifyOption 签名验证选项 func WithSignatureTrustCerts(certs ...[]byte) SignatureVerifyOption { return func(o *signatureVerifyOptions) { o.TrustCerts = appendSignatureCerts(o.TrustCerts, certs...) } } // WithSignatureVerifyTime 设置签名证书验证时间 // 入参: t 验证时间 // 返回: SignatureVerifyOption 签名验证选项 func WithSignatureVerifyTime(t time.Time) SignatureVerifyOption { return func(o *signatureVerifyOptions) { o.VerifyTime = &t } } // appendSignatureCerts 追加签名证书 // 入参: dst 目标证书列表, certs DER或PEM编码证书列表 // 返回: [][]byte 证书列表 func appendSignatureCerts(dst [][]byte, certs ...[]byte) [][]byte { for _, cert := range certs { dst = append(dst, parseSignatureCerts(cert)...) } return dst } // VerifySignaturesBytes 验证OFD字节数据签名 // 入参: data OFD字节数据, opts 签名验证选项 // 返回: []SignatureVerifyReport 签名验证报告, error 错误信息 func VerifySignaturesBytes(data []byte, opts ...SignatureVerifyOption) ([]SignatureVerifyReport, error) { return VerifySignaturesReader(bytes.NewReader(data), int64(len(data)), opts...) } // VerifySignaturesStream 验证OFD顺序流签名 // 入参: r IO顺序读取器, opts 签名验证选项 // 返回: []SignatureVerifyReport 签名验证报告, error 错误信息 func VerifySignaturesStream(r io.Reader, opts ...SignatureVerifyOption) ([]SignatureVerifyReport, error) { data, err := io.ReadAll(r) if err != nil { return nil, err } return VerifySignaturesBytes(data, opts...) } // VerifySignaturesReader 验证OFD读取器签名 // 入参: r IO读取器, size 数据大小, opts 签名验证选项 // 返回: []SignatureVerifyReport 签名验证报告, error 错误信息 func VerifySignaturesReader(r io.ReaderAt, size int64, opts ...SignatureVerifyOption) ([]SignatureVerifyReport, error) { reader, err := NewReader(r, size) if err != nil { return nil, err } return reader.VerifySignatures(opts...) } // VerifySignatures 验证文档签名 // 入参: opts 签名验证选项 // 返回: []SignatureVerifyReport 签名验证报告, error 错误信息 func (r *Reader) VerifySignatures(opts ...SignatureVerifyOption) ([]SignatureVerifyReport, error) { options := signatureVerifyOptions{} for _, opt := range opts { opt(&options) } doc, err := r.Doc() if err != nil { return nil, err } if doc.Signatures == "" { return nil, nil } sigListPath := r.ResPath(doc.Signatures) data, err := r.readFileExact(sigListPath) if err != nil { return nil, err } var signatures Signatures if err := xml.Unmarshal(data, &signatures); err != nil { return nil, err } reports := make([]SignatureVerifyReport, 0, len(signatures.List)) for _, sigRef := range signatures.List { reports = append(reports, r.verifySignature(sigListPath, sigRef, &options)) } return reports, nil } // verifySignature 验证单个签名 // 入参: sigListPath 签名列表路径, sigRef 签名引用, options 验证选项 // 返回: SignatureVerifyReport 签名验证报告 func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options *signatureVerifyOptions) SignatureVerifyReport { sigPath := signatureRefPath(sigListPath, sigRef.BaseLoc) report := SignatureVerifyReport{ ID: sigRef.ID, BaseLoc: sigRef.BaseLoc, Type: sigRef.Type, SealMatchOK: true, } sigData, err := r.readFileExact(sigPath) if err != nil { report.Error = err.Error() return report } sigFile, err := parseSignatureFile(sigData) if err != nil { report.Error = err.Error() return report } report.Provider = sigFile.SignedInfo.Provider report.SignatureMethod = sigFile.SignedInfo.SignatureMethod report.SignatureDateTime = sigFile.SignedInfo.SignatureDateTime 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.StampPositionError = err.Error() } report.DigestOK = referencesOK(report.References) signedValuePath := signatureRefPath(sigPath, sigFile.SignedValue) signedValue, err := r.readFileExact(signedValuePath) if err != nil { report.Error = err.Error() return report } switch sigRef.Type { case SignTypeSign: result, err := verifyDigitalSignature(report.SignatureMethod, report.DigestMethod, signedValue, sigData, options) if err != nil { report.Error = err.Error() return report } report.DataHashOK = result.DataHashOK report.SignedValueOK = result.SignedOK report.SealOK = true report.CertOK = result.CertOK report.SignCert = result.CertInfo report.Signer = result.CertInfo.CommonName report.applySignatureCertificatePolicy(options, result.SignerCerts, result.Certs) report.Valid = report.IntegrityValid() && report.certificatePolicyOK() return report case "", SignTypeSeal: default: report.Error = fmt.Sprintf("unsupported signature type: %s", sigRef.Type) return report } sesResult, err := verifySESSignature(signedValue, sigData, options) if sesResult != nil { report.SignCert = sesResult.SignCert report.SealCert = sesResult.SealCert report.SealType = sesResult.SealType report.Signer = sesResult.SignCert.CommonName } if err != nil { report.Error = err.Error() return report } report.DataHashOK = sesResult.DataHashOK 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) if err != nil { report.Error = err.Error() return report } report.SealMatchOK = bytes.Equal(sealData, sesResult.SealRaw) } report.Valid = report.IntegrityValid() && report.certificatePolicyOK() return report } // verifySignatureReferences 验证签名保护文件列表 // 入参: sigPath 签名文件路径, refs 签名保护文件列表 // 返回: []SignatureReferenceVerify 保护文件验证结果 func (r *Reader) verifySignatureReferences(sigPath string, refs SignatureReferences) []SignatureReferenceVerify { results := make([]SignatureReferenceVerify, 0, len(refs.Reference)) for _, ref := range refs.Reference { results = append(results, r.verifySignatureReference(sigPath, refs.CheckMethod, ref)) } return results } // verifySignatureReference 验证签名保护文件 // 入参: sigPath 签名文件路径, method 摘要算法, ref 保护文件引用 // 返回: SignatureReferenceVerify 保护文件验证结果 func (r *Reader) verifySignatureReference(sigPath, method string, ref SignatureReference) SignatureReferenceVerify { refPath := signatureRefPath(sigPath, ref.FileRef) result := SignatureReferenceVerify{ FileRef: ref.FileRef, Path: refPath, } checkValue, err := base64.StdEncoding.DecodeString(strings.TrimSpace(ref.CheckValue)) if err != nil { result.Error = err.Error() return result } data, err := r.readFileExact(refPath) if err != nil { result.Error = err.Error() return result } actual, err := signatureDigest(method, data) if err != nil { result.Error = err.Error() return result } result.CheckValue = checkValue result.Actual = actual result.OK = subtle.ConstantTimeCompare(checkValue, actual) == 1 return result } // parseSignatureFile 解析签名文件 // 入参: data 签名文件XML数据 // 返回: *SignatureFile 签名文件结构, error 错误信息 func parseSignatureFile(data []byte) (*SignatureFile, error) { var sigFile SignatureFile if err := xml.Unmarshal(data, &sigFile); err != nil { return nil, err } return &sigFile, nil } // readFileExact 读取OFD包内文件 // 入参: name 包内文件路径 // 返回: []byte 文件数据, error 错误信息 func (r *Reader) readFileExact(name string) ([]byte, error) { name = cleanPackagePath(name) if f, ok := r.fileIndex[name]; ok { return readZipFile(f) } return nil, fmt.Errorf("file not found: %s", name) } // signatureDigest 计算签名摘要 // 入参: method 摘要算法, data 原文数据 // 返回: []byte 摘要值, error 错误信息 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 len(method) >= len("urn:oid:") && strings.EqualFold(method[:len("urn:oid:")], "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) return signatureMethodReplacer.Replace(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 包内文件路径 func signatureRefPath(basePath, refPath string) string { p := strings.TrimSpace(refPath) p = strings.ReplaceAll(p, "\\", "/") if strings.HasPrefix(p, "/") { return cleanPackagePath(p) } return path.Clean(resolveResourcePath(basePath, "", p)) } // referencesOK 判断保护文件摘要是否全部通过 // 入参: refs 保护文件验证结果 // 返回: bool 是否全部通过 func referencesOK(refs []SignatureReferenceVerify) bool { if len(refs) == 0 { return false } for _, ref := range refs { if !ref.OK { return false } } 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 pool := append([][]byte{}, options.TrustCerts...) pool = append(pool, options.SignCerts...) pool = append(pool, extraCerts...) pool = compactSignatureCerts(pool) for _, cert := range certs { if !signatureCertTrustedBy(cert, pool, options.TrustCerts, options.VerifyTime) { 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 } // signatureCertMaxChecks 最大证书签名验证次数 const signatureCertMaxChecks = 100 // signatureCertPathState 证书路径状态 type signatureCertPathState struct { Visited map[string]bool SignatureChecks int } // signatureCertTrustedBy 判断证书是否可链到信任证书 // 入参: cert 证书, pool 证书池, trusts 信任证书, verifyTime 中间证书验证时间 // 返回: bool 是否受信任 func signatureCertTrustedBy(cert []byte, pool, trusts [][]byte, verifyTime *time.Time) bool { state := signatureCertPathState{Visited: make(map[string]bool)} return signatureCertPathTrustedBy(cert, pool, trusts, verifyTime, &state, 0, true) } // signatureCertPathTrustedBy 验证证书路径 // 入参: cert 证书, pool 证书池, trusts 信任证书, verifyTime 中间证书验证时间, state 路径状态, caBelow 下级非自颁发中间CA数量, target 是否目标证书 // 返回: bool 是否受信任 func signatureCertPathTrustedBy(cert []byte, pool, trusts [][]byte, verifyTime *time.Time, state *signatureCertPathState, caBelow int, target bool) bool { if len(cert) == 0 { return false } trusted := false for _, trust := range trusts { if bytes.Equal(cert, trust) { trusted = true break } } if trusted && !target { return true } key := string(cert) if state.Visited[key] { return false } state.Visited[key] = true defer delete(state.Visited, key) c, err := parseSignatureCertificate(cert) if err != nil { return false } if c.UnhandledCritical { return false } if !target && verifyTime != nil && (verifyTime.Before(c.NotBefore) || verifyTime.After(c.NotAfter)) { return false } if target { if c.KeyUsage != 0 && c.KeyUsage&(x509.KeyUsageDigitalSignature|x509.KeyUsageContentCommitment) == 0 { return false } } else { if !c.IsCA { return false } if c.KeyUsage != 0 && c.KeyUsage&x509.KeyUsageCertSign == 0 { return false } if c.MaxPathLen != nil && c.MaxPathLen.Cmp(big.NewInt(int64(caBelow))) < 0 { return false } } if trusted { return true } nextCABelow := caBelow if !target && !bytes.Equal(c.Issuer, c.Subject) { nextCABelow++ } for _, issuerCert := range pool { if state.SignatureChecks >= signatureCertMaxChecks { return false } if bytes.Equal(cert, issuerCert) { continue } issuer, err := parseSignatureCertificate(issuerCert) if err != nil || !bytes.Equal(c.Issuer, issuer.Subject) { continue } state.SignatureChecks++ if ok, err := verifyCertificateSignature(c, issuerCert); err != nil || !ok { continue } if signatureCertPathTrustedBy(issuerCert, pool, trusts, verifyTime, state, nextCABelow, false) { 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 } // signatureCertInfo 解析签名证书信息 // 入参: 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 } const ( signatureExtensionKeyUsage = "2.5.29.15" signatureExtensionBasicConstraints = "2.5.29.19" ) // signatureCertificateExtensions 签名证书扩展 type signatureCertificateExtensions struct { IsCA bool MaxPathLen *big.Int KeyUsage x509.KeyUsage UnhandledCritical bool } // signatureCertificate 签名证书结构 type signatureCertificate struct { Raw []byte TBS []byte Issuer []byte IssuerValue asn1.RawValue Subject []byte SubjectValue asn1.RawValue PublicKey asn1.RawValue Serial *big.Int NotBefore time.Time NotAfter time.Time SignatureAlg string Signature []byte signatureCertificateExtensions } // parseSignatureCertificate 解析签名证书 // 入参: data DER编码证书 // 返回: signatureCertificate 签名证书结构, error 错误信息 func parseSignatureCertificate(data []byte) (signatureCertificate, error) { var cert struct { TBSCertificate asn1.RawValue SignatureAlgorithm asn1.RawValue SignatureValue asn1.BitString } rest, err := asn1.Unmarshal(data, &cert) if err != nil || len(rest) != 0 { return signatureCertificate{}, fmt.Errorf("invalid certificate") } items, ok := asn1Children(cert.TBSCertificate.Bytes) if !ok { 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+5 { return signatureCertificate{}, fmt.Errorf("invalid certificate") } if !bytes.Equal(items[idx+1].FullBytes, cert.SignatureAlgorithm.FullBytes) { return signatureCertificate{}, fmt.Errorf("certificate signature algorithm mismatch") } serial, err := asn1IntegerBig(items[idx]) if err != nil { return signatureCertificate{}, err } validity, err := parseCertificateValidity(items[idx+3]) if err != nil { return signatureCertificate{}, err } extensions, err := parseSignatureCertificateExtensions(items[idx+6:]) if err != nil { return signatureCertificate{}, err } 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], PublicKey: items[idx+5], Serial: serial, NotBefore: validity[0], NotAfter: validity[1], SignatureAlg: alg, Signature: append([]byte(nil), cert.SignatureValue.Bytes...), signatureCertificateExtensions: extensions, }, nil } // parseSignatureCertificateExtensions 解析签名证书扩展 // 入参: items TBS证书剩余字段 // 返回: signatureCertificateExtensions 签名证书扩展, error 错误信息 func parseSignatureCertificateExtensions(items []asn1.RawValue) (signatureCertificateExtensions, error) { var out signatureCertificateExtensions var extensions []struct { ID asn1.ObjectIdentifier Critical bool `asn1:"optional"` Value []byte } found := false for _, item := range items { if item.Class != asn1.ClassContextSpecific || item.Tag != 3 { continue } if found || !item.IsCompound { return out, fmt.Errorf("invalid certificate extensions") } found = true rest, err := asn1.Unmarshal(item.Bytes, &extensions) if err != nil || len(rest) != 0 { return out, fmt.Errorf("invalid certificate extensions") } } seen := make(map[string]bool) for _, extension := range extensions { oid := extension.ID.String() if seen[oid] { return out, fmt.Errorf("duplicate certificate extension: %s", oid) } seen[oid] = true switch oid { case signatureExtensionBasicConstraints: isCA, maxPathLen, err := parseSignatureBasicConstraints(extension.Value) if err != nil { return out, err } out.IsCA = isCA out.MaxPathLen = maxPathLen case signatureExtensionKeyUsage: keyUsage, err := parseSignatureKeyUsage(extension.Value) if err != nil { return out, err } out.KeyUsage = keyUsage default: if extension.Critical { out.UnhandledCritical = true } } } return out, nil } // parseSignatureBasicConstraints 解析证书基本约束 // 入参: data 扩展DER数据 // 返回: bool 是否为CA, *big.Int 路径长度限制, error 错误信息 func parseSignatureBasicConstraints(data []byte) (bool, *big.Int, error) { var raw asn1.RawValue rest, err := asn1.Unmarshal(data, &raw) if err != nil || len(rest) != 0 || raw.Tag != asn1.TagSequence || !raw.IsCompound { return false, nil, fmt.Errorf("invalid basic constraints") } items, ok := asn1Children(raw.Bytes) if !ok || len(items) > 2 { return false, nil, fmt.Errorf("invalid basic constraints") } idx := 0 isCA := false if len(items) > 0 && items[0].Tag == asn1.TagBoolean { rest, err := asn1.Unmarshal(items[0].FullBytes, &isCA) if err != nil || len(rest) != 0 { return false, nil, fmt.Errorf("invalid basic constraints") } idx++ } var maxPathLen *big.Int if idx < len(items) { maxPathLen, err = asn1IntegerBig(items[idx]) if err != nil || maxPathLen.Sign() < 0 { return false, nil, fmt.Errorf("invalid basic constraints") } idx++ } if idx != len(items) || (maxPathLen != nil && !isCA) { return false, nil, fmt.Errorf("invalid basic constraints") } return isCA, maxPathLen, nil } // parseSignatureKeyUsage 解析证书密钥用途 // 入参: data 扩展DER数据 // 返回: x509.KeyUsage 密钥用途, error 错误信息 func parseSignatureKeyUsage(data []byte) (x509.KeyUsage, error) { var bits asn1.BitString rest, err := asn1.Unmarshal(data, &bits) if err != nil || len(rest) != 0 || bits.BitLength == 0 || bits.BitLength > 9 { return 0, fmt.Errorf("invalid key usage") } var out x509.KeyUsage for i := 0; i < bits.BitLength; i++ { if bits.At(i) != 0 { out |= 1 << uint(i) } } if out == 0 { return 0, fmt.Errorf("invalid key usage") } return out, 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 解析证书名称字段 // 入参: raw 名称原始值 // 返回: map[string][]string OID字段列表 func certificateNameValues(raw asn1.RawValue) map[string][]string { out := make(map[string][]string) sets, ok := asn1Children(raw.Bytes) if !ok { return out } for _, set := range sets { attrs, ok := asn1Children(set.Bytes) if !ok { continue } for _, attr := range attrs { items, ok := asn1Children(attr.Bytes) if !ok || len(items) < 2 { continue } oid, err := asn1OIDString(items[0]) if err != nil { continue } if value := strings.TrimSpace(asn1String(items[1])); value != "" { out[oid] = append(out[oid], value) } } } return out } // certificateNameFirst 获取证书名称字段首值 // 入参: values OID字段列表, oid 字段OID // 返回: string 字段值 func certificateNameFirst(values map[string][]string, oid string) string { if items := values[oid]; len(items) > 0 { return items[0] } return "" } // certificateNameString 格式化证书名称 // 入参: values OID字段列表 // 返回: string 证书名称 func certificateNameString(values map[string][]string) string { var parts []string for _, item := range []struct { OID string Label string }{ {"2.5.4.3", "CN"}, {"2.5.4.10", "O"}, {"2.5.4.11", "OU"}, {"2.5.4.6", "C"}, {"2.5.4.8", "ST"}, {"2.5.4.7", "L"}, {"2.5.4.5", "SN"}, {"1.2.840.113549.1.9.1", "E"}, } { for _, value := range values[item.OID] { parts = append(parts, item.Label+"="+value) } } return strings.Join(parts, ", ") } // parseSignatureCerts 解析签名验证证书 // 入参: data DER或PEM编码证书 // 返回: [][]byte DER编码证书列表 func parseSignatureCerts(data []byte) [][]byte { data = bytes.TrimSpace(data) if len(data) == 0 { return nil } var certs [][]byte rest := data hasPEM := false for { block, next := pem.Decode(rest) if block == nil { break } hasPEM = true if block.Type == "CERTIFICATE" { certs = append(certs, append([]byte(nil), block.Bytes...)) } rest = next } if len(certs) != 0 { return certs } if hasPEM { return nil } return [][]byte{append([]byte(nil), data...)} }