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 Stamps map[string][]Stamp
Annots map[string][]Annotation Annots map[string][]Annotation
fileIndex map[string]*zip.File fileIndex map[string]*zip.File
fileIndexFold map[string]*zip.File
} }
// Close 关闭阅读器 // Close 关闭阅读器
@@ -54,14 +53,9 @@ func (r *Reader) Close() error {
// 返回: error 错误信息 // 返回: error 错误信息
func (r *Reader) initRoot() error { func (r *Reader) initRoot() error {
r.fileIndex = make(map[string]*zip.File) r.fileIndex = make(map[string]*zip.File)
r.fileIndexFold = make(map[string]*zip.File)
for _, f := range r.Zip.File { for _, f := range r.Zip.File {
name := cleanPackagePath(f.Name) name := cleanPackagePath(f.Name)
r.fileIndex[name] = f r.fileIndex[name] = f
fold := strings.ToLower(name)
if _, ok := r.fileIndexFold[fold]; !ok {
r.fileIndexFold[fold] = f
}
} }
data, err := r.readFile("OFD.xml") data, err := r.readFile("OFD.xml")
if err != nil { if err != nil {
@@ -117,9 +111,6 @@ func (r *Reader) packageFile(name string) (*zip.File, bool) {
if f, ok := r.fileIndex[name]; ok { if f, ok := r.fileIndex[name]; ok {
return f, true return f, true
} }
if f, ok := r.fileIndexFold[strings.ToLower(name)]; ok {
return f, true
}
return nil, false return nil, false
} }
@@ -176,7 +167,7 @@ func (r *Reader) loadRes(resPath string) {
if resPath == "" { if resPath == "" {
return return
} }
fullPath := path.Join(r.RootDir, resPath) fullPath := r.ResPath(resPath)
data, err := r.readFile(fullPath) data, err := r.readFile(fullPath)
if err != nil { if err != nil {
return return
@@ -235,7 +226,7 @@ func resolveResourcePath(resPath, baseLoc, filePath string) string {
// 入参: page 页面对象 // 入参: page 页面对象
// 返回: *PageContent 页面内容, error 错误信息 // 返回: *PageContent 页面内容, error 错误信息
func (r *Reader) PageContent(page 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) data, err := r.readFile(fullPath)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -260,7 +251,9 @@ func (r *Reader) ResPath(resLink string) string {
if resLink == "" { if resLink == "" {
return "" return ""
} }
resLink = strings.TrimPrefix(resLink, "/") if strings.HasPrefix(resLink, "/") {
return cleanPackagePath(resLink)
}
resLink = path.Clean(resLink) resLink = path.Clean(resLink)
rootDir := strings.TrimPrefix(strings.ReplaceAll(r.RootDir, "\\", "/"), "/") rootDir := strings.TrimPrefix(strings.ReplaceAll(r.RootDir, "\\", "/"), "/")
if rootDir != "" && (resLink == rootDir || strings.HasPrefix(resLink, rootDir+"/")) { if rootDir != "" && (resLink == rootDir || strings.HasPrefix(resLink, rootDir+"/")) {
+76 -10
View File
@@ -18,16 +18,23 @@ import (
"bytes" "bytes"
"image" "image"
"image/color" "image/color"
"image/draw"
"math"
"github.com/tdewolff/canvas" "github.com/tdewolff/canvas"
"github.com/tdewolff/canvas/renderers/rasterizer"
) )
// renderStamp 渲染印章 // renderStamp 渲染印章
// 入参: ctx 画布上下文, s 印章对象, pageH 页面高度 // 入参: ctx 画布上下文, s 印章对象, pageH 页面高度
func (r *Renderer) renderStamp(ctx *canvas.Context, s Stamp, pageH float64) { 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.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))) reader, err := NewReader(bytes.NewReader(s.Data), int64(len(s.Data)))
if err == nil { if err == nil {
defer reader.Close() defer reader.Close()
@@ -44,8 +51,8 @@ func (r *Renderer) renderStamp(ctx *canvas.Context, s Stamp, pageH float64) {
continue continue
} }
ctx.Push() ctx.Push()
ctx.Translate(x, screenY) ctx.Translate(s.Box.X, pageH-(s.Box.Y+s.Box.H))
ctx.Scale(w/sealBox.W, h/sealBox.H) ctx.Scale(s.Box.W/sealBox.W, s.Box.H/sealBox.H)
renderer.renderPageToContext(ctx, content, false) renderer.renderPageToContext(ctx, content, false)
ctx.Pop() ctx.Pop()
} }
@@ -56,17 +63,76 @@ func (r *Renderer) renderStamp(ctx *canvas.Context, s Stamp, pageH float64) {
if len(s.Data) > 0 { if len(s.Data) > 0 {
img, _, err := decodeImageData(s.Data) img, _, err := decodeImageData(s.Data)
if err == nil { if err == nil {
img = stampImageWithTransparentWhite(img) r.renderStampImage(ctx, stampImageWithTransparentWhite(img), s, pageH)
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()
return 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 处理印章图片白色底色 // stampImageWithTransparentWhite 处理印章图片白色底色
// 入参: img 印章图片对象 // 入参: img 印章图片对象
// 返回: image.Image 处理后的印章图片对象 // 返回: image.Image 处理后的印章图片对象
+35 -9
View File
@@ -80,6 +80,7 @@ type SignatureStamp struct {
ID string `xml:"ID,attr"` ID string `xml:"ID,attr"`
PageRef string `xml:"PageRef,attr"` PageRef string `xml:"PageRef,attr"`
Boundary string `xml:"Boundary,attr"` Boundary string `xml:"Boundary,attr"`
Clip string `xml:"Clip,attr"`
} }
// SignatureStampPosition 签名外观位置信息 // SignatureStampPosition 签名外观位置信息
@@ -89,6 +90,8 @@ type SignatureStampPosition struct {
PageID string PageID string
Boundary string Boundary string
Box Box Box Box
Clip string
ClipBox *Box
} }
// SignatureReferences 签名保护文件列表 // SignatureReferences 签名保护文件列表
@@ -122,12 +125,22 @@ func (r *Reader) SignatureStampPositions(stamps []SignatureStamp) ([]SignatureSt
if err != nil { if err != nil {
return nil, err 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{ positions = append(positions, SignatureStampPosition{
ID: stamp.ID, ID: stamp.ID,
Page: page, Page: page,
PageID: stamp.PageRef, PageID: stamp.PageRef,
Boundary: stamp.Boundary, Boundary: stamp.Boundary,
Box: box, Box: box,
Clip: stamp.Clip,
ClipBox: clipBox,
}) })
} }
return positions, nil return positions, nil
@@ -139,23 +152,23 @@ func (r *Reader) SignatureStampPositions(stamps []SignatureStamp) ([]SignatureSt
func parseSignatureStampBox(s string) (Box, error) { func parseSignatureStampBox(s string) (Box, error) {
parts := strings.Fields(s) parts := strings.Fields(s)
if len(parts) != 4 { 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) x, err := strconv.ParseFloat(parts[0], 64)
if err != nil { 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) y, err := strconv.ParseFloat(parts[1], 64)
if err != nil { 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) w, err := strconv.ParseFloat(parts[2], 64)
if err != nil { 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) h, err := strconv.ParseFloat(parts[3], 64)
if err != nil { 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 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 { for _, annot := range sigFile.SignedInfo.StampAnnot {
pageID := annot.PageRef pageID := annot.PageRef
bbox, _ := ParseBox(annot.Boundary) bbox, err := parseSignatureStampBox(annot.Boundary)
r.addStamp(pageID, bbox, sealType, sealData) 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) }(sigRef)
} }
@@ -415,18 +439,20 @@ func probeImageMedia(data []byte) (string, []byte) {
// Stamp 印章信息结构 // Stamp 印章信息结构
type Stamp struct { type Stamp struct {
Box Box Box Box
Clip *Box
Type string Type string
Data []byte Data []byte
} }
// addStamp 添加印章到页面 // addStamp 添加印章到页面
// 入参: pageID 页面ID, box 印章区域, sType 印章类型, data 印章数据 // 入参: pageID 页面ID, box 印章区域, clip 裁剪区域, sType 印章类型, data 印章数据
func (r *Reader) addStamp(pageID string, box Box, sType string, data []byte) { func (r *Reader) addStamp(pageID string, box Box, clip *Box, sType string, data []byte) {
if r.Stamps == nil { if r.Stamps == nil {
r.Stamps = make(map[string][]Stamp) r.Stamps = make(map[string][]Stamp)
} }
r.Stamps[pageID] = append(r.Stamps[pageID], Stamp{ r.Stamps[pageID] = append(r.Stamps[pageID], Stamp{
Box: box, Box: box,
Clip: clip,
Type: sType, Type: sType,
Data: data, Data: data,
}) })
+132 -9
View File
@@ -66,8 +66,8 @@ type gbtSignerInfo struct {
// 入参: method 签名算法, digestMethod 摘要算法, signedValue 签名值, signedData 被签名原文, options 验证选项 // 入参: method 签名算法, digestMethod 摘要算法, signedValue 签名值, signedData 被签名原文, options 验证选项
// 返回: *digitalVerifyResult 验证结果, error 错误信息 // 返回: *digitalVerifyResult 验证结果, error 错误信息
func verifyDigitalSignature(method, digestMethod string, signedValue, signedData []byte, options *signatureVerifyOptions) (*digitalVerifyResult, error) { func verifyDigitalSignature(method, digestMethod string, signedValue, signedData []byte, options *signatureVerifyOptions) (*digitalVerifyResult, error) {
if isGBT35275SignedValue(signedValue) { if value, ok := normalizeGBT35275SignedValue(signedValue); ok {
return verifyGBT35275SignedData(signedValue, signedData, options) return verifyGBT35275SignedData(value, signedData, options)
} }
if isSM2SignatureMethod(method) { if isSM2SignatureMethod(method) {
return verifyRawDigitalSignature(signedValue, signedData, options) return verifyRawDigitalSignature(signedValue, signedData, options)
@@ -204,27 +204,150 @@ func verifyGBT35275SignedData(signedValue, signedData []byte, options *signature
return result, nil return result, nil
} }
// isGBT35275SignedValue 判断签名值是否为GB/T 35275 SignedData // normalizeGBT35275SignedValue 规范化GB/T 35275 SignedData编码
// 入参: data 签名值数据 // 入参: data 签名值数据
// 返回: bool 是否为SignedData // 返回: []byte 定长编码数据, bool 是否为SignedData
func isGBT35275SignedValue(data []byte) bool { 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 var root asn1.RawValue
rest, err := asn1.Unmarshal(data, &root) rest, err := asn1.Unmarshal(data, &root)
if err != nil || len(rest) != 0 || root.Tag != signASN1Sequence { if err != nil || len(rest) != 0 || root.Tag != signASN1Sequence {
return false return "", false
} }
items, ok := asn1Children(root.Bytes) items, ok := asn1Children(root.Bytes)
if !ok || len(items) < 2 { if !ok || len(items) == 0 {
return false return "", false
}
if items[0].Tag != asn1.TagOID {
return "", true
} }
oid, err := asn1OIDString(items[0]) 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 // parseGBT35275SignedData 解析GB/T 35275 SignedData
// 入参: data 签名值数据 // 入参: data 签名值数据
// 返回: *gbtSignedData SignedData结构, error 错误信息 // 返回: *gbtSignedData SignedData结构, error 错误信息
func parseGBT35275SignedData(data []byte) (*gbtSignedData, 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) contentType, content, ok, err := parseGBTContentInfoBytes(data)
if err != nil { if err != nil {
return nil, err return nil, err
+119 -31
View File
@@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"strings" "strings"
"time"
) )
const ( const (
@@ -42,6 +43,7 @@ type sesSignature struct {
SignAlg string SignAlg string
Signature []byte Signature []byte
DataHash []byte DataHash []byte
Time time.Time
Seal *sesSeal Seal *sesSeal
} }
@@ -55,6 +57,7 @@ type sesSeal struct {
PicType string PicType string
PicData []byte PicData []byte
CertList sesCertList CertList sesCertList
Info SignatureSealInfo
} }
// sesCertList SES印章证书列表 // sesCertList SES印章证书列表
@@ -82,6 +85,8 @@ type sesVerifyResult struct {
SealRaw []byte SealRaw []byte
Certs [][]byte Certs [][]byte
SealType string SealType string
SealInfo SignatureSealInfo
SignatureTime time.Time
} }
// parseSESSignature 解析SES签章值 // parseSESSignature 解析SES签章值
@@ -119,13 +124,21 @@ func parseSESSignature(data []byte) (*sesSignature, error) {
return nil, err return nil, err
} }
tbsItems, ok := asn1Children(items[0].Bytes) 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") return nil, fmt.Errorf("invalid ses toSign")
} }
seal, err := parseSESSeal(tbsItems[1]) seal, err := parseSESSeal(tbsItems[1])
if err != nil { if err != nil {
return nil, err 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]) dataHash, err := asn1BitOrOctetBytes(tbsItems[3])
if err != nil { if err != nil {
return nil, err return nil, err
@@ -136,6 +149,7 @@ func parseSESSignature(data []byte) (*sesSignature, error) {
SignAlg: alg, SignAlg: alg,
Signature: signature, Signature: signature,
DataHash: dataHash, DataHash: dataHash,
Time: signatureTime,
Seal: seal, Seal: seal,
}, nil }, nil
} }
@@ -156,6 +170,11 @@ func parseSESSignatureV1(items []asn1.RawValue) (*sesSignature, error) {
if err != nil { if err != nil {
return nil, err 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]) dataHash, err := asn1BitOrOctetBytes(tbsItems[3])
if err != nil { if err != nil {
return nil, err return nil, err
@@ -174,10 +193,31 @@ func parseSESSignatureV1(items []asn1.RawValue) (*sesSignature, error) {
SignAlg: alg, SignAlg: alg,
Signature: signature, Signature: signature,
DataHash: dataHash, DataHash: dataHash,
Time: signatureTime,
Seal: seal, Seal: seal,
}, nil }, 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签章值 // verifySESSignature 验证SES签章值
// 入参: data 签章值数据, signedData 被签名数据原文, options 验证选项 // 入参: data 签章值数据, signedData 被签名数据原文, options 验证选项
// 返回: *sesVerifyResult 验证结果, error 错误信息 // 返回: *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, sig.Seal.CertList.Certs...)
result.Certs = append(result.Certs, options.SignCerts...) result.Certs = append(result.Certs, options.SignCerts...)
result.SealType = sig.Seal.PicType result.SealType = sig.Seal.PicType
result.SealInfo = sig.Seal.Info
result.SignatureTime = sig.Time
result.DataHashOK = bytes.Equal(sig.DataHash, signSM3(signedData)) result.DataHashOK = bytes.Equal(sig.DataHash, signSM3(signedData))
signPub, err := parseSM2PublicKeyFromCert(sig.Cert) signPub, err := parseSM2PublicKeyFromCert(sig.Cert)
if err != nil { if err != nil {
@@ -243,11 +285,7 @@ func parseSESSeal(raw asn1.RawValue) (*sesSeal, error) {
if !ok || len(infoItems) < 4 || len(infoItems) > 5 { if !ok || len(infoItems) < 4 || len(infoItems) > 5 {
return nil, fmt.Errorf("invalid ses seal info") return nil, fmt.Errorf("invalid ses seal info")
} }
version, err := parseSESHeaderVersion(infoItems[0]) info, certList, err := parseSESSealInfo(infoItems)
if err != nil {
return nil, err
}
certList, err := parseSESCertList(infoItems[2], version)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -264,6 +302,7 @@ func parseSESSeal(raw asn1.RawValue) (*sesSeal, error) {
PicType: picType, PicType: picType,
PicData: picData, PicData: picData,
CertList: certList, CertList: certList,
Info: info,
}, nil }, nil
} }
@@ -291,7 +330,7 @@ func parseSESSealV1(raw asn1.RawValue, items []asn1.RawValue) (*sesSeal, error)
if err != nil { if err != nil {
return nil, err return nil, err
} }
certList, err := parseSESCertListV1(infoItems[2]) info, certList, err := parseSESSealInfo(infoItems)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -309,43 +348,92 @@ func parseSESSealV1(raw asn1.RawValue, items []asn1.RawValue) (*sesSeal, error)
PicType: picType, PicType: picType,
PicData: picData, PicData: picData,
CertList: certList, CertList: certList,
Info: info,
}, nil }, nil
} }
// parseSESHeaderVersion 解析印章头版本 // parseSESSealInfo 解析电子印章信息
// 入参: raw 印章信息 // 入参: items 印章信息ASN.1子元素
// 返回: int 版本号, error 错误信息 // 返回: SignatureSealInfo 印章信息, sesCertList 证书列表, error 错误信息
func parseSESHeaderVersion(raw asn1.RawValue) (int, error) { func parseSESSealInfo(items []asn1.RawValue) (SignatureSealInfo, sesCertList, error) {
items, ok := asn1Children(raw.Bytes) if len(items) < 4 {
if !ok || len(items) < 2 { return SignatureSealInfo{}, sesCertList{}, fmt.Errorf("invalid ses seal info")
return 0, fmt.Errorf("invalid ses header")
} }
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 解析印章证书列表 // parseSESCertList 解析印章证书列表
// 入参: raw 印章属性信息, version 印章版本 // 入参: property 印章属性, version 印章版本, certIndex 证书列表位置
// 返回: sesCertList 证书列表, error 错误信息 // 返回: sesCertList 证书列表, error 错误信息
func parseSESCertList(raw asn1.RawValue, version int) (sesCertList, error) { func parseSESCertList(property []asn1.RawValue, version, certIndex int) (sesCertList, error) {
items, ok := asn1Children(raw.Bytes) if version == 1 {
if !ok || len(items) < 3 { return parseSESCertListV1(property[certIndex])
return sesCertList{}, fmt.Errorf("invalid ses property")
} }
if version < 4 { if version < 4 {
return parseSESCertInfoList(items[2]) return parseSESCertInfoList(property[certIndex])
} }
if len(items) < 4 { listType, err := asn1Integer(property[certIndex-1])
return sesCertList{}, fmt.Errorf("invalid ses cert list")
}
listType, err := asn1Integer(items[2])
if err != nil { if err != nil {
return sesCertList{}, err return sesCertList{}, err
} }
switch listType { switch listType {
case 1: case 1:
return parseSESCertInfoList(items[3]) return parseSESCertInfoList(property[certIndex])
case 2: case 2:
return parseSESCertDigestList(items[3]) return parseSESCertDigestList(property[certIndex])
default: default:
return sesCertList{}, fmt.Errorf("unsupported ses cert list type") 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))} list := sesCertList{Digests: make([]sesCertDigest, 0, len(items))}
for _, item := range items { for _, item := range items {
fields, ok := asn1Children(item.Bytes) fields, ok := asn1Children(item.Bytes)
if !ok || len(fields) < 2 { if !ok || len(fields) != 2 {
return sesCertList{}, fmt.Errorf("invalid ses cert digest") return sesCertList{}, fmt.Errorf("invalid ses cert digest")
} }
method, err := parseGBTAlgorithm(fields[0]) method := strings.TrimSpace(asn1String(fields[0]))
if err != nil { if method == "" {
return sesCertList{}, err return sesCertList{}, fmt.Errorf("invalid ses cert digest")
} }
digest, err := asn1OctetString(fields[1]) digest, err := asn1OctetString(fields[1])
if err != nil { if err != nil {
+96 -6
View File
@@ -18,6 +18,7 @@ import (
"bytes" "bytes"
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/md5"
"crypto/rsa" "crypto/rsa"
"crypto/sha1" "crypto/sha1"
"crypto/sha256" "crypto/sha256"
@@ -37,7 +38,8 @@ import (
) )
// SignatureVerifyReport 签名验证报告 // SignatureVerifyReport 签名验证报告
// Valid表示签名完整性及调用方指定的证书策略均通过 // Valid表示签名完整性、签名时间语义及调用方指定的证书策略均通过
// SealCertTimeOK仅提供制章证书在签名时间的状态信息, 不参与Valid判断
type SignatureVerifyReport struct { type SignatureVerifyReport struct {
ID string ID string
BaseLoc string BaseLoc string
@@ -46,9 +48,11 @@ type SignatureVerifyReport struct {
Signer string Signer string
SignCert SignatureCertInfo SignCert SignatureCertInfo
SealCert SignatureCertInfo SealCert SignatureCertInfo
SealInfo SignatureSealInfo
SealType string SealType string
SignatureMethod string SignatureMethod string
SignatureDateTime string SignatureDateTime string
SignatureTime time.Time
DigestMethod string DigestMethod string
References []SignatureReferenceVerify References []SignatureReferenceVerify
Stamps []SignatureStamp Stamps []SignatureStamp
@@ -60,6 +64,12 @@ type SignatureVerifyReport struct {
SealOK bool SealOK bool
SealMatchOK bool SealMatchOK bool
CertOK bool CertOK bool
SignatureTimeChecked bool
SignatureTimeOK bool
SealCertTimeChecked bool
SealCertTimeOK bool
SealTimeChecked bool
SealTimeOK bool
CertTimeChecked bool CertTimeChecked bool
CertTimeOK bool CertTimeOK bool
CertTrustChecked bool CertTrustChecked bool
@@ -75,13 +85,14 @@ func (report SignatureVerifyReport) IntegrityValid() bool {
} }
// TrustedValid 判断签名是否可信有效 // TrustedValid 判断签名是否可信有效
// 返回: bool 签名完整性、证书信任及证书有效期是否均验证通过 // 返回: bool 签名完整性、时间语义、证书信任及证书有效期是否均验证通过
func (report SignatureVerifyReport) TrustedValid() 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 签名证书信息 // SignatureCertInfo 签名证书信息
type SignatureCertInfo struct { type SignatureCertInfo struct {
Raw []byte
Subject string Subject string
CommonName string CommonName string
Organization string Organization string
@@ -91,6 +102,18 @@ type SignatureCertInfo struct {
NotAfter 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 签名保护文件验证结果 // SignatureReferenceVerify 签名保护文件验证结果
type SignatureReferenceVerify struct { type SignatureReferenceVerify struct {
FileRef string FileRef string
@@ -239,6 +262,9 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options *
Type: sigRef.Type, Type: sigRef.Type,
SealMatchOK: true, SealMatchOK: true,
} }
if report.Type == "" {
report.Type = SignTypeSeal
}
sigData, err := r.readFileExact(sigPath) sigData, err := r.readFileExact(sigPath)
if err != nil { if err != nil {
report.Error = err.Error() report.Error = err.Error()
@@ -253,6 +279,9 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options *
report.SignatureMethod = sigFile.SignedInfo.SignatureMethod report.SignatureMethod = sigFile.SignedInfo.SignatureMethod
report.SignatureDateTime = sigFile.SignedInfo.SignatureDateTime report.SignatureDateTime = sigFile.SignedInfo.SignatureDateTime
report.DigestMethod = sigFile.SignedInfo.References.CheckMethod report.DigestMethod = sigFile.SignedInfo.References.CheckMethod
if report.DigestMethod == "" {
report.DigestMethod = "MD5"
}
report.References = r.verifySignatureReferences(sigPath, sigFile.SignedInfo.References) report.References = r.verifySignatureReferences(sigPath, sigFile.SignedInfo.References)
report.Stamps = append(report.Stamps, sigFile.SignedInfo.StampAnnot...) report.Stamps = append(report.Stamps, sigFile.SignedInfo.StampAnnot...)
report.StampPositions, err = r.SignatureStampPositions(report.Stamps) report.StampPositions, err = r.SignatureStampPositions(report.Stamps)
@@ -266,7 +295,7 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options *
report.Error = err.Error() report.Error = err.Error()
return report return report
} }
switch sigRef.Type { switch report.Type {
case SignTypeSign: case SignTypeSign:
result, err := verifyDigitalSignature(report.SignatureMethod, report.DigestMethod, signedValue, sigData, options) result, err := verifyDigitalSignature(report.SignatureMethod, report.DigestMethod, signedValue, sigData, options)
if err != nil { if err != nil {
@@ -279,19 +308,23 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options *
report.CertOK = result.CertOK report.CertOK = result.CertOK
report.SignCert = result.CertInfo report.SignCert = result.CertInfo
report.Signer = result.CertInfo.CommonName report.Signer = result.CertInfo.CommonName
report.SignatureTime = parseSignatureDateTime(report.SignatureDateTime)
report.applySignatureTimePolicy()
report.applySignatureCertificatePolicy(options, result.SignerCerts, result.Certs) report.applySignatureCertificatePolicy(options, result.SignerCerts, result.Certs)
report.Valid = report.IntegrityValid() && report.certificatePolicyOK() report.Valid = report.IntegrityValid() && report.certificatePolicyOK()
return report return report
case "", SignTypeSeal: case SignTypeSeal:
default: default:
report.Error = fmt.Sprintf("unsupported signature type: %s", sigRef.Type) report.Error = fmt.Sprintf("unsupported signature type: %s", report.Type)
return report return report
} }
sesResult, err := verifySESSignature(signedValue, sigData, options) sesResult, err := verifySESSignature(signedValue, sigData, options)
if sesResult != nil { if sesResult != nil {
report.SignCert = sesResult.SignCert report.SignCert = sesResult.SignCert
report.SealCert = sesResult.SealCert report.SealCert = sesResult.SealCert
report.SealInfo = sesResult.SealInfo
report.SealType = sesResult.SealType report.SealType = sesResult.SealType
report.SignatureTime = sesResult.SignatureTime
report.Signer = sesResult.SignCert.CommonName report.Signer = sesResult.SignCert.CommonName
} }
if err != nil { if err != nil {
@@ -302,6 +335,7 @@ func (r *Reader) verifySignature(sigListPath string, sigRef Signature, options *
report.SignedValueOK = sesResult.SignedOK report.SignedValueOK = sesResult.SignedOK
report.SealOK = sesResult.SealOK report.SealOK = sesResult.SealOK
report.CertOK = sesResult.CertOK report.CertOK = sesResult.CertOK
report.applySignatureTimePolicy()
report.applySignatureCertificatePolicy(options, [][]byte{sesResult.SignCertRaw, sesResult.SealCertRaw}, sesResult.Certs) report.applySignatureCertificatePolicy(options, [][]byte{sesResult.SignCertRaw, sesResult.SealCertRaw}, sesResult.Certs)
if sigFile.SignedInfo.Seal.BaseLoc != "" { if sigFile.SignedInfo.Seal.BaseLoc != "" {
sealPath := signatureRefPath(sigPath, 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 原文数据 // 入参: method 摘要算法, data 原文数据
// 返回: []byte 摘要值, error 错误信息 // 返回: []byte 摘要值, error 错误信息
func signatureDigest(method string, data []byte) ([]byte, error) { func signatureDigest(method string, data []byte) ([]byte, error) {
if strings.TrimSpace(method) == "" {
sum := md5.Sum(data)
return sum[:], nil
}
if isSM3DigestMethod(method) { if isSM3DigestMethod(method) {
return signSM3(data), nil return signSM3(data), nil
} }
@@ -397,6 +435,8 @@ func signatureDigest(method string, data []byte) ([]byte, error) {
// 返回: crypto.Hash 摘要算法, bool 是否支持 // 返回: crypto.Hash 摘要算法, bool 是否支持
func signatureDigestHash(method string) (crypto.Hash, bool) { func signatureDigestHash(method string) (crypto.Hash, bool) {
switch signatureMethodText(method) { switch signatureMethodText(method) {
case "1.2.840.113549.2.5", "MD5":
return crypto.MD5, true
case "1.3.14.3.2.26", "SHA1": case "1.3.14.3.2.26", "SHA1":
return crypto.SHA1, true return crypto.SHA1, true
case "2.16.840.1.101.3.4.2.4", "SHA224": case "2.16.840.1.101.3.4.2.4", "SHA224":
@@ -453,6 +493,9 @@ func signatureMethodHash(method, digestMethod string) (crypto.Hash, error) {
// 返回: []byte 摘要值 // 返回: []byte 摘要值
func signatureHashBytes(h crypto.Hash, data []byte) []byte { func signatureHashBytes(h crypto.Hash, data []byte) []byte {
switch h { switch h {
case crypto.MD5:
sum := md5.Sum(data)
return sum[:]
case crypto.SHA1: case crypto.SHA1:
sum := sha1.Sum(data) sum := sha1.Sum(data)
return sum[:] 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 判断证书策略是否通过 // certificatePolicyOK 判断证书策略是否通过
// 返回: bool 是否通过 // 返回: bool 是否通过
func (report SignatureVerifyReport) 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 { if report.CertTimeChecked && !report.CertTimeOK {
return false return false
} }
@@ -790,6 +862,7 @@ func signatureCertInfo(data []byte) SignatureCertInfo {
subject := certificateNameValues(cert.SubjectValue) subject := certificateNameValues(cert.SubjectValue)
issuer := certificateNameValues(cert.IssuerValue) issuer := certificateNameValues(cert.IssuerValue)
info := SignatureCertInfo{ info := SignatureCertInfo{
Raw: append([]byte(nil), data...),
Subject: certificateNameString(subject), Subject: certificateNameString(subject),
CommonName: certificateNameFirst(subject, "2.5.4.3"), CommonName: certificateNameFirst(subject, "2.5.4.3"),
Organization: certificateNameFirst(subject, "2.5.4.10"), Organization: certificateNameFirst(subject, "2.5.4.10"),
@@ -803,6 +876,23 @@ func signatureCertInfo(data []byte) SignatureCertInfo {
return info 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 ( const (
signatureExtensionKeyUsage = "2.5.29.15" signatureExtensionKeyUsage = "2.5.29.15"
signatureExtensionBasicConstraints = "2.5.29.19" signatureExtensionBasicConstraints = "2.5.29.19"