Heim  >  Artikel  >  Backend-Entwicklung  >  Signieren Sie PDF-Dateien in Go (Golang) mit digitalorus/pdfsign

Signieren Sie PDF-Dateien in Go (Golang) mit digitalorus/pdfsign

WBOY
WBOYnach vorne
2024-02-09 10:00:11597Durchsuche

使用 digitalorus/pdfsign 在 Go (Golang) 中签署 pdf 文件

Das Signieren von PDF-Dateien in der Go-Sprache ist ein allgemeiner Bedarf, und diese Funktion kann mithilfe der Bibliothek digitalorus/pdfsign problemlos erreicht werden. Der PHP-Editor Youzi führt Sie in die Verwendung dieser Bibliothek ein. Ob bei Geschäftsanwendungen oder persönlichen Projekten: Das Signieren von PDF-Dateien ist ein häufiger Vorgang. Die digitalorus/pdfsign-Bibliothek bietet eine einfache und benutzerfreundliche Oberfläche, die das Signieren von PDF-Dateien in der Go-Sprache einfach und schnell macht. In diesem Artikel erfahren Sie, wie Sie die Bibliothek digitalorus/pdfsign in der Go-Sprache verwenden, um den Signierungsvorgang von PDF-Dateien abzuschließen. Lasst uns gemeinsam erkunden!

Frageninhalt

In Go (Golang) muss ich ein PDF-Dokument signieren, aber im Gegensatz zu anderen Sprachen gibt es keine Bibliothek, die die Arbeit erleichtert. Ich habe ein paar kostenpflichtige gefunden, aber sie waren keine Option.

Zuerst habe ich ein PKCS-Zertifikat (.p12), aus dem ich den privaten Schlüssel und das x509-Zertifikat mit diesem Paket extrahiert habe: https://pkg.go.dev/software.sslmate.com/src/go -pkcs12

Aber wenn ich ein PDF-Dokument signieren möchte, stecke ich fest, weil ich nicht weiß, wie ich Parameter richtig an eine Funktion übergebe, die einen solchen Vorgang ausführt. Das verwendete Paket ist https://pkg.go.dev/github.com/digitorus/pdfsign

Mein vollständiger Code lautet:

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")
    }
}

Um genau zu sein, handelt es sich um die Parameter Signer und CertificateChains meiner Frage. Ich weiß nicht, wie man die Variablen privateKey und chainCerts richtig verwendet.

Der Meldungsfehler ist:

  • PrivateKey (Variable vom Typ interface{}) kann nicht als crypto.Signer-Wert in einem Strukturliteral verwendet werden: interface{} implementiert crypto.Signer nicht (fehlende öffentliche Methode)
  • ChainCertificates (Variable vom Typ []*x509.Certificate) kann nicht als [][]*x509.Certificate-Wert in einem Strukturliteral verwendet werden

Ich bin neu in dieser Sprache und verstehe daher die detaillierten Konzepte und Datentypen immer noch nicht.

Danke, dass du mir gesagt hast, was ich sonst noch tun sollte oder welche Schritte mir fehlen, um erfolgreich zu sein. Oder ob jemand weiß, wie ich ein PDF basierend auf einem PKCS-Zertifikat signieren kann.

Problemumgehung

Das Signieren einer PDF-Datei mit einer digitalen Signatur erfordert die Generierung eines Schlüsselpaars mithilfe der Public-Key-Kryptographie. Der private Schlüssel wird zum Verschlüsseln der mit der Signatur verbundenen Daten verwendet und nur der Unterzeichner kann darauf zugreifen, während der öffentliche Schlüssel zum Entschlüsseln der Signaturdaten zur Überprüfung verwendet wird. Wenn er nicht von einer vertrauenswürdigen Zertifizierungsstelle ausgestellt wird, wird dieser öffentliche Schlüssel verwendet Das Zertifikat muss zum Zertifikatspeicher hinzugefügt werden, um es vertrauenswürdig zu machen. Im gegebenen Beispiel werden diese Signaturdaten in einer Struktur namens sign.SignData gespeichert, die Teil der pdfsign-Bibliothek ist und ein x509-Zertifikat und einen Unterzeichner erfordert, der die crypto.Signer-Schnittstelle implementiert.

Der erste Schritt besteht darin, mithilfe des crypto/ecdsa-Pakets in der Go-Standardbibliothek ein Schlüsselpaar zu generieren. GenerateKey speichert den privaten und öffentlichen Schlüssel in der Variablen privateKey. Diese zurückgegebene privateKey-Variable implementiert die crypto.Signer-Schnittstelle, die zur Lösung des Problems erforderlich ist, mit dem Sie derzeit konfrontiert sind.

Sie können dies überprüfen, indem Sie Zeile 142 des ecdsa.go-Codes lesen.

Sie verwenden derzeit gopkcs12.DecodeChain, um den privaten Schlüssel zurückzugeben, aber es implementiert nicht die crypto.Signer-Schnittstelle, daher der Fehler. Möglicherweise müssen Sie eine benutzerdefinierte Version implementieren, aber das ist eine andere Frage.

Konzept:

ECDSA steht für Elliptic Curve Digital Signature Algorithm. Es handelt sich um einen Verschlüsselungsalgorithmus mit öffentlichem Schlüssel, der für digitale Signaturen verwendet wird. Weitere Informationen finden Sie in der Dokumentation der Go-Standardbibliothek und auf der NIST-Website.

NIST P-384: P-384 ist eine der vom National Institute of Standards and Technology (NIST) empfohlenen elliptischen Kurven mit einer Schlüssellänge von 384 Bit. Weitere Informationen zu digitalen Signaturen und weitere empfohlene elliptische Kurven finden Sie auf der NIST-Website. Als Arbeitslösung verwende ich den 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

Das obige ist der detaillierte Inhalt vonSignieren Sie PDF-Dateien in Go (Golang) mit digitalorus/pdfsign. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:stackoverflow.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen