Perfect PDF Digital Signatures

Adobe CDS, or, how to ensure your Signature no longer has problems in Acrobat

The Problem

It's very easy to digitally sign a PDF. In Acrobat, you select "Save As Certified" from the File menu. In our PDF Viewer you select "Invisibly Sign Document" from the "Tools" menu. Using our API, you can do it in about 10 lines of code. However all these approaches have one thing in common.

The phrase "this signature has problems" refers to the fact the signature is not created by a trusted identity: one certified by a trusted chain of certificates going back to the Adobe root certificate.

It's very easy to get around: simply bring up the signature properties, select the "Signer" tab, bring up the certificate and select "Trust this Identity". But if you want to create a document that's trusted by default, this is a little more complicated. This article describes how to achieve this with our PDF Library. Here's an example PDF.

Adobe CDS

In order for a document to be trusted in a new install of Acrobat, the signer must be certified by a Certificate Document Service Provider. These are simply Certification Authorities who work with Adobe, and include the big players you'd expect - VeriSign and Entrust, amongst others. In order for them to certify a key it has to meet certain conditions, the main one being that it's stored on a "Hardware Security Module", or HSM, that meets FIPS 140-2 (Level 2 or higher).

In this article we're going to cover the steps required to

  • Set up your HSM to work with Java
  • Get your CDS certified digital identity
  • Use that identity to sign a PDF with our PDF Library

The following should work with Sun Java 6.0 on Windows 32-bit or UNIX - we tested with Ubuntu Linux 10.04. However, for reasons known best to the developers at Sun, Windows 64-bit JVMs are not supported (the sun.security.pkcs11.SunPKCS11 class is missing from the Java Runtime). We haven't tested with IBM Java.

1. Setup

1.1 Obtaining your HSM

Your first step to getting a CDS certified key is obtaining an HSM. There are a number to choose from, ranging from network-attached HSMs in the four-figure price range, all the way down to the humble Safenet eToken Pro, a USB Token we managed to pick up for about USD$100. It doesn't matter what you go for, but if you want it to work with Java it must have a PKCS#11 interface - most do - and drivers for your OS, as the Java PKCS#11 classes need to reference a native library.

Some CDS Providers will supply an HSM when you purchase a Key, so it's worth checking before you buy one.

1.2. Obtaining your Key

A CDS certified identity is technically no different to a regular certified identity, and the procedures for purchasing one should be roughly the same. Naturally each company will have their own requirements and those will be subject to change, so while we believe the following to be correct at the time of writing it is subject to change.

  • TrustCenter (part of Symantec) were the company we used for the proof of concept behind this article. You can purchase their TC Business ID for Adobe certificates online, and there's a complete guide to installing and initializing the token and generating the key here - we'd suggest you follow their guide exactly, rather than the steps here. You will need to obtain your own eToken before starting.
  • Symantec also bought Verisign's Authentication Services, and their Adobe CDS page is their entry-point. At the time of writing, we understand that they have two broad types of product: a low-volume key which is supplied on an Aladdin eToken, or for higher volumes you supply your own HSM and they will sign your key as normal. No special drivers are required, and we've been told both products can be used with Java.
  • Entrust have a similar model for their CDS Certificates, but we were told their "Individual" and "Group" certificates, which are supplied on an eToken, are not usable with Java due to custom drivers being required. Their higher volume certificates are installed on your own HSM, and so should work correctly.

1.3a. Installing a Safenet eToken in Windows

The HSM we used was a Safenet eToken. On Windows XP we installed the Safenet Authentication Client v8.0 SP2, which recognised our token instantly. If your token wasn't supplied by your CA with a key on it, you'll need to initialize it. Open the GUI client and initialize the token with a new administrator password and "token password" (in all the examples on this page, we assume the "token password" was set to "secret"). Click the "Advanced" button and select "FIPS" (see enlargeable screenshort to the right), then initialize.

In order to access the eToken from the Java keytool.exe program, you'll need a configuration file containing the two lines

name = eToken
library = C:\Windows\system32\eTPKCS11.dll

Save this file somewhere on your system - we've called ours keytool-etoken.cfg. The format of this file is described here, but those two lines are all that's needed for an eToken.

1.3b. Installing a Safenet eToken in Linux

As you might expect, the procedure in Linux is more complicated. First, install the pcscd and opensc packages (called pcscd and opensc on Ubuntu). You'll also need the the SafeNet Authentication Client supplied with your eToken - the package we installed was SafenetAuthenticationClient-8.0.5-0_i386.deb, although a newer version may be available.

If you're using a brand new token, now is the time to initialize it. In general we'd recommend following the steps recommended by the HSM supplier, but some issues with Safenet's client prevented this for us. We did manage to initialize the token using the pkcs11-tool from the pcscd package. Here's how:

