Digital Signature
Sign a PDF with an X.509 certificate (RSA 2048 + SHA-256). The signature is stored inside the PDF - Adobe Acrobat Reader shows it in the Signatures panel - and any change to the file afterwards breaks it. You can also add a hand-drawn signature to the visible stamp.
Signing certificate
| Subject | C=TH,O=Northwind Print Co. Ltd.,CN=PrintDoc Lab Signing Authority |
| Valid | 25 Oct 2026 - 23 Oct 2036 |
| Key | RSA SHA256withRSA |
| SHA-256 | A8:8A:5F:46:BF:7A:16:56:01:1D:40:25:74:D8:C8:04:F2:9B:67:30:A1:35:A4:B1:45:1D:1A:C7:C9:1A:C9:D9 |
Self-signed and created on the first start (data/signing/lab-signer.p12).
Readers show it as "validity unknown" until you trust it - in real life a certificate authority signs it.
Signed documents
table signed_document| When | Document | Signer | Reason | SHA-256 (signed file) | Size |
|---|
Nothing signed yet.
How it works
- Key + certificate: Java
KeyPairGenerator(RSA 2048) and Bouncy CastleJcaX509v3CertificateBuilder, saved in a PKCS#12 keystore. - Visible stamp: OpenPDF
PdfStamper.getOverContent(lastPage)draws the box and the drawn signature PNG. - Signature: PDFBox reserves space in the file and hands the bytes around it to our callback; Bouncy Castle creates a
detached CMS (PKCS#7) signature over them;
saveIncrementalwrites it in. - Verify: take the signed byte range, check the CMS signature with the certificate inside it, and check the byte range reaches the end of the file (nothing was appended later).
PDSignature sig = new PDSignature();
sig.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
sig.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
sig.setName("Somchai Prasert"); sig.setReason("I approve this document");
doc.addSignature(sig, content -> {
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(privateKey);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(digestProvider).build(signer, certificate));
gen.addCertificates(new JcaCertStore(List.of(certificate)));
return gen.generate(new CMSProcessableByteArray(content.readAllBytes()), false).getEncoded();
});
doc.saveIncremental(out);
Files: service/SignatureService.java, web/ShareSecureController.java.