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
now := time.Now()
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return err
}
template := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: "any", Organization: []string{"Test Company"}},
NotBefore: now.Add(-time.Hour).UTC(),
NotAfter: now.Add(time.Hour * 24 * 365).UTC(),
PublicKeyAlgorithm: x509.RSA,
KeyUsage: x509.KeyUsageKeyEncipherment |
x509.KeyUsageDigitalSignature |
x509.KeyUsageDataEncipherment,
}
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(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. Validated with trust roots, it is
refused unless that exact certificate is one of the roots, while a certificate issued
by a CA you trust passes. See
Validating Signer Trust and Revocation. 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. ECDSA in these
signatures is defined by PDF 2.0, which UniPDF does not enforce; see
Sign with an ECDSA Key.
Run the example
generateKeys builds the pair; main then signs page 1 with a visible appearance
placed at the bottom left. now is a package-level time.Now(), shared by the
certificate validity, the signature date and the date line in the appearance.
git clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/signatures/signing
go run pdf_sign_generate_keys.go <INPUT_PDF_PATH> <OUTPUT_PDF_PATH>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
