小天管理 发表于 2024年9月24日 发表于 2024年9月24日 目前有业务需要给 pdf 合同进行电子签名,目前使用的是 https://github.com/digitorus/pdfsign 这个库,但是碰到一个问题,首先是能够正常签名,但是校验时出现了一些问题。 当对已经签名过的 pdf 文件在末尾添加几个随机字节,此时使用 pdfsign 去检测,是没有办法检测到该文件已经被篡改了,在 adobe reader 中是能够检测到该文件有问题。 这里不讨论公司是否拥有这个资质的问题 有几个需要帮助的地方: 目前市面上常见的 pdf 签名方式是否与 pdfsign 类似 怎么解决这个问题 是否有其他依赖库实现 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 */
已推荐帖子