在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中文網其他相關文章!