目前有业务需要给 pdf 合同进行电子签名,目前使用的是 https://github.com/digitorus/pdfsign 这个库,但是碰到一个问题,首先是能够正常签名,但是校验时出现了一些问题。
当对已经签名过的 pdf 文件在末尾添加几个随机字节,此时使用 pdfsign 去检测,是没有办法检测到该文件已经被篡改了,在 adobe reader 中是能够检测到该文件有问题。
这里不讨论公司是否拥有这个资质的问题
有几个需要帮助的地方:
代码:
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
*/
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.