Skip to content
Sign with Generated Private & Public Key Pair

Sign with Generated Private & Public Key Pair

Signing needs a private key and a certificate, and for trying the API out you can make both with the standard library instead of obtaining a real certificate. The resulting signature is cryptographically valid and verifies against its own certificate, so it exercises the whole path from PdfAppender through the signature handler. What it cannot do is chain to a trusted root, which is why this belongs in tests and not in production output.

Generating the pair

priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
    return err
}

template := x509.Certificate{
    SerialNumber: new(big.Int),
    Subject:      pkix.Name{CommonName: "any", Organization: []string{"Test Company"}},
    NotBefore:    time.Now().Add(-time.Hour).UTC(),
    NotAfter:     time.Now().Add(time.Hour * 24 * 365).UTC(),
    KeyUsage:     x509.KeyUsageDigitalSignature,
}

certData, err := x509.CreateCertificate(rand.Reader, &template, &template, priv.Public(), priv)
if err != nil {
    return err
}

cert, err := x509.ParseCertificate(certData)

Passing &template as both the certificate and the parent is what makes it self-signed. x509.CreateCertificate returns DER bytes, and UniPDF wants an *x509.Certificate, so the DER has to go back through x509.ParseCertificate.

NotBefore in the past by an hour absorbs clock skew between machines. Set it to time.Now() exactly and a verifier whose clock runs a few seconds behind will call the certificate not yet valid.

Signing with it

The pair goes straight into the PKCS#7 detached handler, and the rest is the standard sequence:

handler, err := sighandler.NewAdobePKCS7Detached(priv, cert)
if err != nil {
    return err
}

signature := model.NewPdfSignature(handler)
signature.SetName("Test Self Signed PDF")
signature.SetReason("TestSelfSignedPDF")
signature.SetDate(time.Now(), "")

if err := signature.Initialize(); err != nil {
    return err
}

Initialize has to run before the appender writes: it fills in /Filter and /SubFilter and reserves the space that the real signature bytes are pasted into later. It is also where a handler built without a key fails, with privateKey must not be nil.

Limitations

A self-signed certificate has no issuer to check, so validation reports the signature as signed and verified while IsTrusted stays false. That is not specific to generated keys, since UniPDF’s handlers never set IsTrusted, but with a real certificate a PDF reader would resolve the chain and show a green check. Adobe Acrobat will show this signature as valid but of unknown identity.

Nothing preserves the key. It exists for the lifetime of the process, so the output document cannot be re-signed with the same identity, and each run produces a different signer. For anything repeatable, use a PKCS#12 file or PEM files.

NewAdobePKCS7Detached takes *rsa.PrivateKey. An ECDSA key generated with ecdsa.GenerateKey needs sighandler.NewAdobePKCS7DetachedEcdsa, and that requires the document to be PDF 2.0 or later.

Run the example

generateKeys builds the pair; main then signs page 1 with a visible appearance placed at the bottom left.

git clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/signatures
go run pdf_sign_generate_keys.go <IN.pdf> <OUT.pdf>

If this is your first time using UniPDF, follow the getting started guide to create an API key and set up your development environment.

View the full source

Sample output

Signed with generated keys

Last updated on