Documentation page: PD4ML PDF Signing. This page is also available as Markdown at pd4ml-pdf-signing.md.

PD4ML PDF Signing

PD4ML PDF Signing adds digital signatures to existing PDF files, from Java code or from the command line. The signature is appended as an incremental update: every byte of the original file stays unchanged, and the signature field, the CMS signature and an optional visible appearance are written after it. The private key can come from a PKCS#12 keystore, a PKCS#11 hardware token or HSM, or a cloud KMS.

Available from PD4ML v4.1.1, in the com.pd4ml.pdf.sign package. It uses BouncyCastle (bcprov/bcpkix) for the cryptography, so the BouncyCastle jars must be on the classpath.

What you can do with it

  • Sign a PDF with an invisible signature, or with a visible signature widget showing a stamp image (PNG/JPEG), caption text, or both, at a given page and rectangle.
  • Use the key where it lives: a PKCS#12 (.p12/.pfx) file, a PKCS#11 token or HSM (the key never leaves the token), or a cloud KMS through the one-method RemoteSigner interface (API only, not in the command-line tool).
  • Certify a document (DocMDP) at one of three levels: no changes allowed, form filling allowed, or form filling and annotations allowed. Only the first signature on a document can certify it.
  • Co-sign: add further signatures to an already signed, uncertified document, each in its own signature field.
  • Add an RFC 3161 timestamp from a TSA, with optional HTTP Basic Auth.
  • Add long-term validation (PAdES-LT): embed the certificate chain and OCSP responses and/or CRLs in a /DSS dictionary, right after signing or later, on a PDF signed by this tool or by any other PAdES-compliant signer.
  • Choose the digest: SHA-256 (default), SHA-384 or SHA-512; RSA or ECDSA follows from the certificate's key type.
  • List the signature fields of a PDF with their status, signer data and certification level.

Quick start

Sign a PDF with a PKCS#12 key, a visible signature on the first page and a timestamp:

import com.pd4ml.pdf.sign.*;
import java.io.File;

CertificateUtils.SigningIdentity identity =
        CertificateUtils.loadFromPkcs12(new File("signer.p12"), "changeit".toCharArray());

SigningOptions options = new SigningOptions()
        .setReason("Approved")
        .setSignerName("Jane Doe")
        .setTsaUrl("http://timestamp.digicert.com")
        .setVisibleSignatureOptions(new VisibleSignatureOptions()
                .setPage(0)                       // zero-based
                .setRectangle(36, 36, 220, 70)    // llx, lly, width, height in points
                .setVisibleText("Jane Doe"));

new PdfSigner().sign(new File("in.pdf"), new File("out.pdf"), identity, options);

Every failure is reported as a single checked PdfSigningException.

The same from the command line, with long-term validation added in the same call (on Windows, separate the classpath entries with ;):

java -cp "pd4ml.jar:bouncycastle-runtime-1.84.jar" com.pd4ml.pdf.sign.cli.PdfSignCli sign in.pdf out.pdf \
     --p12=signer.p12 --password=changeit --reason=Approved --signer="Jane Doe" \
     --tsa=http://timestamp.digicert.com --visible --page=0 --rect=36,36,220,70 --text="Jane Doe" \
     --ltv

When to use it

Use PD4ML PDF Signing when a finished PDF needs a digital signature, a certification or long-term validation data. The other PDF tools prepare documents; signing is normally the last step.

Tool Use it to
PD4ML PDF COS read, inspect, query and modify PDF objects; the foundation the other tools are built on
PD4ML PDF Merge combine page ranges from several PDFs into one
PD4ML PDF Optimizer make existing PDFs smaller
PD4ML PDF XFDF import and export form field values and annotations as XFDF

A signature covers the exact bytes of the file as it was signed. Merge and the optimizer write a new file rather than an incremental update on top of the old one, so merge and optimize first, then sign. An encrypted input PDF is refused: the signer can read encrypted files but cannot write back into them. LTV data is added as a further incremental update and does not touch the signature.

In this section