Golang Go语言中怎么实现 pdf 的数字签名与校验
目前有业务需要给 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
}
/*
自签私钥与证书生成
- 生成私钥
openssl genpkey -algorithm RSA -out private_key.pem
- 创建证书签名请求 (CSR)
openssl req -new -key private_key.pem -out csr.pem
- 签发自签证书
openssl x509 -req -days 365 -in csr.pem -signkey private_key.pem -out certificate.crt
*/
Golang Go语言中怎么实现 pdf 的数字签名与校验
更多关于Golang Go语言中怎么实现 pdf 的数字签名与校验的实战教程也可以访问 https://www.itying.com/category-94-b0.html
"当对已经签名过的 pdf 文件在末尾添加几个随机字节"
写点代码去掉就可以了。pdf 可以用软件遍历出文件结尾地址。后面如果是随机字节,并不会影响到签名本体,这部分内容本来就是多余的,又不会去读。
更多关于Golang Go语言中怎么实现 pdf 的数字签名与校验的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
试试随机改几个字节,而不是添加到末尾?
不能把签过的 md5 存数据库么,直接校验文件的 md5 值
这样得到了文档的人没办法检测这个文档是否已经被篡改了,还需要借助系统
在Go语言中实现PDF的数字签名与校验,通常可以借助第三方库来完成,因为Go标准库并不直接支持PDF处理。一个常用的库是 pdfcpu
,它提供了较为全面的PDF处理功能,包括数字签名和校验。
-
安装pdfcpu: 首先,你需要通过
go get
命令安装pdfcpu
库:go get github.com/pdfcpu/pdfcpu/pkg/api
-
数字签名: 使用
pdfcpu
的签名功能,你需要提供私钥、证书以及PDF文件路径。签名过程大致如下:import ( "github.com/pdfcpu/pdfcpu/pkg/api" "github.com/pdfcpu/pdfcpu/pkg/pdfcpu" ) func SignPDF(pdfPath, sigFilePath, outPath string, cert []byte, privKey []byte) error { cfg := pdfcpu.NewDefaultConfiguration() return api.SignFile(pdfPath, sigFilePath, outPath, cert, privKey, nil, cfg) }
-
校验签名: 校验签名主要是验证PDF中的签名块是否有效。你可以使用
api.ValidateSignature
函数来实现。注意:数字签名涉及复杂的加密和证书管理,确保你正确加载和存储私钥和证书,并遵循相关的安全最佳实践。
-
文档和资源: 建议查阅
pdfcpu
的官方文档和示例代码,以获取更多详细信息和高级用法。
通过上述步骤,你可以在Go语言中实现PDF的数字签名与校验。