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

1227 lines
38 KiB
Go

// Copyright 2025-2026 肖其顿 (XIAO QI DUN)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ofdgo
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/md5"
"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表示签名完整性、签名时间语义及调用方指定的证书策略均通过
// SealCertTimeOK仅提供制章证书在签名时间的状态信息, 不参与Valid判断
type SignatureVerifyReport struct {
ID string
BaseLoc string
Type SignType
Provider SignatureProvider
Signer string
SignCert SignatureCertInfo
SealCert SignatureCertInfo
SealInfo SignatureSealInfo
SealType string
SignatureMethod string
SignatureDateTime string
SignatureTime time.Time
DigestMethod string
References []SignatureReferenceVerify
Stamps []SignatureStamp
StampPositions []SignatureStampPosition
StampPositionError string
DigestOK bool
DataHashOK bool
SignedValueOK bool
SealOK bool
SealMatchOK bool
CertOK bool
SignatureTimeChecked bool
SignatureTimeOK bool
SealCertTimeChecked bool
SealCertTimeOK bool
SealTimeChecked bool
SealTimeOK 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.certificatePolicyOK() && report.CertTrustChecked && report.CertTimeChecked
}
// SignatureCertInfo 签名证书信息
type SignatureCertInfo struct {
Raw []byte
Subject string
CommonName string
Organization string
Issuer string
SerialNumber string
NotBefore time.Time
NotAfter time.Time
}
// SignatureSealInfo 电子印章信息
type SignatureSealInfo struct {
Version int
ID string
VendorID string
Type int
Name string
CreateTime time.Time
ValidStart time.Time
ValidEnd 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,
}
if report.Type == "" {
report.Type = SignTypeSeal
}
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
if report.DigestMethod == "" {
report.DigestMethod = "MD5"
}
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 report.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.SignatureTime = parseSignatureDateTime(report.SignatureDateTime)
report.applySignatureTimePolicy()
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", report.Type)
return report
}
sesResult, err := verifySESSignature(signedValue, sigData, options)
if sesResult != nil {
report.SignCert = sesResult.SignCert
report.SealCert = sesResult.SealCert
report.SealInfo = sesResult.SealInfo
report.SealType = sesResult.SealType
report.SignatureTime = sesResult.SignatureTime
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.applySignatureTimePolicy()
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 strings.TrimSpace(method) == "" {
sum := md5.Sum(data)
return sum[:], nil
}
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.2.840.113549.2.5", "MD5":
return crypto.MD5, true
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.MD5:
sum := md5.Sum(data)
return sum[:]
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
}
}
}
// applySignatureTimePolicy 应用签名时间策略
func (report *SignatureVerifyReport) applySignatureTimePolicy() {
if !report.SignatureTime.IsZero() && !report.SignCert.NotBefore.IsZero() && !report.SignCert.NotAfter.IsZero() {
report.SignatureTimeChecked = true
report.SignatureTimeOK = timeInRange(report.SignatureTime, report.SignCert.NotBefore, report.SignCert.NotAfter)
}
if !report.SignatureTime.IsZero() && !report.SealCert.NotBefore.IsZero() && !report.SealCert.NotAfter.IsZero() {
report.SealCertTimeChecked = true
report.SealCertTimeOK = timeInRange(report.SignatureTime, report.SealCert.NotBefore, report.SealCert.NotAfter)
}
if !report.SignatureTime.IsZero() && !report.SealInfo.ValidStart.IsZero() && !report.SealInfo.ValidEnd.IsZero() {
report.SealTimeChecked = true
report.SealTimeOK = timeInRange(report.SignatureTime, report.SealInfo.ValidStart, report.SealInfo.ValidEnd)
}
}
// timeInRange 判断时间是否位于闭区间
// 入参: t 待判断时间, start 起始时间, end 结束时间
// 返回: bool 是否位于区间
func timeInRange(t, start, end time.Time) bool {
return !start.After(end) && !t.Before(start) && !t.After(end)
}
// certificatePolicyOK 判断证书策略是否通过
// 返回: bool 是否通过
func (report SignatureVerifyReport) certificatePolicyOK() bool {
if report.SignatureTimeChecked && !report.SignatureTimeOK {
return false
}
if report.SealTimeChecked && !report.SealTimeOK {
return false
}
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{
Raw: append([]byte(nil), data...),
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
}
// parseSignatureDateTime 解析带时区的签名时间
// 入参: value 签名时间文本
// 返回: time.Time 签名时间
func parseSignatureDateTime(value string) time.Time {
value = strings.TrimSpace(value)
for _, layout := range []string{
time.RFC3339Nano,
"20060102150405.999999999Z07:00",
"20060102150405Z07:00",
} {
if t, err := time.Parse(layout, value); err == nil {
return t
}
}
return time.Time{}
}
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...)}
}