Golang Go语言中怎么实现 pdf 的数字签名与校验

目前有业务需要给 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 */


Golang Go语言中怎么实现 pdf 的数字签名与校验

更多关于Golang Go语言中怎么实现 pdf 的数字签名与校验的实战教程也可以访问 https://www.itying.com/category-94-b0.html

6 回复

"当对已经签名过的 pdf 文件在末尾添加几个随机字节"

写点代码去掉就可以了。pdf 可以用软件遍历出文件结尾地址。后面如果是随机字节,并不会影响到签名本体,这部分内容本来就是多余的,又不会去读。

更多关于Golang Go语言中怎么实现 pdf 的数字签名与校验的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


试试随机改几个字节,而不是添加到末尾?



直接对文档进行修改,检验的检测结果还是能通过

不能把签过的 md5 存数据库么,直接校验文件的 md5 值

这样得到了文档的人没办法检测这个文档是否已经被篡改了,还需要借助系统

在Go语言中实现PDF的数字签名与校验,通常可以借助第三方库来完成,因为Go标准库并不直接支持PDF处理。一个常用的库是 pdfcpu,它提供了较为全面的PDF处理功能,包括数字签名和校验。

  1. 安装pdfcpu: 首先,你需要通过 go get 命令安装 pdfcpu 库:

    go get github.com/pdfcpu/pdfcpu/pkg/api
    
  2. 数字签名: 使用 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)
    }
    
  3. 校验签名: 校验签名主要是验证PDF中的签名块是否有效。你可以使用 api.ValidateSignature 函数来实现。

    注意:数字签名涉及复杂的加密和证书管理,确保你正确加载和存储私钥和证书,并遵循相关的安全最佳实践。

  4. 文档和资源: 建议查阅 pdfcpu 的官方文档和示例代码,以获取更多详细信息和高级用法。

通过上述步骤,你可以在Go语言中实现PDF的数字签名与校验。

回到顶部