求助:怎么使用 go 实现 pdf 的数字签名与校验 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
cdx
V2EX    Go 编程语言

求助:怎么使用 go 实现 pdf 的数字签名与校验

  •  
  •   cdx 2024 年 9 月 24 日 2067 次点击
    这是一个创建于 514 天前的主题,其中的信息可能已经有所发展或是发生改变。

    目前有业务需要给 pdf 合同进行电子签名,目前使用的是 https://github.com/digitorus/pdfsign 这个库,但是碰到一个问题,首先是能够正常签名,但是校验时出现了一些问题。

    当对已经签名过的 pdf 文件在末尾添加几个随机字节,此时使用 pdfsign 去检测,是没有办法检测到该文件已经被篡改了,在 adobe reader 中是能够检测到该文件有问题。

    这里不讨论公司是否拥有这个资质的问题

    有几个需要帮助的地方:

    1. 目前市面上常见的 pdf 签名方式是否与 pdfsign 类似
    2. 怎么解决这个问题
    3. 是否有其他依赖库实现 pdf 的数字签名与校验

    代码:

    package main import ( "crypto" "crypto/rsa" "crypto/x509" "encoding/json" "encoding/pem" "errors" "github.com/digitorus/pdf" "github.com/digitorus/pdfsign/revocation" "github.com/digitorus/pdfsign/sign" "github.com/digitorus/pdfsign/verify" "log" "os" "time" ) func main() { err := run("a.pdf", "b.pdf") if err != nil { panic(err) } data, err := os.ReadFile("b.pdf") if err != nil { panic(err) } data = append(data, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}...) err = os.WriteFile("c.pdf", data, 0644) if err != nil { panic(err) } verifyPdf("c.pdf") } func verifyPdf(pdfName string) { input_file, err := os.Open(pdfName) if err != nil { panic(err) } defer input_file.Close() resp, err := verify.File(input_file) if err != nil { panic(err) } jsonData, err := json.MarshalIndent(resp, "", "\t") if err != nil { panic(err) } // 将 jsonData 的数据写入文件 err = os.WriteFile("verify.json", jsonData, 0644) return } func run(input, output string) error { input_file, err := os.Open(input) if err != nil { panic(err) } defer input_file.Close() output_file, err := os.Create(output) if err != nil { panic(err) } defer output_file.Close() finfo, err := input_file.Stat() if err != nil { panic(err) } size := finfo.Size() rdr, err := pdf.NewReader(input_file, size) if err != nil { panic(err) } certificate_data, err := os.ReadFile("certificate.crt") if err != nil { panic(err) } certificate_data_block, _ := pem.Decode(certificate_data) if certificate_data_block == nil { //log.Fatal(errors.New("failed to parse PEM block containing the certificate")) panic(err) } cert, err := x509.ParseCertificate(certificate_data_block.Bytes) if err != nil { panic(err) } privateKeyFs, err := os.ReadFile("private_key.pem") if err != nil { panic(err) } key_data_block, _ := pem.Decode(privateKeyFs) if key_data_block == nil { panic(errors.New("failed to parse PEM block containing the private key")) } // 尝试解析 PKCS#1 格式的私钥 pkey, err := x509.ParsePKCS1PrivateKey(key_data_block.Bytes) if err != nil { var t any t, err = x509.ParsePKCS8PrivateKey(key_data_block.Bytes) pkey = t.(*rsa.PrivateKey) if err != nil { panic(err) } } certificate_chains := make([][]*x509.Certificate, 0) err = sign.Sign(input_file, output_file, rdr, size, sign.SignData{ Signature: sign.SignDataSignature{ Info: sign.SignDataSignatureInfo{ Name: "xx", Location: "xx", Reason: "xx", ContactInfo: "xxx", Date: time.Now().Local(), }, CertType: sign.CertificationSignature, DocMDPPerm: sign.AllowFillingExistingFormFieldsAndSignaturesPerms, }, Signer: pkey, // crypto.Signer DigestAlgorithm: crypto.SHA256, // hash algorithm for the digest creation Certificate: cert, // x509.Certificate CertificateChains: certificate_chains, // x509.Certificate.Verify() TSA: sign.TSA{ URL: "https://freetsa.org/tsr", Username: "", Password: "", }, // The follow options are likely to change in a future release // // cache revocation data when bulk signing RevocationData: revocation.InfoArchival{}, // custom revocation lookup RevocationFunction: sign.DefaultEmbedRevocationStatusFunction, }) if err != nil { panic(err) } else { log.Println("Signed PDF written to " + output) } return nil } /* 自签私钥与证书生成 1. 生成私钥 openssl genpkey -algorithm RSA -out private_key.pem 2. 创建证书签名请求 (CSR) openssl req -new -key private_key.pem -out csr.pem 3. 签发自签证书 openssl x509 -req -days 365 -in csr.pem -signkey private_key.pem -out certificate.crt */ 
    5 条回复    2024-09-24 23:21:17 +08:00
    tool2dx
        1
    tool2dx  
       2024 年 9 月 24 日
    "当对已经签名过的 pdf 文件在末尾添加几个随机字节"

    写点代码去掉就可以了。pdf 可以用软件遍历出文件结尾地址。后面如果是随机字节,并不会影响到签名本体,这部分内容本来就是多余的,又不会去读。
    DefoliationM
        2
    DefoliationM  
       2024 年 9 月 24 日 via Android
    试试随机改几个字节,而不是添加到末尾?
    cdx
        3
    cdx  
    OP
       2024 年 9 月 24 日 via iPhone
    @DefoliationM
    @tool2dx
    直接对文档进行修改,检验的检测结果还是能通过
    xxmaqzas
        4
    xxmaqzas  
       2024 年 9 月 24 日
    不能把签过的 md5 存数据库么,直接校验文件的 md5 值
    cdx
        5
    cdx  
    OP
       2024 年 9 月 24 日
    @xxmaqzas 这样得到了文档的人没办法检测这个文档是否已经被篡改了,还需要借助系统
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1760 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 06:25 PVG 14:25 LAX 22:25 JFK 01:25
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86