Search code examples
javapdfpdfboxdigital-signaturehsm

PDFBox - Define visual signature template after signing


I've been reading the official PDFBox examples to visually sign a PDF document, especifically the example from CreateVisualSignature2.java, which generates an empty document as a template to define the signature appearance and then sets it to the real document by calling SignatureOptions.setVisibleSignature().

In my case, I've been using an HSM service to do the signing for me, so I don't have direct access to private keys or certificates. I send the document hash to this service and it returns a PKCS7 byte array which I add to my document using ExternalSigningSupport.setSignature().

The code, which is based on the PDFBox example linked above, looks like this:

// Read the document and prepare a signature.

PDDocument document = PDDocument.load( "path/to/file.pdf" );

PDSignature signature = new PDSignature();
signature.setFilter( PDSignature.FILTER_ADOBE_PPKLITE );
signature.setSubFilter( PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED );
signature.setReason( "Test" );

InputStream template = createVisualSignatureTemplate( document ); // Implementation defined below.
SignatureOptions options = new SignatureOptions();
options.setVisibleSignature( template );
options.setPage( 0 );

document.addSignature( signature, options );

// Get the content to sign using PDFBox external signing support.

FileOutputStream outputStream = new FileOutputStream();
ExternalSigningSupport externalSigning = document.saveIncrementalForExternalSigning( outputStream );
byte[] content = IOUtils.toByteArray( externalSigning.getContent() );

// Send the content to the HSM service and get the response.

byte[] hash = MessageDigest.getInstance( "SHA-256" ).digest( content );
byte[] pkcs7 = MyHSMService.getSignedHash( hash );

// Add the signature to the PDF. 

externalSigning.setSignature( pkcs7 );
document.close();

And my template method, based on the method of the same name in the linked PDFBox example, simplified:

PDDocument emptyDocument = new PDDocument();
emptyDocument.addPage( new PDFPage( document.getPage(0).getMediaBox() ) );

// Create the PDAcroForm, PDSignatureField, PDAppearanceDictionary, etc,
// just like in the official example.

(...)

// Define the content stream of the visual signature.

PDPageContentStream content = new PDPageContentStream( emptyDocument, appearanceStream );

content.beginText();
content.showText( "Signed by: ... " ); // The signer name should be here.
content.newLine();
content.showText( "Date: ..." ); // The signature datetime should be here.
content.endText();
content.close();

// Convert the empty document as an input stream and return it, just like the example.

(...)

This works fine and I can add valid visual signatures without issues. My problem is that I need to add the signer name and sign date in the signature appearance, but since I create the template BEFORE calling my HSM service to sign, I don't have access to that data yet, which is why I need to define the content AFTER signing the document.

Is there a way to achieve this? I'm new to PDF signing, so my understanding of the fundamentals is pretty poor.

Thanks in advance!


Solution

  • The in-document visualization of a pdf signature is a pdf widget annotation and as such it is part of the signed content. Thus, it must be added before signing and only information can be used in it which is known before signing.

    There is one option, though, most signature types allow changing annotations in a revision after the signed revision. Read here about allowed and disallowed changes to signed pdf documents.

    But a pdf viewer then still may warn that changes have been applied to the document after signing. Such a warning would look like this:

    (Here the signature appearance has been changed to a red diagonal cross.)

    If you sign this changed document again, you can even get rid of the warning in the signature ribbon at the top:

    (Images are from this answer where changes to signature appearances using itext have been tested.)

    When it comes to the datetime, I could show the date of the PDSignature object itself, but in this case the time shown in the visualization won't be the same as the time shown in the signatures panel in the PDF viewer, since the actual signature will happen a few seconds later when I call the HSM service. In this case I have no other choice than to make a new revision just to correct the time, right?

    Well, first of all there is no need for a signing time attribute in the CMS signature container embedded in a pdf signature because the signing time can also be given in the M entry if the signature dictionary.

    Thus, one choice is to try and retrieve a signature container without signing time attribute. In that case the PDSignature signing time will be shown in the signature panel.

    If you use digital time stamps, though, to document the signing time, it is part of the CMS signature container or even a separate document time stamp, so this's option wouldn't work.

    Furthermore, you mention the signature panel. As the exact information can be found there, do you really need the signing time in the signature visualization? The visualization is only cosmetic in nature, it may contain arbitrary information, so nobody should trust it anyways. Adobe had warned about this already many years ago, even before pdf became an ISO standard...

    Thus, another choice is simply to put a nice looking logo there and that's it!