# Initialize the token, set the "administrator password"
pkcs11-tool --module /usr/lib/libeTPkcs11.so --init-token
  --label MyToken --so-pin adminsecret

# Set the "token password"
pkcs11-tool --module /usr/lib/libeTPkcs11.so --init-pin
  --pin secret

Finally, in order to access the token from Java you'll need to edit the file /etc/eToken.conf and add the line TolerantX509Attributes=1 under the [GENERAL] section. As under Windows, you'll also need to create a configuration file for the Java keytool program - we used the following and saved it as keytool-etoken.cfg:

name = eToken
library = /usr/lib/libeTPkcs11.so

2. Using the HSM from Java

Now your HSM is set up with a key, you need to get it working with Java and then with our API. Here's how:

2.1 Accessing the HSM from Java's keytool

You can now use the Java keytool program to access the token as a regular Java java.security.KeyStore. So for example, to list which keys are on the token:

keytool -list -keystore NONE -storetype PKCS11 -providerClass
   sun.security.pkcs11.SunPKCS11  -providerArg keytool-etoken.cfg
  -storepass secret
or to generate a new key:
keytool -genkeypair -alias testkey -validity 365 -keyalg RSA
  -keysize 2048 -sigalg SHA1withRSA -keystore NONE
  -storetype PKCS11 -providerClass sun.security.pkcs11.SunPKCS11
  -providerArg keytool-etoken.cfg -storepass secret

Note the arguments above: the providerArg is the full path to the configuration file keytool-etoken.cfg you created in step 1.3 above, and the storepass is the token password you set when you were initializing the token. If you're generating your own keys, it's also important to specify RSA as the key algorithm.

2.2 Confirming the certificate chain

In order for a signed PDF to be trusted in Acrobat, the certificate chain must be embedded in the PDF and the chain must be signed by the "Adobe Root CA". These certificates are supplied with Acrobat but are not included with our PDF Library, so it may be necessary to import them into the KeyStore before signing (as it was for us with our TrustCenter-supplied certificate).

You can check the certificate chain in the KeyStore with the keytool -list code above: the key you're using to Sign PDFs should have a "Certificate Chain Length" of at least 3, and the final Certificate should belong to the "Adobe Root CA". Check this annotated output from the keytool command to see what the results should look like.

If you don't have all the required certificates, you need to import them. This is a Java-focused blog so we've tried to do this with keytool, but frankly we couldn't get it working with our eToken. We'd suggest you import the certificates you require using the interface supplied by your HSM manufacturer - for us, it was a matter of downloading the TrustCenter CA Certificate and the Adobe Root CA Certificate and importing them onto the eToken, as shown on the screenshot to the right (click to enlarge).

2.3 Accessing the HSM from Java

Signing a PDF with our API means supplying a java.security.KeyStore to the FormSignature class, and this is very simple to do. The following code will open the eToken from Java:
// "config" contains the same data as "keytool-etoken.cfg"
byte[] config = "name=eToken\n
  library=/usr/lib/libeTPkcs11.so\n".getBytes("ISO-8859-1");
char[] password = "secret".toCharArray();  // "token password"
String alias = "mykey";  // The key alias in the KeyStore
Provider provider = new sun.security.pkcs11.SunPKCS11(new ByteArrayInputStream(config));
KeyStore ks = KeyStore.getInstance("PKCS11", provider);
ks(null, password);

Rather than referencing the keytool-etoken.cfg file we created earlier, we're passing in the contents of the configuration file as an InputStream instead - much neater, as no external configuration file is required. Once you have this KeyStore you can use it as you would any other, with the proviso that modifications are made immediately: calling keystore.save() is not required.

2.4 Accessing the HSM from the PDF Viewer

Configuring our PDF Viewer to use the HSM as a KeyStore can be done with a single property: org.faceless.pdf2.viewer2.KeyStoreManager.params. This can be specified on the command line like so for Linux:
java -Dorg.faceless.pdf2.viewer2.KeyStoreManager.params=
   "type=pkcs11;name=eToken;library=/usr/lib/libeTPkcs11.so;
   password=secret" org.faceless.pdf2.viewer2.PDFViewer

or, if you're on Windows

java -Dorg.faceless.pdf2.viewer2.KeyStoreManager.params=
  "type=pkcs11;name=eToken;
  library=C:\Windows\System32\eTPKCS11.dll;password=secret"
  org.faceless.pdf2.viewer2.PDFViewer

These parameters are a combination of those defined in the Java PKCS#11 Reference and those defined in the API Docs for KeyStoreManager - no external configuration file is required.

Note: Although the examples above refer to a Safenet eToken HSM, you can access any PKCS#11 compliant HSM from Java using the same approach: only the "name" and "library" in the configuration are likely to change