How to Implement Digital Signatures Using the CertSignController in Stirling-PDF
The CertSignController exposes a POST endpoint at /api/v1/security/cert-sign that consumes multipart certificate files and PDFs to generate cryptographically signed documents using Apache PDFBox and BouncyCastle, supporting both visible digital signature overlays and invisible certification.
The Stirling-Tools/Stirling-PDF repository provides a Spring Boot-based backend for PDF manipulation. To implement digital signatures using the CertSignController, you interact with the app/core/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java class, which orchestrates keystore creation, certificate chain validation, and PDF cryptographic signing through a standardized multipart HTTP interface.
CertSignController Endpoint Architecture
The controller exposes a single REST endpoint mapped via @AutoJobPostMapping to handle certificate-based signing operations.
Request Mapping and DTO Binding
The endpoint listens at POST /api/v1/security/cert-sign and consumes multipart/form-data. Spring automatically binds incoming requests to the SignPDFWithCertRequest DTO defined in app/core/src/main/java/stirling/software/SPDF/model/api/security/SignPDFWithCertRequest.java.
This DTO extends PDFFile and encapsulates:
- The source PDF file (
fileInput) - Certificate files (PEM key pairs, PKCS12/PFX stores, or JKS keystores)
- Passwords for encrypted private keys
- Visual signature options (page number, coordinates, logo display, signer metadata)
Certificate Type Handling
The controller supports four certificate input modes via the certType parameter:
- PEM: Separate private key and certificate files parsed using BouncyCastle helpers (
getPrivateKeyFromPEM,getCertificateFromPEM) - PKCS12/PFX: Standard
.p12or.pfxkeystore files - JKS: Java KeyStore format
- SERVER: Server-side keystore retrieved via
ServerCertificateServiceInterface
Based on the type, the controller constructs a Java KeyStore containing the private key and certificate chain (lines 93-124). The static initializer registers BouncyCastle as a security provider (Security.addProvider(new BouncyCastleProvider())) to enable PEM parsing and cryptographic operations.
PDF Signing Implementation
The actual signing logic resides in the inner class CreateSignature, which extends PDFBox's CreateSignatureBase, and the static sign helper method.
Signature Engine Initialization
When processing a request, the controller instantiates CreateSignature with the prepared KeyStore. The constructor (lines 107-125 in CertSignController.java) loads an optional logo image (signature.png) from resources for visual signatures and initializes the cryptographic parameters.
Document Signing Process
The static sign method (lines 165-199) performs the following operations:
- Loads the input PDF using
CustomPDFDocumentFactoryto create aPDDocumentinstance - Constructs a
PDSignatureobject with metadata (reason, location, signer name, date) - Determines signature visibility based on the
showSignatureflag - For visible signatures, creates
SignatureOptionsand callsinstance.createVisibleSignature - For invisible signatures, applies the cryptographic signature without visual artifacts
- Writes the signed PDF bytes to an
OutputStream
Visual Signature Generation
The createVisibleSignature method (lines 128-170) generates the visual overlay by:
- Creating a form XObject for the signature appearance on the specified page
- Rendering the optional logo via
PDImageXObject - Drawing signer information extracted from the X.509 certificate (name, date, reason)
The final signed PDF bytes are wrapped in a ResponseEntity<byte[]> via WebResponseUtils.bytesToWebResponse (lines 262-264) with the filename pattern *_signed.pdf.
Implementation Examples
cURL HTTP Request
For command-line integration or testing, submit a multipart POST request:
curl -X POST "http://localhost:8080/api/v1/security/cert-sign" \
-F "fileInput=@sample.pdf" \
-F "certType=PKCS12" \
-F "p12File=@mycert.p12" \
-F "password=secret123" \
-F "showSignature=true" \
-F "pageNumber=1" \
-F "showLogo=true" \
-F "reason=Approved" \
-F "location=New York" \
-F "name=John Doe" \
-o signed_sample.pdf
The multipart fields map directly to properties of SignPDFWithCertRequest.
Java Client Using Spring RestTemplate
For programmatic integration in Java applications:
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("fileInput", new FileSystemResource("sample.pdf"));
body.add("certType", "PEM");
body.add("privateKeyFile", new FileSystemResource("private_key.pem"));
body.add("certFile", new FileSystemResource("certificate.pem"));
body.add("password", "myPassword");
body.add("showSignature", true);
body.add("pageNumber", 1);
body.add("showLogo", true);
body.add("reason", "Contract approval");
body.add("location", "Berlin");
body.add("name", "Alice Smith");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(body, headers);
ResponseEntity<byte[]> response = restTemplate.postForEntity(
"http://localhost:8080/api/v1/security/cert-sign",
request,
byte[].class);
Files.write(Paths.get("sample_signed.pdf"), response.getBody());
Spring automatically populates the SignPDFWithCertRequest DTO from the multipart map.
Unit Testing the Controller
For internal testing or custom extensions, invoke the controller directly:
@MockBean private ServerCertificateServiceInterface serverCertificateService;
@Autowired private CertSignController certSignController;
@Autowired private CustomPDFDocumentFactory pdfFactory;
@Test
void signPdfWithPem() throws Exception {
MockMultipartFile pdf = new MockMultipartFile("fileInput", "doc.pdf",
"application/pdf", Files.readAllBytes(Paths.get("src/test/resources/doc.pdf")));
MockMultipartFile key = new MockMultipartFile("privateKeyFile", "key.pem",
"application/octet-stream", Files.readAllBytes(Paths.get("src/test/resources/key.pem")));
MockMultipartFile cert = new MockMultipartFile("certFile", "cert.pem",
"application/octet-stream", Files.readAllBytes(Paths.get("src/test/resources/cert.pem")));
SignPDFWithCertRequest req = new SignPDFWithCertRequest();
req.setFileInput(pdf);
req.setCertType("PEM");
req.setPrivateKeyFile(key);
req.setCertFile(cert);
req.setPassword(""); // PEM keys may be unencrypted
req.setShowSignature(true);
req.setPageNumber(1);
req.setShowLogo(true);
req.setReason("Test");
req.setLocation("Test Lab");
req.setName("Tester");
ResponseEntity<byte[]> resp = certSignController.signPDFWithCert(req);
assertEquals(HttpStatus.OK, resp.getStatusCode());
assertTrue(resp.getBody().length > 0);
}
This test bypasses the HTTP layer while exercising the same code path used by the REST endpoint.
Key Source Files and Dependencies
Understanding the controller requires familiarity with these components:
CertSignController.java: Main orchestrator located atapp/core/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.javathat handles request mapping, certificate validation, and response generation.SignPDFWithCertRequest.java: DTO atapp/core/src/main/java/stirling/software/SPDF/model/api/security/SignPDFWithCertRequest.javadefining the multipart request structure.CustomPDFDocumentFactory.java: Wrapper service for PDFBoxPDDocumentloading with application-specific memory and configuration handling.ServerCertificateServiceInterface.java: Service interface for retrieving server-side keystores when usingcertType=SERVER.WebResponseUtils.java: Utility class for converting byte arrays to properResponseEntityobjects with correct HTTP headers.- Apache PDFBox: Provides core PDF manipulation primitives (
PDDocument,PDSignature,SignatureOptions,CreateSignatureBase). - BouncyCastle: Enables PEM parsing and cryptographic provider services via
PEMParserandJcaPEMKeyConverter.
Summary
- The CertSignController exposes
POST /api/v1/security/cert-signfor certificate-based PDF signing in Stirling-PDF. - It supports PEM, PKCS12/PFX, JKS, and SERVER certificate types through BouncyCastle-backed keystore creation.
- The CreateSignature inner class extends PDFBox's
CreateSignatureBaseto handle cryptographic signing and optional visual overlays. - Visual signatures include configurable logos, signer names, dates, and reasons rendered via PDFBox form XObjects.
- The controller returns signed PDF bytes wrapped in a
ResponseEntitywith standardized headers viaWebResponseUtils.
Frequently Asked Questions
What certificate formats does CertSignController support?
The controller supports four certificate formats: PEM (separate key and certificate files), PKCS12/PFX (standard keystores), JKS (Java KeyStore), and SERVER (server-side keystore retrieval). PEM files are parsed using BouncyCastle helpers, while PKCS12 and JKS files are loaded directly into a Java KeyStore object. The SERVER type delegates keystore retrieval to the ServerCertificateServiceInterface implementation.
How does the controller handle visual versus invisible signatures?
The showSignature parameter in the request DTO determines visibility. When true, the controller calls createVisibleSignature to render a signature appearance containing the signer name, date, reason, and optional logo on the specified page. When false, the controller applies a cryptographic signature without visual artifacts, certifying the document without modifying its visible content.
Can I use the CertSignController for batch processing or automated workflows?
Yes, the endpoint is annotated with @AutoJobPostMapping, indicating integration with Stirling-PDF's job automation framework. The multipart interface accepts standard HTTP clients, making it suitable for batch scripts using cURL or automated Java clients using RestTemplate or WebClient. For high-volume server-side signing, configure the SERVER certificate type to avoid transferring certificate files with each request.
What dependencies are required for the cryptographic operations?
The implementation requires Apache PDFBox for PDF manipulation and BouncyCastle for cryptographic operations and PEM parsing. The controller registers BouncyCastle as a security provider during class initialization. The CustomPDFDocumentFactory provides additional application-specific PDF loading configuration, while Spring Boot handles the REST layer and multipart file binding.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →