Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Tandatangani fail pdf dalam Go (Golang) menggunakan digitalorus/pdfsign

Tandatangani fail pdf dalam Go (Golang) menggunakan digitalorus/pdfsign

WBOY
WBOYke hadapan
2024-02-09 10:00:11601semak imbas

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

Menandatangani fail PDF dalam bahasa Go ialah keperluan biasa dan fungsi ini boleh dicapai dengan mudah menggunakan perpustakaan digitalorus/pdfsign. Editor PHP Youzi akan memperkenalkan anda cara menggunakan perpustakaan ini. Sama ada dalam aplikasi perniagaan atau projek peribadi, menandatangani fail PDF adalah operasi biasa. Pustaka digitalorus/pdfsign menyediakan antara muka yang ringkas dan mudah digunakan, menjadikan tandatangan fail PDF dalam bahasa Go mudah dan pantas. Melalui artikel ini, anda akan belajar cara menggunakan perpustakaan digitalorus/pdfsign dalam bahasa Go untuk melengkapkan operasi menandatangani fail PDF. Mari kita meneroka bersama-sama!

Kandungan soalan

In go (golang), saya perlu menandatangani dokumen pdf, tetapi tidak seperti bahasa lain, tidak ada perpustakaan yang memudahkan kerja. Saya menemui beberapa yang berbayar, tetapi ia bukan pilihan.

Pertama, saya mempunyai sijil PKCS (.p12) yang mana saya telah mengekstrak kunci peribadi dan sijil x509 menggunakan pakej ini: https://pkg.go.dev/software.sslmate.com/src/go -pkcs12

Tetapi apabila saya ingin menandatangani dokumen pdf, saya buntu kerana saya tidak tahu cara menghantar parameter dengan betul kepada fungsi yang melakukan operasi sedemikian. Pakej yang digunakan ialah https://pkg.go.dev/github.com/digitorus/pdfsign

Kod lengkap saya ialah:

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

Setepatnya, mereka adalah parameter Signer dan CertificateChains bagi soalan saya. Saya tidak tahu cara menggunakan privateKey dan pembolehubah chainCerts dengan betul.

Ralat mesej ialah:

  • Tidak boleh menggunakan privateKey (pembolehubah jenis antara muka{}) sebagai kripto.Nilai penandatangan dalam struct literal: antara muka{} tidak melaksanakan crypto.Signer (kaedah Awam tiada)
  • Tidak dapat menggunakan chainCertificates (pembolehubah jenis []*x509.Certificate) sebagai [][]*x509.Nilai sijil dalam struktur literal

Saya baru dalam bahasa ini, jadi saya masih tidak memahami konsep dan jenis data yang mendalam.

Terima kasih kerana memberitahu saya apa lagi yang perlu saya lakukan atau langkah yang saya ketinggalan untuk berjaya. Atau jika sesiapa tahu bagaimana saya boleh menandatangani pdf berdasarkan sijil pkcs.

Penyelesaian

Menandatangani PDF menggunakan tandatangan digital melibatkan penjanaan sepasang kunci menggunakan kriptografi kunci awam. Kunci persendirian digunakan untuk menyulitkan data yang berkaitan dengan tandatangan dan hanya penandatangan yang boleh mengaksesnya, manakala kunci awam digunakan untuk menyahsulit data tandatangan untuk pengesahan Jika ia tidak dikeluarkan oleh pihak berkuasa sijil yang dipercayai, kunci awam tersebut sijil mesti Tambahnya pada stor sijil untuk menjadikannya dipercayai. Dalam contoh yang diberikan, data tandatangan ini disimpan di dalam struktur yang dipanggil sign.SignData, yang merupakan sebahagian daripada perpustakaan pdfsign dan memerlukan sijil x509 dan penandatangan yang melaksanakan antara muka crypto.Signer.

Langkah pertama ialah menjana sepasang kunci menggunakan pakej crypto/ecdsa dalam perpustakaan standard Go. GenerateKey akan menyimpan kunci peribadi dan awam ke dalam pembolehubah privateKey. Pembolehubah privateKey yang dikembalikan ini melaksanakan antara muka crypto.Signer, yang diperlukan untuk menyelesaikan masalah yang sedang anda hadapi.

Anda boleh menyemak ini dengan membaca baris 142 kod ecdsa.go.

Anda sedang menggunakan gopkcs12.DecodeChain untuk mengembalikan kunci persendirian, tetapi ia tidak melaksanakan antara muka crypto.Signer, maka ralat itu. Anda mungkin perlu melaksanakan yang tersuai, tetapi itu soalan lain.

Konsep:

ECDSA bermaksud Algoritma Tandatangan Digital Lengkung Eliptik. Ia ialah algoritma penyulitan kunci awam yang digunakan untuk tandatangan digital. Lihat dokumentasi perpustakaan standard Go dan tapak web NIST untuk mendapatkan maklumat lanjut.

NIST P-384: P-384 ialah salah satu lengkung elips yang disyorkan oleh Institut Piawaian dan Teknologi Kebangsaan (NIST) dengan panjang kunci 384 bit. Untuk mendapatkan maklumat lanjut tentang tandatangan digital dan lebih banyak lengkung eliptik yang disyorkan, lihat tapak web NIST. Saya menggunakan P-384 sebagai penyelesaian yang berfungsi.

第二步是使用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

Atas ialah kandungan terperinci Tandatangani fail pdf dalam Go (Golang) menggunakan digitalorus/pdfsign. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:stackoverflow.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam