Files
ofdgo/ofdgo_sign_verify.go
T
2026-10-09 10:39:51 +08:00

1065 lines
35 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"
"encoding/asn1"
"encoding/base64"
"encoding/pem"
"encoding/xml"
"fmt"
"hash"
"io"
"math/big"
"path"
"strings"
"time"
"github.com/emmansun/gmsm/sm2"
"github.com/emmansun/gmsm/sm3"
"github.com/emmansun/gmsm/smx509"
)
// signatureMethodReplacer 移除签名算法名称中的分隔符
var signatureMethodReplacer = strings.NewReplacer("-", "", "_", "", " ", "")
// SignatureVerifyReport 签名验证报告
// Valid表示签名完整性、签名时间语义及调用方指定的全部策略均通过
// Checked表示对应检查已得出结论, 未检查时不能将OK的零值视为失败
// SealCertTimeOK仅提供制章证书在签名时间的状态信息, 不参与Valid判断
type SignatureVerifyReport struct {
DocIndex int
DocRoot string
ID string
BaseLoc string
Type SignType
Valid bool
Error string
Signer string
SignatureDateTime string
SignatureTime time.Time
Provider SignatureProvider
SealInfo SignatureSealInfo
SealType string
SignCert SignatureCertInfo
SealCert SignatureCertInfo
SignatureMethod string
DigestMethod string
References []SignatureReferenceVerify
Stamps []SignatureStamp
StampPositions []SignatureStampPosition
StampPositionError string
DigestOK bool
DataHashChecked bool
DataHashOK bool
SignedValueChecked bool
SignedValueOK bool
CertChecked bool
CertOK bool
CertTimeChecked bool
CertTimeOK bool
CertTrustChecked bool
CertTrustOK bool
CertTrustError string
SignatureTimeChecked bool
SignatureTimeOK bool
SealChecked bool
SealOK bool
SealMatchChecked bool
SealMatchOK bool
SealTimeChecked bool
SealTimeOK bool
SealCertTimeChecked bool
SealCertTimeOK bool
CoverageChecked bool
CoverageOK bool
CoverageError string
CoveredFiles []string
UncoveredFiles []string
PolicyChecked bool
PolicyOK bool
PolicyError string
TimestampChecked bool
TimestampOK bool
Timestamps []SignatureTimestampReport
RevocationChecked bool
RevocationOK bool
Revocations []SignatureRevocationReport
}
// 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
Checked bool
OK bool
Error string
}
// signatureVerifyOptions 签名验证选项
type signatureVerifyOptions struct {
SignCerts [][]byte
TrustCerts [][]byte
VerifyTime *time.Time
Policy *SignaturePolicy
Timestamp *SignatureTimestampOptions
Revocation *SignatureRevocationOptions
DocIndex int
DocRoot string
}
// SignatureVerifyOption 签名验证选项函数
type SignatureVerifyOption func(*signatureVerifyOptions)
// IntegrityValid 判断签名完整性是否有效
// 返回: bool 是否有效
func (report SignatureVerifyReport) IntegrityValid() bool {
sealOK := report.Type == SignTypeSign || report.SealChecked && report.SealOK
return report.Error == "" && report.DigestOK && referencesOK(report.References) &&
report.DataHashChecked && report.DataHashOK && report.SignedValueChecked && report.SignedValueOK &&
sealOK && report.SealMatchOK && report.CertChecked && report.CertOK
}
// TrustedValid 判断签名是否可信有效
// 返回: bool 签名完整性、时间语义、证书信任及证书有效期是否均验证通过
func (report SignatureVerifyReport) TrustedValid() bool {
return report.IntegrityValid() && report.certificatePolicyOK() && report.CertTrustChecked && report.CertTimeChecked
}
// HasFailure 判断已完成的签名检查是否存在失败
// 返回: bool 是否存在失败, 未完成检查时仍需判断Valid和Error
func (report SignatureVerifyReport) HasFailure() bool {
for _, ref := range report.References {
if ref.Checked && !ref.OK {
return true
}
}
return report.DataHashChecked && !report.DataHashOK ||
report.SignedValueChecked && !report.SignedValueOK ||
report.SealChecked && !report.SealOK ||
report.SealMatchChecked && !report.SealMatchOK ||
report.CertChecked && !report.CertOK || !report.certificatePolicyOK()
}
// 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
}
}
// 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) {
if r.OFD == nil || len(r.OFD.DocBody) == 0 {
return nil, fmt.Errorf("no docbody found")
}
if _, err := r.Doc(); err != nil {
return nil, err
}
var reports []SignatureVerifyReport
for i := range r.OFD.DocBody {
items, err := r.VerifyDocumentSignatures(i, opts...)
reports = append(reports, items...)
if err != nil {
return reports, fmt.Errorf("document %d: %w", i, err)
}
}
return reports, nil
}
// VerifyDocumentSignatures 验证指定DocBody的签名且不切换阅读器当前文档
// 入参: index 从0开始的文档索引, opts 签名验证选项
// 返回: []SignatureVerifyReport 签名验证报告, error 错误信息
func (r *Reader) VerifyDocumentSignatures(index int, opts ...SignatureVerifyOption) ([]SignatureVerifyReport, error) {
if r.OFD == nil || index < 0 || index >= len(r.OFD.DocBody) {
return nil, fmt.Errorf("document index out of range: %d", index)
}
options := signatureVerifyOptions{}
for _, opt := range opts {
if opt != nil {
opt(&options)
}
}
body := r.OFD.DocBody[index]
options.DocIndex, options.DocRoot = index, r.signatureCoveragePath(body.DocRoot)
if index == r.documentIndex && r.doc != nil && r.doc.Signatures == "" {
return nil, nil
}
data, err := r.readFile(body.DocRoot)
if err != nil {
return nil, err
}
var doc Document
if err := xml.Unmarshal(data, &doc); err != nil {
return nil, err
}
if doc.Signatures == "" {
doc.Signatures = body.Signatures
}
if doc.Signatures == "" {
return nil, nil
}
view := &Reader{Zip: r.Zip, files: r.files, encryption: r.encryption, documentIndex: index}
if err := view.initRoot(); err != nil {
return nil, err
}
view.doc, view.RootDir = &doc, path.Dir(body.DocRoot)
sigListPath := view.ResPath(doc.Signatures)
data, err = view.readFile(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, view.verifySignature(sigListPath, sigRef, &options))
}
return reports, nil
}
// appendSignatureCerts 追加签名证书
// 入参: dst 目标证书列表, certs DER或PEM编码证书列表
// 返回: [][]byte 证书列表
func appendSignatureCerts(dst [][]byte, certs ...[]byte) [][]byte {
for _, cert := range certs {
dst = append(dst, parseSignatureCerts(cert)...)
}
return dst
}
// verifySignature 验证单个签名
// 入参: sigListPath 签名列表路径, sigRef 签名引用, options 验证选项
// 返回: SignatureVerifyReport 签名验证报告
func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options *signatureVerifyOptions) SignatureVerifyReport {
sigPath := signatureRefPath(sigListPath, sigRef.BaseLoc)
report := SignatureVerifyReport{
DocIndex: options.DocIndex,
DocRoot: options.DocRoot,
ID: sigRef.ID,
BaseLoc: sigRef.BaseLoc,
Type: sigRef.Type,
SealMatchOK: true,
}
if report.Type == "" {
report.Type = SignTypeSeal
}
sigData, err := r.readFile(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)
r.applySignatureCoverage(&report, sigListPath, options)
report.applySignaturePolicy(options, r.OFD.Version, 0, report.SignatureMethod, report.DigestMethod)
signedValuePath := signatureRefPath(sigPath, sigFile.SignedValue)
signedValue, err := r.readFile(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 result != nil {
report.DataHashChecked = result.DataHashChecked
report.DataHashOK = result.DataHashOK
report.SignedValueChecked = result.SignedChecked
report.SignedValueOK = result.SignedOK
report.CertChecked = result.CertChecked
report.CertOK = result.CertOK
report.SignCert = result.CertInfo
report.Signer = result.CertInfo.CommonName
}
if err != nil {
report.applySignaturePolicyError(err)
report.Error = err.Error()
return report
}
report.SealOK = true
report.SignatureTime = parseSignatureDateTime(report.SignatureDateTime)
report.applySignatureTimePolicy()
report.applySignatureCertificatePolicy(options, result.SignerCerts, result.Certs)
report.applySignatureEvidence(options, signedValue, result.Timestamps, 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.DataHashChecked = sesResult.DataHashChecked
report.DataHashOK = sesResult.DataHashOK
report.SignedValueChecked = sesResult.SignedChecked
report.SignedValueOK = sesResult.SignedOK
report.SealChecked = sesResult.SealChecked
report.SealOK = sesResult.SealOK
report.CertChecked = sesResult.CertChecked
report.CertOK = sesResult.CertOK
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.applySignaturePolicyError(err)
report.Error = err.Error()
return report
}
report.applySignatureTimePolicy()
if report.SignatureMethod != "" && !signatureAlgorithmEquivalent(report.SignatureMethod, sesResult.SignatureMethod, "SM3") {
report.Error = "signature method does not match SES"
return report
}
report.applySignatureCertificatePolicy(options, [][]byte{sesResult.SignCertRaw, sesResult.SealCertRaw}, sesResult.Certs)
report.applySignaturePolicy(options, r.OFD.Version, sesResult.SealInfo.Version, sesResult.SignatureMethod, "SM3")
report.applySignatureEvidence(options, signedValue, sesResult.Timestamps, [][]byte{sesResult.SignCertRaw, sesResult.SealCertRaw}, sesResult.Certs)
if sigFile.SignedInfo.Seal.BaseLoc != "" {
sealPath := signatureRefPath(sigPath, sigFile.SignedInfo.Seal.BaseLoc)
sealData, err := r.readFile(sealPath)
if err != nil {
report.Error = err.Error()
return report
}
report.SealMatchChecked = true
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
}
var digest hash.Hash
switch {
case strings.TrimSpace(method) == "":
digest = md5.New()
case isSM3DigestMethod(method):
digest = sm3.New()
default:
algorithm, ok := signatureDigestHash(method)
if !ok {
result.Error = fmt.Sprintf("unsupported digest method: %s", method)
return result
}
digest = algorithm.New()
}
input, err := r.openFile(refPath)
if err != nil {
result.Error = err.Error()
return result
}
_, err = io.Copy(digest, input)
closeErr := input.Close()
if err != nil {
result.Error = err.Error()
return result
}
if closeErr != nil {
result.Error = closeErr.Error()
return result
}
actual := digest.Sum(nil)
result.CheckValue = checkValue
result.Actual = actual
result.Checked = true
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
}
// 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 := parseSignatureCertificate(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")
}
}
// verifySM2Signature 验证DER或定长拼接编码的SM2签名值
// 入参: pub 公钥, userID 用户标识, msg 原文, sig 签名值
// 返回: bool 是否验证通过
func verifySM2Signature(pub *ecdsa.PublicKey, userID, msg, sig []byte) bool {
if sm2.VerifyASN1WithSM2(pub, userID, msg, sig) {
return true
}
if len(sig) != 64 {
return false
}
digest, err := sm2.CalculateSM2Hash(pub, msg, userID)
if err != nil {
return false
}
return sm2.Verify(pub, digest, new(big.Int).SetBytes(sig[:32]), new(big.Int).SetBytes(sig[32:]))
}
// 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.Checked || !ref.OK || ref.Error != "" {
return false
}
}
return true
}
// applySignatureCertificatePolicy 应用签名证书策略
// 入参: options 验证选项, certs 待验证证书, extraCerts 证书池
func (report *SignatureVerifyReport) applySignatureCertificatePolicy(options *signatureVerifyOptions, certs [][]byte, extraCerts [][]byte) {
certs = compactSignatureCerts(certs)
verifyTime := options.VerifyTime
if verifyTime == nil && len(options.TrustCerts) != 0 {
now := time.Now()
verifyTime = &now
}
if verifyTime != nil {
report.CertTimeChecked = true
report.CertTimeOK = signatureCertsValidAt(certs, *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 err := verifySignatureCertificateTrust(cert, pool, options.TrustCerts, *verifyTime); err != nil {
report.CertTrustOK = false
report.CertTrustError = err.Error()
break
}
}
if len(certs) == 0 {
report.CertTrustOK = false
report.CertTrustError = "signature certificate missing"
}
}
}
// 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.PolicyChecked && !report.PolicyOK || report.CoverageChecked && !report.CoverageOK ||
report.TimestampChecked && !report.TimestampOK || report.RevocationChecked && !report.RevocationOK {
return false
}
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
}
// verifySignatureCertificateTrust 按指定时间验证签署证书到显式信任根的证书链
// 入参: cert 证书, pool 证书池, trusts 信任证书, at 验证时间
// 返回: error 验证错误
func verifySignatureCertificateTrust(cert []byte, pool, trusts [][]byte, at time.Time) error {
c, err := parseSignatureCertificate(cert)
if err != nil {
return err
}
if c.KeyUsage != 0 && c.KeyUsage&(smx509.KeyUsageDigitalSignature|smx509.KeyUsageContentCommitment) == 0 {
return fmt.Errorf("certificate does not authorize digital signatures")
}
return verifySignatureCertificateChain(c, pool, trusts, at, smx509.ExtKeyUsageAny)
}
// verifySignatureCertificateChain 使用统一X.509实现验证证书链
// 入参: cert 目标证书, pool 中间证书, trusts 信任根, at 验证时间, usage 扩展用途
// 返回: error 验证错误
func verifySignatureCertificateChain(cert *smx509.Certificate, pool, trusts [][]byte, at time.Time, usage smx509.ExtKeyUsage) error {
roots, intermediates := smx509.NewCertPool(), smx509.NewCertPool()
for _, group := range []struct {
data [][]byte
pool *smx509.CertPool
}{{trusts, roots}, {pool, intermediates}} {
for _, data := range group.data {
ca, err := parseSignatureCertificate(data)
if err != nil {
return err
}
addSignatureCertificate(group.pool, ca)
}
}
_, err := cert.Verify(smx509.VerifyOptions{Roots: roots, Intermediates: intermediates, CurrentTime: at, KeyUsages: []smx509.ExtKeyUsage{usage}})
return err
}
// addSignatureCertificate 添加证书并按RFC5280排除自颁发中间证书的路径长度
// 入参: pool 证书池, cert 证书
func addSignatureCertificate(pool *smx509.CertPool, cert *smx509.Certificate) {
if !cert.BasicConstraintsValid || cert.MaxPathLen < 0 {
pool.AddCert(cert)
return
}
copy := *cert
copy.MaxPathLen, copy.MaxPathLenZero = -1, false
pool.AddCertWithConstraint(&copy, func(chain []*smx509.Certificate) error {
length := 0
for i, intermediate := range chain {
if i > 0 && !bytes.Equal(intermediate.RawSubject, intermediate.RawIssuer) {
length++
}
}
if length > cert.MaxPathLen {
return smx509.CertificateInvalidError{Cert: cert, Reason: smx509.TooManyIntermediates}
}
return nil
})
}
// verifyCertificateSignature 验证证书签名
// 入参: cert 证书信息, issuerCert 颁发者证书
// 返回: bool 是否验证通过, error 错误信息
func verifyCertificateSignature(cert *smx509.Certificate, issuerCert []byte) (bool, error) {
issuer, err := parseSignatureCertificate(issuerCert)
if err != nil {
return false, err
}
err = issuer.CheckSignature(cert.SignatureAlgorithm, cert.RawTBSCertificate, cert.Signature)
return err == nil, err
}
// 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{}
}
return SignatureCertInfo{
Raw: bytes.Clone(cert.Raw),
Subject: cert.Subject.String(),
CommonName: cert.Subject.CommonName,
Organization: strings.Join(cert.Subject.Organization, ", "),
Issuer: cert.Issuer.String(),
NotBefore: cert.NotBefore,
NotAfter: cert.NotAfter,
SerialNumber: cert.SerialNumber.String(),
}
}
// 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{}
}
// parseSignatureCertificate 解析唯一的PEM或DER证书
// 入参: data 证书数据
// 返回: *smx509.Certificate 证书, error 错误信息
func parseSignatureCertificate(data []byte) (*smx509.Certificate, error) {
if block, rest := pem.Decode(data); block != nil {
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 || len(bytes.TrimSpace(rest)) != 0 {
return nil, fmt.Errorf("expected one certificate")
}
data = block.Bytes
}
return smx509.ParseCertificate(data)
}
// 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
}
// parseSignatureCerts 解析签名验证证书
// 入参: data DER或PEM编码证书
// 返回: [][]byte DER编码证书列表
func parseSignatureCerts(data []byte) [][]byte {
if len(data) == 0 {
return nil
}
var certs [][]byte
rest := bytes.TrimSpace(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...)}
}