digitorus / pdfsign

Add/verify Advanced Electronic Signature (AES) and Qualified Electronic Signature (QES) in PDF (usign pure Go)
BSD 2-Clause "Simplified" License
72 stars 16 forks source link

output empty pdf, any ideas? #1

Closed Legislaturasj closed 1 year ago

Legislaturasj commented 1 year ago

Hi im trying to sign a pdf file from a certificate x509.Certificate that i get from a token or smartcard, the certificates storage fine but the output pdf is empty.... here is my code

import (
    "crypto/x509"

    "log"

    "os"
    "time"

    "github.com/digitorus/pdf"
    "github.com/digitorus/pdfsign/revocation"
    "github.com/digitorus/pdfsign/sign"
    "github.com/miekg/pkcs11"
)

func main() {
    // PKCS11 Configuration
    libPath := "C:\\Windows\\System32\\eps2003csp11.dll" // Library path of the token driver
    label := "Macroseguridad.org"                        // Label of the token
    pin := "password"                                   // PIN of the token

    // Open the PKCS11 library
    p := pkcs11.New(libPath)
    if p == nil {
        log.Fatalf("Failed to load PKCS11 library: %s", libPath)
    }
    defer p.Destroy()

    // Initialize the library
    err := p.Initialize()
    if err != nil {
        log.Fatalf("Failed to initialize PKCS11 library: %v", err)
    }
    defer p.Finalize()

    // Get the slot list
    slots, err := p.GetSlotList(true)
    if err != nil {
        log.Fatalf("Failed to get slots: %v", err)
    }

    // Find the slot for the token
    var slot uint
    for _, s := range slots {
        info, err := p.GetTokenInfo(s)
        if err != nil {
            log.Fatalf("Failed to get token info: %v", err)
        }
        if info.Label == label {
            slot = s
            break
        }
    }
    if slot == 0 {
        log.Fatalf("Failed to find slot for token: %s", label)
    }

    // Open a session to the slot
    session, err := p.OpenSession(slot, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION)
    if err != nil {
        log.Fatalf("Failed to open session: %v", err)
    }
    defer p.CloseSession(session)

    // Login to the token
    err = p.Login(session, pkcs11.CKU_USER, pin)
    if err != nil {
        log.Fatalf("Failed to login: %v", err)
    }
    defer p.Logout(session)

    // Configurar la plantilla para encontrar la clave privada
    privateKeyTemplate := []*pkcs11.Attribute{
        pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY),
        pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, pkcs11.CKK_RSA),
        pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
        pkcs11.NewAttribute(pkcs11.CKA_SIGN, true),
    }

    err = p.FindObjectsInit(session, privateKeyTemplate)
    if err != nil {
        log.Fatalf("Error al inicializar la búsqueda de la clave privada: %s", err)
    }
    defer p.FindObjectsFinal(session)

    obj, _, err := p.FindObjects(session, 1)
    if err != nil {
        log.Fatalf("Error al buscar la clave privada: %s", err)
    }
    if len(obj) == 0 {
        log.Fatal("No se encontró la clave privada")
    }

    privateKeyHandle := obj[0]
    println(privateKeyHandle)

    /****/

    // Get the slot list
    slots, err = p.GetSlotList(true)
    if err != nil {
        log.Fatalf("Failed to get slots: %v", err)
    }

    // Find the slot for the token
    var slot2 uint
    for _, s := range slots {
        info, err := p.GetTokenInfo(s)
        if err != nil {
            log.Fatalf("Failed to get token info: %v", err)
        }
        if info.Label == label {
            slot2 = s
            break
        }
    }
    if slot2 == 0 {
        log.Fatalf("Failed to find slot for token: %s", label)
    }

    // Open a session to the slot
    session, err = p.OpenSession(slot2, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION)
    if err != nil {
        log.Fatalf("Failed to open session: %v", err)
    }
    defer p.CloseSession(session)

    // Login to the token

    /*****/
    println(pkcs11.CKO_CERTIFICATE)

    var searchTemplate = []*pkcs11.Attribute{
        pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_CERTIFICATE),
    }
    var attrTemplate = []*pkcs11.Attribute{
        pkcs11.NewAttribute(pkcs11.CKA_ID, nil),
        pkcs11.NewAttribute(pkcs11.CKA_LABEL, nil),
        pkcs11.NewAttribute(pkcs11.CKA_VALUE, nil),
    }
    err = p.FindObjectsInit(session, searchTemplate)
    if err != nil {
        log.Fatal("FindObjectsInit: %v", err)
    }
    hObjects, _, err := p.FindObjects(session, 1024)
    if err != nil {
        log.Fatal("FindObjectsInit: %v", err)
    }
    var cert *x509.Certificate
    //var sffg *rsa.PrivateKey
    for _, hObject := range hObjects {
        attrs, err := p.GetAttributeValue(session, hObject, attrTemplate)
        if err != nil {
            continue
        }

        cert, err = x509.ParseCertificate(attrs[2].Value)

        if err != nil {
            continue
        }

        if cert.IsCA {
            continue
        }

    }

    //println(cert.DNSNames)
    _ = p.FindObjectsFinal(session)

    // Inicializar fichero de salida
    outFile, err := os.Create("output5.pdf")
    if err != nil {
        panic(err)
    }
    defer outFile.Close()
    signData := sign.SignData{}
    roots := x509.NewCertPool()
    roots.AddCert(cert)
    opts := x509.VerifyOptions{
        Roots:     roots,
        KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
    }
    ver, err := cert.Verify(opts)

    println(ver)
    if err != nil {
        panic(err)
    }

    signData = 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,
        },

        Certificate:       cert, // x509.Certificate
        CertificateChains: ver,  // 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,
    }
    /*signData.CertificateChains = ver
    signData.Signature.Info.Name = "victoria"
    signData.Certificate = cert

    signData.Signature.Info.Reason = "Firma"
    signData.Signature.Info.Date = time.Now().Local()

    // The follow options are likely to change in a future release
    //
    // cache revocation data when bulk signing
    signData.Signature.CertType = sign.CertificationSignature */
    input_file, err := os.Open("outconf.pdf")

    if err != nil {
        log.Fatal("Error initializing object search: %v", err)
    }
    defer input_file.Close()

    output_file, err := os.Create("output45.pdf")
    if err != nil {
        log.Fatal("Error initializing object search: %v", err)
    }
    defer output_file.Close()

    finfo, err := input_file.Stat()
    if err != nil {
        log.Fatal("Error initializing object search: %v", err)
    }
    size := finfo.Size()
    println(size)
    rdr, err := pdf.NewReader(input_file, size)
    if err != nil {
        log.Fatal("Error initializing object search: %v", err)
    }
    err = sign.Sign(input_file, output_file, rdr, size, signData)

}
vanbroup commented 1 year ago

Does the signing work if you sign with a key in software? Does the CLI example work? Can you make sure you check all the errors (you are skipping a few).

You can also have a look at: https://github.com/digitorus/pdfsigner/

Which provides you with PKCS#11 options/examples out of the box.

Legislaturasj commented 1 year ago

sorry my english is not so good, im new in golang.. this lib is free? or i new to have a license?

vanbroup commented 1 year ago

Please check the LICENSE file of the repository.

The pdfsigner project is under a dual licence and is for free (GNU licence) for non-commercial use. For commercial use a license is required.