在Go语言中签署PDF文件是一项常见的需求,而使用digitalorus/pdfsign库可以轻松实现这一功能。php小编柚子为您介绍该库的使用方法。无论是在业务应用中还是在个人项目中,签署PDF文件都是一个常见的操作。digitalorus/pdfsign库提供了简洁易用的接口,使得在Go语言中签署PDF文件变得简单快捷。通过本文,您将了解到如何在Go语言中使用digitalorus/pdfsign库来完成PDF文件的签署操作。让我们一起来探索吧!
在 go (golang) 中,我需要签署 pdf 文档,但与其他语言不同的是,没有任何库可以使工作变得更容易。我找到了几个付费的,但它们不是一个选择。
首先,我有一个 PKCS 证书 (.p12),我已经使用此包从中提取私钥和 x509 证书:https://pkg.go.dev/software.sslmate.com/src/go -pkcs12
但是当我想签署 pdf 文档时,我陷入了困境,因为我不知道如何正确地将参数传递给执行此类操作的函数。使用的包是https://pkg.go.dev/github.com/digitorus/pdfsign
我的完整代码是:
package main import ( "crypto" "fmt" "os" "time" "github.com/digitorus/pdf" "github.com/digitorus/pdfsign/revocation" "github.com/digitorus/pdfsign/sign" gopkcs12 "software.sslmate.com/src/go-pkcs12" ) func main() { certBytes, err := os.ReadFile("certificate.p12") if err != nil { fmt.Println(err) return } privateKey, certificate, chainCerts, err := gopkcs12.DecodeChain(certBytes, "MyPassword") if err != nil { fmt.Println(err) return } input_file, err := os.Open("input-file.pdf") if err != nil { fmt.Println(err) return } defer input_file.Close() output_file, err := os.Create("output-file.pdf") if err != nil { fmt.Println(err) return } defer output_file.Close() finfo, err := input_file.Stat() if err != nil { fmt.Println(err) return } size := finfo.Size() rdr, err := pdf.NewReader(input_file, size) if err != nil { fmt.Println(err) return } err = sign.Sign(input_file, output_file, rdr, size, sign.SignData{ Signature: sign.SignDataSignature{ Info: sign.SignDataSignatureInfo{ Name: "John Doe", Location: "Somewhere on the globe", Reason: "My season for siging this document", ContactInfo: "How you like", Date: time.Now().Local(), }, CertType: sign.CertificationSignature, DocMDPPerm: sign.AllowFillingExistingFormFieldsAndSignaturesPerms, }, Signer: privateKey, // crypto.Signer DigestAlgorithm: crypto.SHA256, // hash algorithm for the digest creation Certificate: certificate, // x509.Certificate CertificateChains: chainCerts, // 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 { fmt.Println(err) } else { fmt.Println("Signed PDF written to output.pdf") } }
确切地说,它们是我的问题的Signer和CertificateChains参数。我不知道如何正确使用 privateKey 和 chainCerts 变量。
消息错误是:
我是这门语言的新手,所以我仍然不了解深入的概念和数据类型。
感谢您告诉我还应该做什么或缺少哪些步骤才能取得成功。或者如果有人知道我如何根据 pkcs 证书签署 pdf。
使用数字签名对 PDF 进行签名包括使用公钥加密技术生成一对密钥。私钥用于加密与签名相关的数据,只有签名者才能访问它,而公钥则用于解密签名数据以进行验证,如果不是由受信任的证书颁发机构颁发,则所述公钥证书必须将其添加到证书存储中以使其受到信任。 在给定的示例中,此签名数据存储在名为 sign.SignData 的结构内,该结构是 pdfsign 库的一部分,需要 x509 证书和实现 crypto.Signer 接口的签名者。
第一步是使用 Go 标准库中的 crypto/ecdsa 包生成一对密钥。 GenerateKey 将把私钥和公钥保存到 privateKey 变量中。这个返回的 privateKey 变量实现了 crypto.Signer 接口,这是解决您当前面临的问题所必需的。
您可以通过阅读 ecdsa.go 代码第 142 行来检查这一点。
您当前正在使用 gopkcs12.DecodeChain 返回私钥,但它没有实现 crypto.Signer 接口,因此会出现错误。您可能需要实现一个自定义的,但这是另一个问题。
概念:
ECDSA 代表椭圆曲线数字签名算法。它是一种用于数字签名的公钥加密算法。请参阅 Go 标准库文档和 NIST 网站了解更多信息。
NIST P-384:P-384 是美国国家标准与技术研究院 (NIST) 推荐的椭圆曲线之一,密钥长度为 384 位。有关数字签名和更多推荐的椭圆曲线的更多信息,请参阅 NIST 网站。我使用 P-384 作为工作解决方案。
第二步是使用Go标准库中的crypto/x509包通过其链生成器生成x509证书和证书链。这些是您在问题中寻求帮助的特定变量,但不属于您在错误消息中可以清楚看到的预期类型。只需遵循 lib 指令并使用 x509.Certificate.Verify() 就像我在工作解决方案中所做的那样,这将返回正确的类型 [][]*x509.Certificate。
请参阅 Go 标准库文档以获取更多信息。
第三步是打开输入 pdf 文件并使用 Go 标准库中的 os 包创建输出 pdf 文件。
第四步实际上是使用 digitalorus/pdfsign 库对 pdf 文件进行签名。
这是我今天编码和测试的一个有效解决方案,旨在让您回到正轨,进行一些研究并根据您的需求进行修改:
package main import ( "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/x509" "crypto/x509/pkix" "fmt" "github.com/digitorus/pdf" "github.com/digitorus/pdfsign/revocation" "github.com/digitorus/pdfsign/sign" "log" "math/big" "os" "time" ) func main() { // First step privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) if err != nil { panic(err) } // Second step x509RootCertificate := &x509.Certificate{ SerialNumber: big.NewInt(2023), Subject: pkix.Name{ Organization: []string{"Your Organization"}, Country: []string{"Your Country"}, Province: []string{"Your Province"}, Locality: []string{"Your Locality"}, StreetAddress: []string{"Your Street Address"}, PostalCode: []string{"Your Postal Code"}, }, NotBefore: time.Now(), NotAfter: time.Now().AddDate(10, 0, 0), IsCA: true, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, BasicConstraintsValid: true, } rootCertificateBytes, err := x509.CreateCertificate(rand.Reader, x509RootCertificate, x509RootCertificate, &privateKey.PublicKey, privateKey) if err != nil { panic(err) } rootCertificate, err := x509.ParseCertificate(rootCertificateBytes) if err != nil { panic(err) } roots := x509.NewCertPool() roots.AddCert(rootCertificate) certificateChain, err := rootCertificate.Verify(x509.VerifyOptions{ Roots: roots, }) if err != nil { panic(err) } // Third step outputFile, err := os.Create("output.pdf") if err != nil { panic(err) } defer func(outputFile *os.File) { err = outputFile.Close() if err != nil { log.Println(err) } fmt.Println("output file closed") }(outputFile) inputFile, err := os.Open("input.pdf") if err != nil { panic(err) } defer func(inputFile *os.File) { err = inputFile.Close() if err != nil { log.Println(err) } fmt.Println("input file closed") }(inputFile) fileInfo, err := inputFile.Stat() if err != nil { panic(err) } size := fileInfo.Size() pdfReader, err := pdf.NewReader(inputFile, size) if err != nil { panic(err) } // Fourth step err = sign.Sign(inputFile, outputFile, pdfReader, size, sign.SignData{ Signature: sign.SignDataSignature{ Info: sign.SignDataSignatureInfo{ Name: "Your name", Location: "Your location", Reason: "Your reason", ContactInfo: "Your contact info", Date: time.Now().Local(), }, CertType: sign.CertificationSignature, DocMDPPerm: sign.AllowFillingExistingFormFieldsAndSignaturesPerms, }, Signer: privateKey, // crypto.Signer DigestAlgorithm: crypto.SHA256, // hash algorithm for the digest creation Certificate: rootCertificate, // x509.Certificate CertificateChains: certificateChain, // x509.Certificate.Verify() TSA: sign.TSA{ URL: "", 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 { log.Println(err) } else { log.Println("pdf signed") } }
结果:
go run main.go 2023/12/01 21:53:37 pdf signed input file closed output file closed
以上是使用 digitalorus/pdfsign 在 Go (Golang) 中签署 pdf 文件的详细内容。更多信息请关注PHP中文网其他相关文章!