Signing PDFs with an EU identity card (and how to get one)
History
Back in 2011 we wrote an article on how to achieve a "perfect" Digital Signature in Acrobat. This involved signing with a key issued by a company on the Adobe CDS list, where CDS was "Certificate Document Services".
Eight years on and the technology hasn't changed much. But the legislative framework has, certainly in Europe. The EU eSignature program has standardised the use of digital signatures across most of Europe as part of its eiDAS Regulation. The technical standards for these Advanced Electronic Signatures describe how to digitally sign XML, binary files and PDF, with the PDF variation known as PAdES.
PAdES and PDF
PAdES is part of "PDF 2.0" (officially
ISO32000-2:2017), although the original ETSI
Technical Specification, TS 102778
was published in 2010. Technically, they're not that different to the regular PDF
signatures first added to
PDF 20 years ago in PDF 1.3. We've supported PAdES in our API since 2016, but as the
process of signing
with a PAdES signature is really no different to a normal signature, we haven't written
much on it: the one line you need to add is
factory.setPAdES(true)
.
What is different is that the list of trusted root certificates supplied by the EU is included in Acrobat by default. This makes a big difference when you're verifying a signatures authenticity. Eight years ago, when we wrote the original version of this article, we had to track down a hardware token, purchase a digital identity (from a brand that has long since disappeared), install custom drivers for the USB token they posted us before signing the PDF. It was complicated.
Now, in theory, all you need to create a "perfect" digital signature is an EU identity card.
EU Identity Cards
The EU is in the news a bit at the moment, especially in London, where BFO is based. But today I shall skip lightly over our national trainwreck and talk, instead, about identity cards.
Most EU countries have some form of ID
card. They are largely
standardized smart-cards,
and many of them actually run
Java.
Which sounds promising, but the javax.smartcard
interface is very low level
and the flexibility of smart-cards has led to considerable complexity. We have tried
and failed to understand most of it.
All we specifically want is the ability to sign data with one, and for that we need PKCS#11.
PKCS#11 is a software API for accessing cryptographic hardware. Java supports it quite well, and our API can use it for signing documents: we documented the process in the last article we wrote on this topic, when we used a USB PKCS#11 hardware token, and software from OpenSC.
So when started planning this project a few months ago, and went straight to the OpenSC project to see what hardware they supported. We tried a few USB tokens and a few cards, with mixed success. Part of the issue was that the technical requirements for an Adobe AATL was that the storage had to be on a FIPS140-2 or better device, which we struggled to find in card format. Then we noticed that OpenSC also supported some National Identity cards, including Estonia.
Estonian Identity Cards and e-Residency
Since 2014, Estonia have been running their e-Residency program, which theoretically allows anyone to become an "e-resident" of Estonia. So I applied. It's quite an easy process (and recently quite popular in Britain, for some reason). Initially OpenSC's software was unable to use it, but that's been fixed just this week.
So, without further waffle, here are the steps we took, and that you can take too - anywhere in the world - to get yourself a digital identity card that will allow you to sign a PDF with a PAdES signature that is approved by Acrobat, and automatically accepted across the EU for signing documents, and generally doing business.
Step 1: Establish your identity
Apply for e-Residency in Estonia. If you already have an EU identity card listed on the OpenSC supported hardware page, you can skip this step.
If you prefer to do this without a national identity card, we can recommend Trustfactory's "Personal Pass" which was, at the time of writing, the cheapest way to get an Adobe AATL approved certificate at $43. You'll need your own hardware in this case. I would like to be able to recommend a particular FIPS-140 USB token that just worked but so far, I can't. We'll keep trying, and will update this article when we have a fully working solution
The steps to generate a keypair and certificate signing request are broadly the same for all Certifiying Authorities, and are probably unchanged from our previous article.
Step 2: Install the software
Download and install OpenSC from their website. For the Estonian ID cards, you'll
need at least version 0.20.
A few months from now, it's likely the opensc
package supplied with your version of Linux will includes these changes too.
Step 3: Set up your card
When your identity card arrives, you may need to do some setup. For my e-Residency card, I had to run the DigiDoc 4 software and initialise two PIN numbers. The PIN you'll use with Java will depend on the slot.
You'll probably want to put your identity card into a USB smart card reader, and see if it's recognised by OpenSC. Here's what we got from mine.
bash$ /usr/local/bin/pkcs11-tool -L Available slots: Slot 0 (0x0): ACS ACR 38U-CCID token label : BREMFORD,MICHAEL PAUL,... (PIN1) token manufacturer : IDEMIA token model : PKCS#15 emulated token flags : login required, token initialized, PIN initialized, readonly hardware version : 0.0 firmware version : 0.0 serial num : UA00XXXXX pin min/max : 4/12 Slot 1 (0x1): ACS ACR 38U-CCID token label : BREMFORD,MICHAEL PAUL,... (PIN2) token manufacturer : IDEMIA token model : PKCS#15 emulated token flags : login required, token initialized, PIN initialized, readonly hardware version : 0.0 firmware version : 0.0 serial num : UA00XXXXX pin min/max : 5/12
Step 4: Sign!
That's it. All we need to do now is plug it into Java. Here's some code I used to create a simple "Hello World" (in Estonian, of course) and sign it.
Edit Apr 2021: we've changed our example to select "slot 1" on the card, as this seems to be the certificate intended for signing on most - if not all - eiDAS cards.
import org.faceless.pdf2.*; import java.security.*; import java.util.*; import java.io.*; import java.net.URL; public class TestEE { private static final String password = "00000000"; private static final String config = "name=OpenSC\nlibrary=/Library/OpenSC/lib/opensc-pkcs11.so\nslot=1"; public static void main(String[] args) throws Exception { PDF pdf = new PDF(); PDFPage page = pdf.newPage("A4"); PDFStyle style = new PDFStyle(); style.setFont(new StandardFont(StandardFont.HELVETICA), 24); page.setStyle(style); page.drawText("Tere, Maailm", 50, 800); Provider provider; // Set up the provider (if you're using Java 6, 7 or 8)... provider = new sun.security.pkcs11.SunPKCS11(new ByteArrayInputStream(config.getBytes("ISO-8859-1"))); // ... or to set up the provider (if you're using Java 9 or later) provider = Security.getProvider("SunPKCS11"); provider.configure("--" + config); // undocumented way to load configuration from a String AcrobatSignatureHandlerFactory factory = new AcrobatSignatureHandlerFactory(); factory.setPAdES(true); factory.setValidateCertificatesOnSigning(true); // Make it LTV factory.setTimeStampServer(new URL("http://timestamp.digicert.com")); FormSignature sig = new FormSignature(); KeyStore keystore = KeyStore.getInstance("PKCS11", provider); keystore.load(null, password.toCharArray()); String alias = keystore.aliases().nextElement(); // Use the first alias sig.sign(keystore, alias, password.toCharArray(), factory); pdf.getForm().getElements().put("Sig1", sig); OutputStream fo = new FileOutputStream("HelloWorld.pdf"); pdf.render(fo); fo.close(); } }
There are a few things to note here. First, the method of creating a "PKCS11" provider
changed in Java 9 - you
no longer need to call into the sun.*
package. The config
string is just the name
of the provider (anything you like) and the path to the OpenSC "pkcs11" library, which
is as shown for macOS,
and probably /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
for Linux.
And here's the result.
You can also do this with our PDF Viewer. You'll need to configure it to use a PKCS11 when you run it, which is quite easy to do from the command line. You'll recognise the config string from the example above (and don't forget to add "type=pkcs11"). We recommend at least version 2.23.5, which included some fixes for smartcard use - this article was supposed to accompany that release, but was delayed while we struggled with the other parts of the equation.
java -Dorg.faceless.pdf2.viewer2.KeyStoreManager.params="type=pkcs11;name=OpenSC;library=/Library/OpenSC/lib/opensc-pkcs11.so;slot=1" org.faceless.pdf2.viewer2.PDFViewer
There you have it. Anyone can become at least an electronic resident of the EU.