mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-08-30 12:12:40 +08:00
534 lines
16 KiB
Go
534 lines
16 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"
|
|
"encoding/asn1"
|
|
"fmt"
|
|
"math/big"
|
|
)
|
|
|
|
const (
|
|
signContentData = "1.2.156.10197.6.1.4.2.1"
|
|
signContentSignedData = "1.2.156.10197.6.1.4.2.2"
|
|
signAttrMessageDigest = "1.2.840.113549.1.9.4"
|
|
)
|
|
|
|
// digitalVerifyResult 数字签名验证结果
|
|
type digitalVerifyResult struct {
|
|
DataHashOK bool
|
|
SignedOK bool
|
|
CertOK bool
|
|
Cert []byte
|
|
SignerCerts [][]byte
|
|
Certs [][]byte
|
|
CertInfo SignatureCertInfo
|
|
}
|
|
|
|
// gbtSignedData GB/T 35275 SignedData结构
|
|
type gbtSignedData struct {
|
|
ContentDigest []byte
|
|
Certs []gbtCertificate
|
|
Signers []gbtSignerInfo
|
|
}
|
|
|
|
// gbtCertificate SignedData证书索引信息
|
|
type gbtCertificate struct {
|
|
Raw []byte
|
|
Issuer []byte
|
|
Serial *big.Int
|
|
}
|
|
|
|
// gbtSignerInfo SignedData签名者信息
|
|
type gbtSignerInfo struct {
|
|
Issuer []byte
|
|
Serial *big.Int
|
|
DigestAlg string
|
|
SignatureAlg string
|
|
Signature []byte
|
|
AuthAttrs []byte
|
|
AttrDigest []byte
|
|
}
|
|
|
|
// verifyDigitalSignature 验证OFD数字签名
|
|
// 入参: method 签名算法, digestMethod 摘要算法, signedValue 签名值, signedData 被签名原文, options 验证选项
|
|
// 返回: *digitalVerifyResult 验证结果, error 错误信息
|
|
func verifyDigitalSignature(method, digestMethod string, signedValue, signedData []byte, options *signatureVerifyOptions) (*digitalVerifyResult, error) {
|
|
if isGBT35275SignedValue(signedValue) {
|
|
return verifyGBT35275SignedData(signedValue, signedData, options)
|
|
}
|
|
if isSM2SignatureMethod(method) {
|
|
return verifyRawDigitalSignature(signedValue, signedData, options)
|
|
}
|
|
if isRSASignatureMethod(method) || isECDSASignatureMethod(method) {
|
|
return verifyRawPublicKeySignature(method, digestMethod, signedValue, signedData, options)
|
|
}
|
|
return nil, fmt.Errorf("unsupported signature method: %s", method)
|
|
}
|
|
|
|
// verifyRawDigitalSignature 验证裸SM2数字签名
|
|
// 入参: signedValue 签名值, signedData 被签名原文, options 验证选项
|
|
// 返回: *digitalVerifyResult 验证结果, error 错误信息
|
|
func verifyRawDigitalSignature(signedValue, signedData []byte, options *signatureVerifyOptions) (*digitalVerifyResult, error) {
|
|
if len(options.SignCerts) == 0 {
|
|
return nil, fmt.Errorf("signature certificate not found")
|
|
}
|
|
result := &digitalVerifyResult{DataHashOK: true}
|
|
for _, cert := range options.SignCerts {
|
|
pub, err := parseSM2PublicKeyFromCert(cert)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
result.CertOK = true
|
|
result.Cert = cert
|
|
result.SignerCerts = [][]byte{cert}
|
|
result.CertInfo = signatureCertInfo(cert)
|
|
if sm2VerifySignature(pub, nil, signedData, signedValue) {
|
|
result.SignedOK = true
|
|
return result, nil
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// verifyRawPublicKeySignature 验证裸RSA或ECDSA数字签名
|
|
// 入参: method 签名算法, digestMethod 摘要算法, signedValue 签名值, signedData 被签名原文, options 验证选项
|
|
// 返回: *digitalVerifyResult 验证结果, error 错误信息
|
|
func verifyRawPublicKeySignature(method, digestMethod string, signedValue, signedData []byte, options *signatureVerifyOptions) (*digitalVerifyResult, error) {
|
|
if len(options.SignCerts) == 0 {
|
|
return nil, fmt.Errorf("signature certificate not found")
|
|
}
|
|
result := &digitalVerifyResult{DataHashOK: true}
|
|
for _, cert := range options.SignCerts {
|
|
ok, err := verifyPublicKeySignature(method, digestMethod, cert, signedData, signedValue)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
result.CertOK = true
|
|
result.Cert = cert
|
|
result.SignerCerts = [][]byte{cert}
|
|
result.CertInfo = signatureCertInfo(cert)
|
|
if ok {
|
|
result.SignedOK = true
|
|
return result, nil
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// verifyGBT35275SignedData 验证GB/T 35275 SignedData签名值
|
|
// 入参: signedValue 签名值, signedData 被签名原文, options 验证选项
|
|
// 返回: *digitalVerifyResult 验证结果, error 错误信息
|
|
func verifyGBT35275SignedData(signedValue, signedData []byte, options *signatureVerifyOptions) (*digitalVerifyResult, error) {
|
|
sd, err := parseGBT35275SignedData(signedValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, cert := range options.SignCerts {
|
|
c, err := parseGBTCertificate(cert)
|
|
if err == nil {
|
|
sd.Certs = append(sd.Certs, c)
|
|
}
|
|
}
|
|
if len(sd.Signers) == 0 {
|
|
return nil, fmt.Errorf("invalid signed data signer info")
|
|
}
|
|
result := &digitalVerifyResult{Certs: sd.rawCerts()}
|
|
for _, signer := range sd.Signers {
|
|
digest, err := signatureDigest(signer.DigestAlg, signedData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(sd.ContentDigest) != 0 && !bytes.Equal(sd.ContentDigest, digest) {
|
|
return result, nil
|
|
}
|
|
result.DataHashOK = true
|
|
plain := sd.ContentDigest
|
|
if len(signer.AuthAttrs) != 0 {
|
|
if !bytes.Equal(signer.AttrDigest, digest) {
|
|
result.DataHashOK = false
|
|
return result, nil
|
|
}
|
|
plain = signer.AuthAttrs
|
|
}
|
|
if len(plain) == 0 {
|
|
return nil, fmt.Errorf("invalid signed data content")
|
|
}
|
|
cert := sd.findCert(signer.Issuer, signer.Serial)
|
|
if cert == nil {
|
|
return result, nil
|
|
}
|
|
result.Cert = cert.Raw
|
|
result.SignerCerts = append(result.SignerCerts, cert.Raw)
|
|
result.CertInfo = signatureCertInfo(cert.Raw)
|
|
if isSM2SignatureMethod(signer.SignatureAlg) {
|
|
pub, err := parseSM2PublicKeyFromCert(cert.Raw)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
result.CertOK = true
|
|
if !sm2VerifySignature(pub, nil, plain, signer.Signature) {
|
|
return result, nil
|
|
}
|
|
continue
|
|
}
|
|
if !isRSASignatureMethod(signer.SignatureAlg) && !isECDSASignatureMethod(signer.SignatureAlg) {
|
|
return nil, fmt.Errorf("unsupported signature method: %s", signer.SignatureAlg)
|
|
}
|
|
ok, err := verifyPublicKeySignature(signer.SignatureAlg, signer.DigestAlg, cert.Raw, plain, signer.Signature)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
result.CertOK = true
|
|
if !ok {
|
|
return result, nil
|
|
}
|
|
}
|
|
result.SignedOK = true
|
|
return result, nil
|
|
}
|
|
|
|
// isGBT35275SignedValue 判断签名值是否为GB/T 35275 SignedData
|
|
// 入参: data 签名值数据
|
|
// 返回: bool 是否为SignedData
|
|
func isGBT35275SignedValue(data []byte) bool {
|
|
var root asn1.RawValue
|
|
rest, err := asn1.Unmarshal(data, &root)
|
|
if err != nil || len(rest) != 0 || root.Tag != signASN1Sequence {
|
|
return false
|
|
}
|
|
items, ok := asn1Children(root.Bytes)
|
|
if !ok || len(items) < 2 {
|
|
return false
|
|
}
|
|
oid, err := asn1OIDString(items[0])
|
|
return err == nil && oid == signContentSignedData
|
|
}
|
|
|
|
// parseGBT35275SignedData 解析GB/T 35275 SignedData
|
|
// 入参: data 签名值数据
|
|
// 返回: *gbtSignedData SignedData结构, error 错误信息
|
|
func parseGBT35275SignedData(data []byte) (*gbtSignedData, error) {
|
|
contentType, content, ok, err := parseGBTContentInfoBytes(data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !ok || contentType != signContentSignedData {
|
|
return nil, fmt.Errorf("invalid signed data content type")
|
|
}
|
|
items, ok := asn1Children(content.Bytes)
|
|
if !ok || len(items) < 4 {
|
|
return nil, fmt.Errorf("invalid signed data")
|
|
}
|
|
sd := &gbtSignedData{}
|
|
contentOID, content, hasContent, err := parseGBTContentInfo(items[2])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if hasContent {
|
|
if contentOID != signContentData {
|
|
return nil, fmt.Errorf("invalid signed data inner content type")
|
|
}
|
|
if content.Tag == asn1.TagOctetString {
|
|
sd.ContentDigest, err = asn1OctetString(content)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
for i := 3; i < len(items); i++ {
|
|
item := items[i]
|
|
if item.Class == asn1.ClassContextSpecific && item.Tag == 0 {
|
|
certs, err := parseGBTCertificates(item)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sd.Certs = certs
|
|
continue
|
|
}
|
|
if item.Class == asn1.ClassContextSpecific && item.Tag == 1 {
|
|
continue
|
|
}
|
|
signers, err := parseGBTSignerInfos(item)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sd.Signers = signers
|
|
}
|
|
return sd, nil
|
|
}
|
|
|
|
// parseGBTContentInfoBytes 解析ContentInfo字节
|
|
// 入参: data DER编码数据
|
|
// 返回: string 内容类型, asn1.RawValue 内容, bool 是否存在内容, error 错误信息
|
|
func parseGBTContentInfoBytes(data []byte) (string, asn1.RawValue, bool, error) {
|
|
var raw asn1.RawValue
|
|
rest, err := asn1.Unmarshal(data, &raw)
|
|
if err != nil || len(rest) != 0 {
|
|
return "", asn1.RawValue{}, false, fmt.Errorf("invalid content info")
|
|
}
|
|
return parseGBTContentInfo(raw)
|
|
}
|
|
|
|
// parseGBTContentInfo 解析ContentInfo结构
|
|
// 入参: raw ASN.1原始值
|
|
// 返回: string 内容类型, asn1.RawValue 内容, bool 是否存在内容, error 错误信息
|
|
func parseGBTContentInfo(raw asn1.RawValue) (string, asn1.RawValue, bool, error) {
|
|
items, ok := asn1Children(raw.Bytes)
|
|
if !ok || len(items) == 0 {
|
|
return "", asn1.RawValue{}, false, fmt.Errorf("invalid content info")
|
|
}
|
|
oid, err := asn1OIDString(items[0])
|
|
if err != nil {
|
|
return "", asn1.RawValue{}, false, err
|
|
}
|
|
if len(items) == 1 {
|
|
return oid, asn1.RawValue{}, false, nil
|
|
}
|
|
content, err := asn1Explicit(items[1])
|
|
if err != nil {
|
|
return "", asn1.RawValue{}, false, err
|
|
}
|
|
return oid, content, true, nil
|
|
}
|
|
|
|
// parseGBTCertificates 解析SignedData证书集合
|
|
// 入参: raw ASN.1原始值
|
|
// 返回: []gbtCertificate 证书列表, error 错误信息
|
|
func parseGBTCertificates(raw asn1.RawValue) ([]gbtCertificate, error) {
|
|
items, ok := asn1Children(raw.Bytes)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid signed data certificates")
|
|
}
|
|
certs := make([]gbtCertificate, 0, len(items))
|
|
for _, item := range items {
|
|
cert, err := parseGBTCertificate(item.FullBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
certs = append(certs, cert)
|
|
}
|
|
return certs, nil
|
|
}
|
|
|
|
// parseGBTCertificate 解析X.509证书索引字段
|
|
// 入参: data DER编码证书
|
|
// 返回: gbtCertificate 证书索引信息, error 错误信息
|
|
func parseGBTCertificate(data []byte) (gbtCertificate, 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 gbtCertificate{}, fmt.Errorf("invalid certificate")
|
|
}
|
|
items, ok := asn1Children(cert.TBSCertificate.Bytes)
|
|
if !ok {
|
|
return gbtCertificate{}, 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+2 {
|
|
return gbtCertificate{}, fmt.Errorf("invalid certificate issuer")
|
|
}
|
|
serial, err := asn1IntegerBig(items[idx])
|
|
if err != nil {
|
|
return gbtCertificate{}, err
|
|
}
|
|
return gbtCertificate{
|
|
Raw: append([]byte(nil), data...),
|
|
Issuer: append([]byte(nil), items[idx+2].FullBytes...),
|
|
Serial: serial,
|
|
}, nil
|
|
}
|
|
|
|
// parseGBTSignerInfos 解析签名者信息集合
|
|
// 入参: raw ASN.1原始值
|
|
// 返回: []gbtSignerInfo 签名者列表, error 错误信息
|
|
func parseGBTSignerInfos(raw asn1.RawValue) ([]gbtSignerInfo, error) {
|
|
items, ok := asn1Children(raw.Bytes)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid signer infos")
|
|
}
|
|
signers := make([]gbtSignerInfo, 0, len(items))
|
|
for _, item := range items {
|
|
signer, err := parseGBTSignerInfo(item)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
signers = append(signers, signer)
|
|
}
|
|
return signers, nil
|
|
}
|
|
|
|
// parseGBTSignerInfo 解析签名者信息
|
|
// 入参: raw ASN.1原始值
|
|
// 返回: gbtSignerInfo 签名者信息, error 错误信息
|
|
func parseGBTSignerInfo(raw asn1.RawValue) (gbtSignerInfo, error) {
|
|
items, ok := asn1Children(raw.Bytes)
|
|
if !ok || len(items) < 5 {
|
|
return gbtSignerInfo{}, fmt.Errorf("invalid signer info")
|
|
}
|
|
issuer, serial, err := parseGBTIssuerAndSerial(items[1])
|
|
if err != nil {
|
|
return gbtSignerInfo{}, err
|
|
}
|
|
digestAlg, err := parseGBTAlgorithm(items[2])
|
|
if err != nil {
|
|
return gbtSignerInfo{}, err
|
|
}
|
|
idx := 3
|
|
var authAttrs []byte
|
|
var attrDigest []byte
|
|
if items[idx].Class == asn1.ClassContextSpecific && items[idx].Tag == 0 {
|
|
authAttrs = asn1SetBytes(items[idx].Bytes)
|
|
attrDigest, err = parseGBTMessageDigestAttr(items[idx])
|
|
if err != nil {
|
|
return gbtSignerInfo{}, err
|
|
}
|
|
idx++
|
|
}
|
|
if len(items) <= idx+1 {
|
|
return gbtSignerInfo{}, fmt.Errorf("invalid signer info")
|
|
}
|
|
signatureAlg, err := parseGBTAlgorithm(items[idx])
|
|
if err != nil {
|
|
return gbtSignerInfo{}, err
|
|
}
|
|
signature, err := asn1OctetString(items[idx+1])
|
|
if err != nil {
|
|
return gbtSignerInfo{}, err
|
|
}
|
|
return gbtSignerInfo{
|
|
Issuer: issuer,
|
|
Serial: serial,
|
|
DigestAlg: digestAlg,
|
|
SignatureAlg: signatureAlg,
|
|
Signature: signature,
|
|
AuthAttrs: authAttrs,
|
|
AttrDigest: attrDigest,
|
|
}, nil
|
|
}
|
|
|
|
// parseGBTIssuerAndSerial 解析证书颁发者和序列号
|
|
// 入参: raw ASN.1原始值
|
|
// 返回: []byte 颁发者DN, *big.Int 序列号, error 错误信息
|
|
func parseGBTIssuerAndSerial(raw asn1.RawValue) ([]byte, *big.Int, error) {
|
|
items, ok := asn1Children(raw.Bytes)
|
|
if !ok || len(items) < 2 {
|
|
return nil, nil, fmt.Errorf("invalid issuer and serial")
|
|
}
|
|
serial, err := asn1IntegerBig(items[1])
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return append([]byte(nil), items[0].FullBytes...), serial, nil
|
|
}
|
|
|
|
// parseGBTAlgorithm 解析算法标识
|
|
// 入参: raw ASN.1原始值
|
|
// 返回: string 算法OID, error 错误信息
|
|
func parseGBTAlgorithm(raw asn1.RawValue) (string, error) {
|
|
if raw.Tag == asn1.TagOID {
|
|
return asn1OIDString(raw)
|
|
}
|
|
items, ok := asn1Children(raw.Bytes)
|
|
if !ok || len(items) == 0 {
|
|
return "", fmt.Errorf("invalid algorithm identifier")
|
|
}
|
|
return asn1OIDString(items[0])
|
|
}
|
|
|
|
// parseGBTMessageDigestAttr 解析认证属性中的message-digest
|
|
// 入参: raw ASN.1原始值
|
|
// 返回: []byte 摘要值, error 错误信息
|
|
func parseGBTMessageDigestAttr(raw asn1.RawValue) ([]byte, error) {
|
|
attrs, ok := asn1Children(raw.Bytes)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid authenticated attributes")
|
|
}
|
|
for _, attr := range attrs {
|
|
items, ok := asn1Children(attr.Bytes)
|
|
if !ok || len(items) < 2 {
|
|
continue
|
|
}
|
|
oid, err := asn1OIDString(items[0])
|
|
if err != nil || oid != signAttrMessageDigest {
|
|
continue
|
|
}
|
|
values, ok := asn1Children(items[1].Bytes)
|
|
if !ok || len(values) == 0 {
|
|
return nil, fmt.Errorf("invalid message digest attribute")
|
|
}
|
|
return asn1OctetString(values[0])
|
|
}
|
|
return nil, fmt.Errorf("message digest attribute not found")
|
|
}
|
|
|
|
// findCert 查找签名者证书
|
|
// 入参: issuer 颁发者DN, serial 证书序列号
|
|
// 返回: *gbtCertificate 证书信息
|
|
func (sd *gbtSignedData) findCert(issuer []byte, serial *big.Int) *gbtCertificate {
|
|
for i := range sd.Certs {
|
|
cert := &sd.Certs[i]
|
|
if bytes.Equal(cert.Issuer, issuer) && cert.Serial.Cmp(serial) == 0 {
|
|
return cert
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// rawCerts 获取SignedData证书原文
|
|
// 返回: [][]byte 证书列表
|
|
func (sd *gbtSignedData) rawCerts() [][]byte {
|
|
certs := make([][]byte, 0, len(sd.Certs))
|
|
for _, cert := range sd.Certs {
|
|
certs = append(certs, cert.Raw)
|
|
}
|
|
return certs
|
|
}
|
|
|
|
// asn1Explicit 解析显式标签内容
|
|
// 入参: raw ASN.1原始值
|
|
// 返回: asn1.RawValue 标签内容, error 错误信息
|
|
func asn1Explicit(raw asn1.RawValue) (asn1.RawValue, error) {
|
|
if raw.Class != asn1.ClassContextSpecific || raw.Tag != 0 || !raw.IsCompound {
|
|
return asn1.RawValue{}, fmt.Errorf("invalid explicit content")
|
|
}
|
|
var out asn1.RawValue
|
|
rest, err := asn1.Unmarshal(raw.Bytes, &out)
|
|
if err != nil || len(rest) != 0 {
|
|
return asn1.RawValue{}, fmt.Errorf("invalid explicit content")
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// asn1IntegerBig 解析ASN.1整数
|
|
// 入参: raw ASN.1原始值
|
|
// 返回: *big.Int 整数值, error 错误信息
|
|
func asn1IntegerBig(raw asn1.RawValue) (*big.Int, error) {
|
|
var out *big.Int
|
|
rest, err := asn1.Unmarshal(raw.FullBytes, &out)
|
|
if err != nil || len(rest) != 0 || out == nil {
|
|
return nil, fmt.Errorf("invalid integer")
|
|
}
|
|
return out, nil
|
|
}
|