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

This commit is contained in:
2026-07-15 12:23:28 +08:00
parent fa0c933f01
commit b23ee90d31
6 changed files with 501 additions and 115 deletions
+5 -12
View File
@@ -38,7 +38,6 @@ type Reader struct {
Stamps map[string][]Stamp
Annots map[string][]Annotation
fileIndex map[string]*zip.File
fileIndexFold map[string]*zip.File
}
// Close 关闭阅读器
@@ -54,14 +53,9 @@ func (r *Reader) Close() error {
// 返回: error 错误信息
func (r *Reader) initRoot() error {
r.fileIndex = make(map[string]*zip.File)
r.fileIndexFold = make(map[string]*zip.File)
for _, f := range r.Zip.File {
name := cleanPackagePath(f.Name)
r.fileIndex[name] = f
fold := strings.ToLower(name)
if _, ok := r.fileIndexFold[fold]; !ok {
r.fileIndexFold[fold] = f
}
}
data, err := r.readFile("OFD.xml")
if err != nil {
@@ -117,9 +111,6 @@ func (r *Reader) packageFile(name string) (*zip.File, bool) {
if f, ok := r.fileIndex[name]; ok {
return f, true
}
if f, ok := r.fileIndexFold[strings.ToLower(name)]; ok {
return f, true
}
return nil, false
}
@@ -176,7 +167,7 @@ func (r *Reader) loadRes(resPath string) {
if resPath == "" {
return
}
fullPath := path.Join(r.RootDir, resPath)
fullPath := r.ResPath(resPath)
data, err := r.readFile(fullPath)
if err != nil {
return
@@ -235,7 +226,7 @@ func resolveResourcePath(resPath, baseLoc, filePath string) string {
// 入参: page 页面对象
// 返回: *PageContent 页面内容, error 错误信息
func (r *Reader) PageContent(page Page) (*PageContent, error) {
fullPath := path.Join(r.RootDir, page.BaseLoc)
fullPath := r.ResPath(page.BaseLoc)
data, err := r.readFile(fullPath)
if err != nil {
return nil, err
@@ -260,7 +251,9 @@ func (r *Reader) ResPath(resLink string) string {
if resLink == "" {
return ""
}
resLink = strings.TrimPrefix(resLink, "/")
if strings.HasPrefix(resLink, "/") {
return cleanPackagePath(resLink)
}
resLink = path.Clean(resLink)
rootDir := strings.TrimPrefix(strings.ReplaceAll(r.RootDir, "\\", "/"), "/")
if rootDir != "" && (resLink == rootDir || strings.HasPrefix(resLink, rootDir+"/")) {
+76 -10
View File
@@ -18,16 +18,23 @@ import (
"bytes"
"image"
"image/color"
"image/draw"
"math"
"github.com/tdewolff/canvas"
"github.com/tdewolff/canvas/renderers/rasterizer"
)
// renderStamp 渲染印章
// 入参: ctx 画布上下文, s 印章对象, pageH 页面高度
func (r *Renderer) renderStamp(ctx *canvas.Context, s Stamp, pageH float64) {
x, y, w, h := s.Box.X, s.Box.Y, s.Box.W, s.Box.H
screenY := pageH - (y + h)
if s.Type == "ofd" && len(s.Data) > 0 {
if s.Clip != nil {
if img := r.renderOFDStampImage(s.Data); img != nil {
r.renderStampImage(ctx, stampImageWithTransparentWhite(img), s, pageH)
}
return
}
reader, err := NewReader(bytes.NewReader(s.Data), int64(len(s.Data)))
if err == nil {
defer reader.Close()
@@ -44,8 +51,8 @@ func (r *Renderer) renderStamp(ctx *canvas.Context, s Stamp, pageH float64) {
continue
}
ctx.Push()
ctx.Translate(x, screenY)
ctx.Scale(w/sealBox.W, h/sealBox.H)
ctx.Translate(s.Box.X, pageH-(s.Box.Y+s.Box.H))
ctx.Scale(s.Box.W/sealBox.W, s.Box.H/sealBox.H)
renderer.renderPageToContext(ctx, content, false)
ctx.Pop()
}
@@ -56,17 +63,76 @@ func (r *Renderer) renderStamp(ctx *canvas.Context, s Stamp, pageH float64) {
if len(s.Data) > 0 {
img, _, err := decodeImageData(s.Data)
if err == nil {
img = stampImageWithTransparentWhite(img)
ctx.Push()
ctx.Translate(x, screenY)
ctx.Scale(w/float64(img.Bounds().Dx()), h/float64(img.Bounds().Dy()))
ctx.DrawImage(0, 0, img, canvas.DPMM(1.0))
ctx.Pop()
r.renderStampImage(ctx, stampImageWithTransparentWhite(img), s, pageH)
return
}
}
}
// renderOFDStampImage 渲染OFD印章图像
// 入参: data OFD印章数据
// 返回: image.Image 印章图像
func (r *Renderer) renderOFDStampImage(data []byte) image.Image {
reader, err := NewReader(bytes.NewReader(data), int64(len(data)))
if err != nil {
return nil
}
defer reader.Close()
doc, err := reader.Doc()
if err != nil {
return nil
}
renderer := r.childRenderer(reader)
for _, pageRef := range doc.Pages.Page {
content, err := reader.PageContent(pageRef)
if err != nil {
continue
}
sealBox, err := renderer.GetPageBox(content)
if err != nil {
continue
}
c := canvas.New(sealBox.W, sealBox.H)
if err := renderer.renderPageToContext(canvas.NewContext(c), content, false); err != nil {
continue
}
return rasterizer.Draw(c, canvas.DPMM(r.DPI/25.4), canvas.DefaultColorSpace)
}
return nil
}
// renderStampImage 渲染印章图像
// 入参: ctx 画布上下文, img 印章图像, s 印章对象, pageH 页面高度
func (r *Renderer) renderStampImage(ctx *canvas.Context, img image.Image, s Stamp, pageH float64) {
box := s.Box
if s.Clip != nil {
img = clipStampImage(img, box, *s.Clip)
box.X += s.Clip.X
box.Y += s.Clip.Y
box.W = s.Clip.W
box.H = s.Clip.H
}
ctx.Push()
ctx.Translate(box.X, pageH-(box.Y+box.H))
ctx.Scale(box.W/float64(img.Bounds().Dx()), box.H/float64(img.Bounds().Dy()))
ctx.DrawImage(0, 0, img, canvas.DPMM(1.0))
ctx.Pop()
}
// clipStampImage 裁剪印章图像
// 入参: img 印章图像, box 印章区域, clip 裁剪区域
// 返回: image.Image 裁剪后的印章图像
func clipStampImage(img image.Image, box, clip Box) image.Image {
bounds := img.Bounds()
x0 := int(math.Floor(clip.X / box.W * float64(bounds.Dx())))
y0 := int(math.Floor(clip.Y / box.H * float64(bounds.Dy())))
x1 := int(math.Ceil((clip.X + clip.W) / box.W * float64(bounds.Dx())))
y1 := int(math.Ceil((clip.Y + clip.H) / box.H * float64(bounds.Dy())))
out := image.NewNRGBA(image.Rect(0, 0, x1-x0, y1-y0))
draw.Draw(out, out.Bounds(), img, image.Pt(bounds.Min.X+x0, bounds.Min.Y+y0), draw.Src)
return out
}
// stampImageWithTransparentWhite 处理印章图片白色底色
// 入参: img 印章图片对象
// 返回: image.Image 处理后的印章图片对象
+35 -9
View File
@@ -80,6 +80,7 @@ type SignatureStamp struct {
ID string `xml:"ID,attr"`
PageRef string `xml:"PageRef,attr"`
Boundary string `xml:"Boundary,attr"`
Clip string `xml:"Clip,attr"`
}
// SignatureStampPosition 签名外观位置信息
@@ -89,6 +90,8 @@ type SignatureStampPosition struct {
PageID string
Boundary string
Box Box
Clip string
ClipBox *Box
}
// SignatureReferences 签名保护文件列表
@@ -122,12 +125,22 @@ func (r *Reader) SignatureStampPositions(stamps []SignatureStamp) ([]SignatureSt
if err != nil {
return nil, err
}
var clipBox *Box
if stamp.Clip != "" {
clip, err := parseSignatureStampBox(stamp.Clip)
if err != nil {
return nil, err
}
clipBox = &clip
}
positions = append(positions, SignatureStampPosition{
ID: stamp.ID,
Page: page,
PageID: stamp.PageRef,
Boundary: stamp.Boundary,
Box: box,
Clip: stamp.Clip,
ClipBox: clipBox,
})
}
return positions, nil
@@ -139,23 +152,23 @@ func (r *Reader) SignatureStampPositions(stamps []SignatureStamp) ([]SignatureSt
func parseSignatureStampBox(s string) (Box, error) {
parts := strings.Fields(s)
if len(parts) != 4 {
return Box{}, fmt.Errorf("invalid signature stamp boundary: %s", s)
return Box{}, fmt.Errorf("invalid signature stamp box: %s", s)
}
x, err := strconv.ParseFloat(parts[0], 64)
if err != nil {
return Box{}, fmt.Errorf("invalid signature stamp boundary: %s", s)
return Box{}, fmt.Errorf("invalid signature stamp box: %s", s)
}
y, err := strconv.ParseFloat(parts[1], 64)
if err != nil {
return Box{}, fmt.Errorf("invalid signature stamp boundary: %s", s)
return Box{}, fmt.Errorf("invalid signature stamp box: %s", s)
}
w, err := strconv.ParseFloat(parts[2], 64)
if err != nil {
return Box{}, fmt.Errorf("invalid signature stamp boundary: %s", s)
return Box{}, fmt.Errorf("invalid signature stamp box: %s", s)
}
h, err := strconv.ParseFloat(parts[3], 64)
if err != nil {
return Box{}, fmt.Errorf("invalid signature stamp boundary: %s", s)
return Box{}, fmt.Errorf("invalid signature stamp box: %s", s)
}
return Box{X: x, Y: y, W: w, H: h}, nil
}
@@ -212,8 +225,19 @@ func (r *Reader) parseSignatures(doc *Document) error {
}
for _, annot := range sigFile.SignedInfo.StampAnnot {
pageID := annot.PageRef
bbox, _ := ParseBox(annot.Boundary)
r.addStamp(pageID, bbox, sealType, sealData)
bbox, err := parseSignatureStampBox(annot.Boundary)
if err != nil {
continue
}
var clipBox *Box
if annot.Clip != "" {
clip, err := parseSignatureStampBox(annot.Clip)
if err != nil {
continue
}
clipBox = &clip
}
r.addStamp(pageID, bbox, clipBox, sealType, sealData)
}
}(sigRef)
}
@@ -415,18 +439,20 @@ func probeImageMedia(data []byte) (string, []byte) {
// Stamp 印章信息结构
type Stamp struct {
Box Box
Clip *Box
Type string
Data []byte
}
// addStamp 添加印章到页面
// 入参: pageID 页面ID, box 印章区域, sType 印章类型, data 印章数据
func (r *Reader) addStamp(pageID string, box Box, sType string, data []byte) {
// 入参: pageID 页面ID, box 印章区域, clip 裁剪区域, sType 印章类型, data 印章数据
func (r *Reader) addStamp(pageID string, box Box, clip *Box, sType string, data []byte) {
if r.Stamps == nil {
r.Stamps = make(map[string][]Stamp)
}
r.Stamps[pageID] = append(r.Stamps[pageID], Stamp{
Box: box,
Clip: clip,
Type: sType,
Data: data,
})
+132 -9
View File
@@ -66,8 +66,8 @@ type gbtSignerInfo struct {
// 入参: 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 value, ok := normalizeGBT35275SignedValue(signedValue); ok {
return verifyGBT35275SignedData(value, signedData, options)
}
if isSM2SignatureMethod(method) {
return verifyRawDigitalSignature(signedValue, signedData, options)
@@ -204,27 +204,150 @@ func verifyGBT35275SignedData(signedValue, signedData []byte, options *signature
return result, nil
}
// isGBT35275SignedValue 判断签名值是否为GB/T 35275 SignedData
// normalizeGBT35275SignedValue 规范化GB/T 35275 SignedData编码
// 入参: data 签名值数据
// 返回: bool 是否为SignedData
func isGBT35275SignedValue(data []byte) bool {
// 返回: []byte 定长编码数据, bool 是否为SignedData
func normalizeGBT35275SignedValue(data []byte) ([]byte, bool) {
if contentType, ok := gbtContentType(data); ok {
return data, contentType == signContentSignedData
}
der, err := berToDefinite(data)
if err != nil {
return nil, false
}
contentType, ok := gbtContentType(der)
if !ok || contentType != signContentSignedData {
return nil, false
}
return der, true
}
// gbtContentType 读取GB/T 35275内容类型
// 入参: data DER编码数据
// 返回: string 内容类型, bool 是否完成解析
func gbtContentType(data []byte) (string, bool) {
var root asn1.RawValue
rest, err := asn1.Unmarshal(data, &root)
if err != nil || len(rest) != 0 || root.Tag != signASN1Sequence {
return false
return "", false
}
items, ok := asn1Children(root.Bytes)
if !ok || len(items) < 2 {
return false
if !ok || len(items) == 0 {
return "", false
}
if items[0].Tag != asn1.TagOID {
return "", true
}
oid, err := asn1OIDString(items[0])
return err == nil && oid == signContentSignedData
return oid, err == nil
}
// berToDefinite 将BER不定长编码转换为定长编码
// 入参: data BER编码数据
// 返回: []byte 定长编码数据, error 错误信息
func berToDefinite(data []byte) ([]byte, error) {
out, n, err := berValueToDefinite(data)
if err != nil {
return nil, err
}
if n != len(data) {
return nil, fmt.Errorf("invalid BER trailing data")
}
return out, nil
}
// berValueToDefinite 转换单个BER编码值
// 入参: data BER编码数据
// 返回: []byte 定长编码数据, int 已读取长度, error 错误信息
func berValueToDefinite(data []byte) ([]byte, int, error) {
if len(data) < 2 {
return nil, 0, fmt.Errorf("invalid BER value")
}
pos := 1
if data[0]&0x1f == 0x1f {
for pos < len(data) && data[pos]&0x80 != 0 {
pos++
}
pos++
}
if pos >= len(data) {
return nil, 0, fmt.Errorf("invalid BER tag")
}
tag := data[:pos]
firstLength := data[pos]
pos++
if firstLength == 0x80 {
if tag[0]&0x20 == 0 {
return nil, 0, fmt.Errorf("invalid BER indefinite primitive")
}
var content []byte
for {
if len(data)-pos < 2 {
return nil, 0, fmt.Errorf("invalid BER unterminated value")
}
if data[pos] == 0 && data[pos+1] == 0 {
pos += 2
break
}
child, n, err := berValueToDefinite(data[pos:])
if err != nil {
return nil, 0, err
}
content = append(content, child...)
pos += n
}
return wrapBERValue(tag, content), pos, nil
}
length := uint64(firstLength)
if firstLength&0x80 != 0 {
n := int(firstLength & 0x7f)
if n == 0 || n > 8 || len(data)-pos < n {
return nil, 0, fmt.Errorf("invalid BER length")
}
length = 0
for _, b := range data[pos : pos+n] {
length = length<<8 | uint64(b)
}
pos += n
}
if length > uint64(len(data)-pos) {
return nil, 0, fmt.Errorf("invalid BER truncated value")
}
end := pos + int(length)
content := append([]byte(nil), data[pos:end]...)
if tag[0]&0x20 != 0 {
content = content[:0]
for pos < end {
child, n, err := berValueToDefinite(data[pos:end])
if err != nil {
return nil, 0, err
}
content = append(content, child...)
pos += n
}
}
return wrapBERValue(tag, content), end, nil
}
// wrapBERValue 包装定长BER编码值
// 入参: tag 标签, content 内容
// 返回: []byte 定长编码数据
func wrapBERValue(tag, content []byte) []byte {
out := make([]byte, 0, len(tag)+len(content)+9)
out = append(out, tag...)
out = append(out, asn1LengthBytes(len(content))...)
return append(out, content...)
}
// parseGBT35275SignedData 解析GB/T 35275 SignedData
// 入参: data 签名值数据
// 返回: *gbtSignedData SignedData结构, error 错误信息
func parseGBT35275SignedData(data []byte) (*gbtSignedData, error) {
var ok bool
data, ok = normalizeGBT35275SignedValue(data)
if !ok {
return nil, fmt.Errorf("invalid signed data content type")
}
contentType, content, ok, err := parseGBTContentInfoBytes(data)
if err != nil {
return nil, err
+130 -42
View File
@@ -20,6 +20,7 @@ import (
"fmt"
"math/big"
"strings"
"time"
)
const (
@@ -42,6 +43,7 @@ type sesSignature struct {
SignAlg string
Signature []byte
DataHash []byte
Time time.Time
Seal *sesSeal
}
@@ -55,6 +57,7 @@ type sesSeal struct {
PicType string
PicData []byte
CertList sesCertList
Info SignatureSealInfo
}
// sesCertList SES印章证书列表
@@ -71,17 +74,19 @@ type sesCertDigest struct {
// sesVerifyResult SES签章验证结果
type sesVerifyResult struct {
DataHashOK bool
SignedOK bool
SealOK bool
CertOK bool
SignCert SignatureCertInfo
SealCert SignatureCertInfo
SignCertRaw []byte
SealCertRaw []byte
SealRaw []byte
Certs [][]byte
SealType string
DataHashOK bool
SignedOK bool
SealOK bool
CertOK bool
SignCert SignatureCertInfo
SealCert SignatureCertInfo
SignCertRaw []byte
SealCertRaw []byte
SealRaw []byte
Certs [][]byte
SealType string
SealInfo SignatureSealInfo
SignatureTime time.Time
}
// parseSESSignature 解析SES签章值
@@ -119,13 +124,21 @@ func parseSESSignature(data []byte) (*sesSignature, error) {
return nil, err
}
tbsItems, ok := asn1Children(items[0].Bytes)
if !ok || len(tbsItems) < 5 {
if !ok || len(tbsItems) < 5 || len(tbsItems) > 6 {
return nil, fmt.Errorf("invalid ses toSign")
}
seal, err := parseSESSeal(tbsItems[1])
if err != nil {
return nil, err
}
version, err := asn1Integer(tbsItems[0])
if err != nil || version != seal.Info.Version {
return nil, fmt.Errorf("invalid ses version")
}
signatureTime, err := asn1Time(tbsItems[2])
if err != nil {
return nil, err
}
dataHash, err := asn1BitOrOctetBytes(tbsItems[3])
if err != nil {
return nil, err
@@ -136,6 +149,7 @@ func parseSESSignature(data []byte) (*sesSignature, error) {
SignAlg: alg,
Signature: signature,
DataHash: dataHash,
Time: signatureTime,
Seal: seal,
}, nil
}
@@ -156,6 +170,11 @@ func parseSESSignatureV1(items []asn1.RawValue) (*sesSignature, error) {
if err != nil {
return nil, err
}
version, err := asn1Integer(tbsItems[0])
if err != nil || version != seal.Info.Version {
return nil, fmt.Errorf("invalid ses version")
}
signatureTime := parseSESSignatureTimeV1(tbsItems[2])
dataHash, err := asn1BitOrOctetBytes(tbsItems[3])
if err != nil {
return nil, err
@@ -174,10 +193,31 @@ func parseSESSignatureV1(items []asn1.RawValue) (*sesSignature, error) {
SignAlg: alg,
Signature: signature,
DataHash: dataHash,
Time: signatureTime,
Seal: seal,
}, nil
}
// parseSESSignatureTimeV1 解析SES V1签名时间
// 入参: raw 签名时间或时间戳数据
// 返回: time.Time 签名时间
func parseSESSignatureTimeV1(raw asn1.RawValue) time.Time {
data, err := asn1BitOrOctetBytes(raw)
if err != nil {
return time.Time{}
}
if t := parseSignatureDateTime(string(data)); !t.IsZero() {
return t
}
var value asn1.RawValue
rest, err := asn1.Unmarshal(data, &value)
if err != nil || len(rest) != 0 {
return time.Time{}
}
t, _ := asn1Time(value)
return t
}
// verifySESSignature 验证SES签章值
// 入参: data 签章值数据, signedData 被签名数据原文, options 验证选项
// 返回: *sesVerifyResult 验证结果, error 错误信息
@@ -198,6 +238,8 @@ func verifySESSignature(data, signedData []byte, options *signatureVerifyOptions
result.Certs = append(result.Certs, sig.Seal.CertList.Certs...)
result.Certs = append(result.Certs, options.SignCerts...)
result.SealType = sig.Seal.PicType
result.SealInfo = sig.Seal.Info
result.SignatureTime = sig.Time
result.DataHashOK = bytes.Equal(sig.DataHash, signSM3(signedData))
signPub, err := parseSM2PublicKeyFromCert(sig.Cert)
if err != nil {
@@ -243,11 +285,7 @@ func parseSESSeal(raw asn1.RawValue) (*sesSeal, error) {
if !ok || len(infoItems) < 4 || len(infoItems) > 5 {
return nil, fmt.Errorf("invalid ses seal info")
}
version, err := parseSESHeaderVersion(infoItems[0])
if err != nil {
return nil, err
}
certList, err := parseSESCertList(infoItems[2], version)
info, certList, err := parseSESSealInfo(infoItems)
if err != nil {
return nil, err
}
@@ -264,6 +302,7 @@ func parseSESSeal(raw asn1.RawValue) (*sesSeal, error) {
PicType: picType,
PicData: picData,
CertList: certList,
Info: info,
}, nil
}
@@ -291,7 +330,7 @@ func parseSESSealV1(raw asn1.RawValue, items []asn1.RawValue) (*sesSeal, error)
if err != nil {
return nil, err
}
certList, err := parseSESCertListV1(infoItems[2])
info, certList, err := parseSESSealInfo(infoItems)
if err != nil {
return nil, err
}
@@ -309,43 +348,92 @@ func parseSESSealV1(raw asn1.RawValue, items []asn1.RawValue) (*sesSeal, error)
PicType: picType,
PicData: picData,
CertList: certList,
Info: info,
}, nil
}
// parseSESHeaderVersion 解析印章头版本
// 入参: raw 印章信息
// 返回: int 版本号, error 错误信息
func parseSESHeaderVersion(raw asn1.RawValue) (int, error) {
items, ok := asn1Children(raw.Bytes)
if !ok || len(items) < 2 {
return 0, fmt.Errorf("invalid ses header")
// parseSESSealInfo 解析电子印章信息
// 入参: items 印章信息ASN.1子元素
// 返回: SignatureSealInfo 印章信息, sesCertList 证书列表, error 错误信息
func parseSESSealInfo(items []asn1.RawValue) (SignatureSealInfo, sesCertList, error) {
if len(items) < 4 {
return SignatureSealInfo{}, sesCertList{}, fmt.Errorf("invalid ses seal info")
}
return asn1Integer(items[1])
header, ok := asn1Children(items[0].Bytes)
if !ok || len(header) != 3 {
return SignatureSealInfo{}, sesCertList{}, fmt.Errorf("invalid ses header")
}
if asn1String(header[0]) != "ES" {
return SignatureSealInfo{}, sesCertList{}, fmt.Errorf("invalid ses header")
}
version, err := asn1Integer(header[1])
if err != nil {
return SignatureSealInfo{}, sesCertList{}, err
}
property, ok := asn1Children(items[2].Bytes)
if !ok {
return SignatureSealInfo{}, sesCertList{}, fmt.Errorf("invalid ses property")
}
certIndex := 2
timeIndex := 3
if version >= 4 {
certIndex = 3
timeIndex = 4
}
if len(property) != timeIndex+3 {
return SignatureSealInfo{}, sesCertList{}, fmt.Errorf("invalid ses property")
}
typeValue, err := asn1Integer(property[0])
if err != nil {
return SignatureSealInfo{}, sesCertList{}, err
}
certList, err := parseSESCertList(property, version, certIndex)
if err != nil {
return SignatureSealInfo{}, sesCertList{}, err
}
createTime, err := asn1Time(property[timeIndex])
if err != nil {
return SignatureSealInfo{}, sesCertList{}, err
}
validStart, err := asn1Time(property[timeIndex+1])
if err != nil {
return SignatureSealInfo{}, sesCertList{}, err
}
validEnd, err := asn1Time(property[timeIndex+2])
if err != nil {
return SignatureSealInfo{}, sesCertList{}, err
}
return SignatureSealInfo{
Version: version,
ID: strings.TrimSpace(asn1String(items[1])),
VendorID: strings.TrimSpace(asn1String(header[2])),
Type: typeValue,
Name: strings.TrimSpace(asn1String(property[1])),
CreateTime: createTime,
ValidStart: validStart,
ValidEnd: validEnd,
}, certList, nil
}
// parseSESCertList 解析印章证书列表
// 入参: raw 印章属性信息, version 印章版本
// 入参: property 印章属性, version 印章版本, certIndex 证书列表位置
// 返回: sesCertList 证书列表, error 错误信息
func parseSESCertList(raw asn1.RawValue, version int) (sesCertList, error) {
items, ok := asn1Children(raw.Bytes)
if !ok || len(items) < 3 {
return sesCertList{}, fmt.Errorf("invalid ses property")
func parseSESCertList(property []asn1.RawValue, version, certIndex int) (sesCertList, error) {
if version == 1 {
return parseSESCertListV1(property[certIndex])
}
if version < 4 {
return parseSESCertInfoList(items[2])
return parseSESCertInfoList(property[certIndex])
}
if len(items) < 4 {
return sesCertList{}, fmt.Errorf("invalid ses cert list")
}
listType, err := asn1Integer(items[2])
listType, err := asn1Integer(property[certIndex-1])
if err != nil {
return sesCertList{}, err
}
switch listType {
case 1:
return parseSESCertInfoList(items[3])
return parseSESCertInfoList(property[certIndex])
case 2:
return parseSESCertDigestList(items[3])
return parseSESCertDigestList(property[certIndex])
default:
return sesCertList{}, fmt.Errorf("unsupported ses cert list type")
}
@@ -396,12 +484,12 @@ func parseSESCertDigestList(raw asn1.RawValue) (sesCertList, error) {
list := sesCertList{Digests: make([]sesCertDigest, 0, len(items))}
for _, item := range items {
fields, ok := asn1Children(item.Bytes)
if !ok || len(fields) < 2 {
if !ok || len(fields) != 2 {
return sesCertList{}, fmt.Errorf("invalid ses cert digest")
}
method, err := parseGBTAlgorithm(fields[0])
if err != nil {
return sesCertList{}, err
method := strings.TrimSpace(asn1String(fields[0]))
if method == "" {
return sesCertList{}, fmt.Errorf("invalid ses cert digest")
}
digest, err := asn1OctetString(fields[1])
if err != nil {
+123 -33
View File
@@ -18,6 +18,7 @@ import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/md5"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
@@ -37,35 +38,44 @@ import (
)
// SignatureVerifyReport 签名验证报告
// Valid表示签名完整性及调用方指定的证书策略均通过
// Valid表示签名完整性、签名时间语义及调用方指定的证书策略均通过
// SealCertTimeOK仅提供制章证书在签名时间的状态信息, 不参与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
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 判断签名完整性是否有效
@@ -75,13 +85,14 @@ func (report SignatureVerifyReport) IntegrityValid() bool {
}
// TrustedValid 判断签名是否可信有效
// 返回: bool 签名完整性、证书信任及证书有效期是否均验证通过
// 返回: bool 签名完整性、时间语义、证书信任及证书有效期是否均验证通过
func (report SignatureVerifyReport) TrustedValid() bool {
return report.IntegrityValid() && report.CertTrustChecked && report.CertTrustOK && report.CertTimeChecked && report.CertTimeOK
return report.IntegrityValid() && report.certificatePolicyOK() && report.CertTrustChecked && report.CertTimeChecked
}
// SignatureCertInfo 签名证书信息
type SignatureCertInfo struct {
Raw []byte
Subject string
CommonName string
Organization string
@@ -91,6 +102,18 @@ type SignatureCertInfo struct {
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
@@ -239,6 +262,9 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options *
Type: sigRef.Type,
SealMatchOK: true,
}
if report.Type == "" {
report.Type = SignTypeSeal
}
sigData, err := r.readFileExact(sigPath)
if err != nil {
report.Error = err.Error()
@@ -253,6 +279,9 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options *
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)
@@ -266,7 +295,7 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options *
report.Error = err.Error()
return report
}
switch sigRef.Type {
switch report.Type {
case SignTypeSign:
result, err := verifyDigitalSignature(report.SignatureMethod, report.DigestMethod, signedValue, sigData, options)
if err != nil {
@@ -279,19 +308,23 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options *
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:
case SignTypeSeal:
default:
report.Error = fmt.Sprintf("unsupported signature type: %s", sigRef.Type)
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 {
@@ -302,6 +335,7 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options *
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)
@@ -383,6 +417,10 @@ func (r *Reader) readFileExact(name string) ([]byte, error) {
// 入参: 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
}
@@ -397,6 +435,8 @@ func signatureDigest(method string, data []byte) ([]byte, error) {
// 返回: 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":
@@ -453,6 +493,9 @@ func signatureMethodHash(method, digestMethod string) (crypto.Hash, error) {
// 返回: []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[:]
@@ -623,9 +666,38 @@ func (report *SignatureVerifyReport) applySignatureCertificatePolicy(options *si
}
}
// 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
}
@@ -790,6 +862,7 @@ func signatureCertInfo(data []byte) 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"),
@@ -803,6 +876,23 @@ func signatureCertInfo(data []byte) SignatureCertInfo {
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"