Thursday, December 13, 2007

Getting Started With iText - How to Verify a PDF

I am pleased to say I finally finished translating the iText example How to Verify a PDF. It makes a nice follow up to the previous entry How to Sign a PDF with iText. If you do not have a certificate, you can experiment with self-signing How to Self-Sign a PDF with iText.

I have only a passing familiarity with certificates and keys, from many moons ago. So the bulk of my time was spent reading about keys, certificates and the java keytool command.

But one new concept was introduced in this example: byte arrays. The verification process extracts all revisions of the PDF. Byte arrays are used to read and write the revised versions of the PDF. The code to create byte arrays was courtesy of an old but helpful tip from Christian Cantrell's blog.

If you have not read the previous entries, be sure to read the requirements before you get started.

As always, comments/suggestions/corrections are always welcome. Enjoy!

The attached code sample was updated to use an instance of the javaLoader stored in the server scope to avoid memory leaks caused by a bug in ColdFusion. To find out more about this issue read Mark Mandel's article Using a Java URLClassLoader in CFMX Can Cause a Memory Leak.

<h1>How to verify a PDF Example</h1>

fullPathToSignedFile = ExpandPath("./HelloWorldSigned.pdf");

cfSearching: This example was updated to use an instance of
the javaLoader that is stored in the server scope. This prevents
memory leaks due to a bug in ColdFusion.

"Using a Java URLClassLoader in CFMX Can Cause a Memory Leak"
javaLoader = server[YourUniqueKeyForTheJavaLoader];

// load class for PKCS##7 signature handling
PdfPKCS7 = javaloader.create("com.lowagie.text.pdf.PdfPKCS7");
// Loads the default root certificates at <java.home>/lib/security/cacerts
keyStore = PdfPKCS7.loadCacertsKeyStore();

// create a reader for the document
pdfReader = javaLoader.create("com.lowagie.text.pdf.PdfReader").init( fullPathToSignedFile );
// get a read-only copy of the fields in the document
acroFields = pdfReader.getAcroFields();

// create an output stream used later to extract revisions
outStream = createObject("java","");

// get an array of signature names
signatureNames = acroFields.getSignatureNames();
for (k = 1; k LTE arrayLen(signatureNames); k = k + 1) {
// get current signature
name = signatureNames[k];

// display signature+revision information
WriteOutput("Signature name: "& name &"<br>");
WriteOutput("Signature covers whole document: "& acroFields.signatureCoversWholeDocument(name) &"<br>");
WriteOutput("Document revision: "& acroFields.getRevision(name) &" of "& acroFields.getTotalRevisions() &"<br>");

// Start revision extraction
out = outStream.init("revision_"& acroFields.getRevision(name) & ".pdf");

// create a byte array for extracting revision
byteClass = createObject("java", "java.lang.Byte").TYPE;
byteArray = createObject("java","java.lang.reflect.Array").newInstance(byteClass, javacast("int", 8192));

inputStream = acroFields.extractRevision(name);

offset = javacast("int", 0);
// read up to 8192 bytes into the array
length =;
// if there is any data to read
while ( length GT 0) {
// write the bytes to the output file
out.write(byteArray, offset, length);
// read up to the next 8192 bytes into the array
length =;
// close the file streams
// End revision extraction

pk = acroFields.verifySignature(name);
calendar = pk.getSignDate();
certificateArray = pk.getCertificates();
WriteOutput("Subject: "& PdfPKCS7.getSubjectFields(pk.getSigningCertificate()) &"<br>");
WriteOutput("Document modified: "& (NOT pk.verify()) &"<br>");

// verify the array of certificates
failureArray = PdfPKCS7.verifyCertificates( certificateArray,
javacast("null", ""),

// note, java null values are not defined in CF
if ( NOT IsDefined("failureArray")) {
WriteOutput("<div style='color: green;'>Certificates verified against the KeyStore" &"<br></div>");
else {
WriteOutput("<div style='color: red;'>Certificate failed[1]: "& failureArray[1] &"<br></div>");
WriteOutput("Certificate failed[0]: <pre>"& failureArray[0] &"</pre><br>");



  © Blogger templates The Professional Template by 2008

Header image adapted from atomicjeep