PAdES and PDF Java Toolkit Part 4 : PAdES LTV

PAdES and PDF Java Toolkit Part 4 : PAdES LTV

At last we make it to Part 4 of the PAdES specification!

In case you missed parts 1 through 3 here are links to them so you can catch up

In this discussion we outline how PAdES overcomes the problem of validating documents long after the document was originally signed. The particular concerns that PAdES LTV was designed to address are how do you verify signatures after the signing certificate expires, the original validation data is no longer available or there is uncertainty about what the original validation data was. To overcome these concerns the PAdES specification introduces two new types of dictionaries for use in PDF files, these are the Document Security Store (DSS) and Validation Relation Information (VRI). PAdES LTV also introduces a new type of signature, the Time stamp signature which is defined by the Document Time Stamp dictionary. Let’s take a look at these new dictionaries a little closer.

The Validation Relation Information (VRI) dictionary

The VRI dictionary relates the validation data stored in a document to the specific signature in the document that it is meant to validate. The entries in this dictionary are as follows

  • Type – must be “VRI” if present
  • Cert – an array of indirect references to the certificates used in this signature
  • CRL – an array of indirect references to the CRLs used in validation of this signature
  • OCSP – an array of indirect references to the OCSPs used in validation of this signature
  • TU – date/time at which this VRI dictionary was obtained
  • TS – stream containing the BER-encoded Time-stamp which represents the secure time at which this VRI dictionary was obtained

The TU and TS entries represent a time stamp of when the document was signed. Only one of them can be present in a VRI dictionary at a time. The PAdES specification also states that the TU and TS entries are completely optional and that the values in them should be ignored.

The Document Security Store (DSS) dictionary

The DSS dictionary is meant to store all validation related information for some or all signatures in a document in one place. The DSS dictionary is stored in the documents catalog dictionary. The DSS dictionary contains the following entries

  • Type – must be “DSS” if present
  • VRI – a dictionary that contains VRI dictionaries for signatures
  • Certs – an array of indirect references to the certificates used in signatures
  • OCSPs – an array of indirect references to the OCSPs used in validation of signatures
  • CRLs – an array of indirect references to the CRLs used in validation of signatures

Document Time Stamp dictionary

The document time stamp dictionary that PAdES defines is a slight variation of the standard PDF Signature so if you are not familiar with the structure of that object please see ISO 32000-1 section 12.8.1 . PAdES places the following restrictions on the PDF signature dictionary to turn it into a document time stamp dictionary

  • Type must be “DocTimeStamp”
  • Subfilter must be “ETSI.RFC3161”
  • Contents must contain a time stamp token

The following entries can not be present

  • Cert
  • Reference
  • Changes
  • R
  • Prop_AuthTime
  • Prop_AuthType

The following entries should not be present

  • Name
  • M
  • Location
  • Reason
  • ContactInfo

The reason that these should not be in the dictionary is that they are stored elsewhere in the document.

PDF Java Toolkit code for working with PAdES LTV

We added a sample to the PDF Java Toolkit named AddPAdESLTV as part of the samples.pades package. This is the code that we will model our discussion after, some pieces of the code have been moved around to make the discussion flow better, the results are the same though.

For this discussion assume that we have already obtained an instance of a PDFDocument object named doc.

The first thing we are going to do is create a DSS dictionary because the DSS dictionary holds the references to all of the other objects we will be creating. As part of creating the DSS dictionary we will also create a VRIMap which helps organize the references that the DSS dictionary holds.

[sourcecode language=”java”]
PDFDocumentSecurityStore dss = PDFDocumentSecurityStore.newInstance(doc);

// Create a PDFVRIMap to use with the PDFDocumentSecurityStore
PDFVRIMap vriMap = PDFVRIMap.newInstance(doc);

// Set the VRIMap of the PDFDocumentSecurityStore to the
dss.setPDFVRIMap(vriMap);
[/sourcecode]

We will need a few PDFStreamList objects (list objects that holds streams specialized for PDF).

[sourcecode language=”java” firstline=”8″]
// Create lists to hold any certs, CRLs, and OCSP responses needed.
PDFStreamList certList = PDFStreamList.newInstance(doc);
PDFStreamList crlList = PDFStreamList.newInstance(doc);
PDFStreamList ocspList = PDFStreamList.newInstance(doc);
[/sourcecode]

At this point the sample code iterates through all of the signatures in a document looking for one that meets the following criteria

  • is signed
  • has a subfilter that matches one of Adobe PKCS7 detached, Adobe PKCS7 SHA1, or ETSI CADES detached

So we will skip the code for iterating over the signatures and move to the inner workings of the loop.

If we find a signature that meets the criteria listed above we need to create entries in the VRIMap for the CRL and the OCSP of the signature. To add these to the VRIMap we use the hash of the signature as the key and a PDFValidationRelatedInfo object as the value. Let’s start by getting the hash of the signature, assume we have a PDFSignature object named sig.

[sourcecode language=”java” firstline=”20″]
// Get the digest of the signature's PKCS7 packet.
byte[] pkcs7 = sig.getContents();
MessageDigest digester = MessageDigest.getInstance("SHA1");
byte[] hash = digester.digest(pkcs7);
[/sourcecode]

Next we read in the CRLs from local files on disk using the createBERStreams utility function because PDF Java Toolkit does not have access to online resources or a trust store.

[sourcecode language=”java” firstline=”24″]
// Get the CRLs and OCSPs for this signature;
PDFStream[] sigCRLs = createBERStreams(doc, crlDirPath, ".crl");
[/sourcecode]

We then create our PDFValidationRelatedInfo object and fill in its values.

[sourcecode language=”java” firstline=”26″]
// Set up the VRI dict for this signature.
PDFValidationRelatedInfo vri = PDFValidationRelatedInfo.newInstance(doc);

// Set the time of the VRI
vri.setUnsecureTime(new ASDate());

// Create a new stream list to hold the CRLs
PDFStreamList vriCRLList = PDFStreamList.newInstance(doc);
vriCRLList.addStreams(sigCRLs);

// Set the list of CRLs for this signature in the VRI
vri.setCRLs(vriCRLList);

// Add the VRI object we just created to the VRIMap
vriMap.setPDFVRI(hash, vri);
[/sourcecode]

At this point the VRI entry for a signature has all of its required information, except the OCSPs which the sample leaves out, but the DSS dictionary still needs to have references to the CRLs and OCSPs in each signature. So now we need to loop over the CRLs that are in the VRI for this signature, once again the loop setup is left out.

[sourcecode language=”java” firstline=”42″]
X509CRL x509CRL = new X509CRL(crl, 0, crl.length);
byte[] crlSig = x509CRL.getSignature();

// Define the DER encoding for an octet string.
OctetStringContainer octetContainer = new OctetStringContainer(ASN1.NO_SPECIAL, true, ASN1.NO_TAG, crlSig, 0, crlSig.length);
ASN1Container[] asn1Def = {octetContainer};
byte[] octetStrBytes = ASN1.derEncode(asn1Def);
digester = MessageDigest.getInstance("SHA1");
hash = digester.digest(octetStrBytes);

// Create a VRI dict for the CRL's signature.
vri = PDFValidationRelatedInfo.newInstance(doc);
vri.setUnsecureTime(new ASDate());

// Add it to the VRIMap
vriMap.setPDFVRI(hash, vri);
[/sourcecode]

Then we need to add the streams we created for the certificates and CRLs to the global stream objects we created

[sourcecode language=”java” firstline=”59″]
// Add the CRLs used by the certificate chain to list of all CRLs
// in the DSS CRL array. you would also need to do this for OCSP
// reponses.
crlList.addStreams(sigCRLs);

// Add the certificates used by this signature to the global
// cert list that will go in the DSS dict.
PDFStream[] certs = createBERStreams(doc, certDirPath, ".cer");
certList.addStreams(certs);
[/sourcecode]

Now the DSS and VRI objects are mostly complete (I say mostly because as noted earlier we left out the OCSPs). But for PAdES LTV we also want to add a time stamp signature. To do that we have a couple of objects to create as setup and some final things to do with the DSS. So let’s finish off the DSS.

[sourcecode lanauge=”java” firstline=”67″]
// Get the PDFCatalog, this is where the DSS dictionary is stored
PDFCatalog catalog = doc.requireCatalog();

// Set the DSS dictionary on the catalog
catalog.setDSSDictionary(dss);

// Fill in the lists of certificates, CRLs, and OCSPs
if (certList.size() > 0)
dss.setCerts(certList);
if (crlList.size() > 0)
dss.setCRLs(crlList);
if (ocspList.size() > 0)
dss.setOCSPs(ocspList);
[/sourcecode]

Before signing the document we need to set version of the document in the save options so that LTV is supported in our document after signing has finished.

[sourcecode language=”java” firstline=”77″]
PDFSaveFullOptions saveOptions = PDFSaveFullOptions.newInstance();

// change the version number in the save options
// LTV is only supported in documents that use extension level 5.
saveOptions.setVersion(PDFVersion.v1_7_e5);
[/sourcecode]

Now create a signature options object and apply the save options to it.

[sourcecode language=”java” firstline=”82″]
// To create a document time stamp we must use a SignatureOptionsDocumentTimeStamp object
SignatureOptionsDocumentTimeStamp sigOptions = SignatureOptionsDocumentTimeStamp.newInstance();

// use save options when signing the document
// so that the version number is correctly updated.
sigOptions.setSaveOptions(saveOptions);
[/sourcecode]

And tie all of this together by creating a document time stamp signature. This is almost the exact same as creating a regular signature except it requires the use of a time stamp provider. There is a sample time stamp provider included in the PDF Java Toolkit samples but for production code you will want to change this because its values are hardcoded instead of getting them from a time stamp provider.

[sourcecode language=”java” firstline=”89″]
// Create a new crypto context to use when time stamping the document
CryptoContext cryptoContext = new CryptoContext(CryptoMode.NON_FIPS_MODE, "SHA1", "RSA");

// Create a new time stamp provider
TimeStampProvider tsProvider = new SampleTimeStampProvider();

// Register the time stamp provider with the crypto context
cryptoContext.registerTimeStampProvider(tsProvider);

// Create a signature service provider with the crypto context that was just created
SignatureServiceProvider defaultCryptoProvider = new CertJNonFIPSProvider(cryptoContext);

// Ask the signature manager to apply a document time stamp signature with the options we have specified
sigMgr.applyDocumentTimeStampSignature(sigOptions , defaultCryptoProvider, writer);
[/sourcecode]

Notice that at the beginning I said this sample looks for a signature in the document that meets its requirements. This sample can be modified to create the signature and add PAdES LTV instead of doing it as two separate pieces of code. For testing purposes though, you can use the output of either the AddPAdESBasic or AddPAdESEPES samples as input to the AddPAdESLTV sample.

With that said, I hope you have enjoyed reading this series about working with PAdES and the PDF Java Toolkit that Datalogics distributes. If you have any further questions feel free to leave them here as comments or contact us through our website. Thanks for reading!

Leave a Reply

Your email address will not be published. Required fields are marked *