The PKCS#11 standard defines a platform-independent API for accessing cryptographic tokens. If a device manufacturer or a service operator provides a PKCS#11 driver, they allow you to use that device or service from different platforms with the same functionality and the same key material.
In this part we focus on signing via PKCS#11; first we illuminate how to access PKCS#11 drivers from Java and .Net, and then we use those ways of access with a number of example devices.
How to Address PKCS#11 Drivers in Java
There are two main ways to address tokens via PKCS#11 drivers in Java: Via a security provider that abstracts away the PKCS#11 API and via a thin PKCS#11 wrapper that maps the PKCS#11 API to Java classes and methods.
Using a PKCS#11 Security Provider
If you address a PKCS#11 driver via a Java security provider, you can of course use the
PrivateKeySignature class included in the Java distribution as described before. To later be able to focus on actual differences, we will use a different
IExternalSignature implementation, one that provides convenience methods for selecting key and certificate by their alias.
Pkcs11Signature accepts or initializes a PKCS#11 security provider in its constructor; if it initializes one itself, it uses the SunPKCS11 provider. It allows key selection using this method:
and then signs using
Pkcs11Signature.java; full code at <https://git.itextsupport.com/projects/I7JS/repos/signing-examples/browse/pkcs11-java11/src/main/java/com/itextpdf/signingexamples/pkcs11/Pkcs11Signature.java>.)
Thus, if you have a SunPKCS11 configuration as string in
config, the desired alias in
alias, the pin in
pin, and the desired hash algorithm in
hash, you initialize a
Pkcs11Signature like this:
Similarly we used a custom
IExternalSignatureContainer implementation for signing via a PKCS#11 security provider, to be able to focus on details specific to the use case in later examples.
This custom implementation
Pkcs11SignatureContainer actually is very similar to the
UtimacoJceSignatureContainer above. Just like in that case we cannot use the
PrivateKeySignatureContainerBC as the way BouncyCastle normalizes signature algorithm names there interferes with the proper execution of code in the PKCS11 providers.
Merely the constructors and the keystore retrieval are PKCS11 provider specific, similar to the
Pkcs11Signature in this section. You can find the code at <https://git.itextsupport.com/projects/I7JS/repos/signing-examples/browse/pkcs11-java11/src/main/java/com/itextpdf/signingexamples/pkcs11/Pkcs11SignatureContainer.java>.
Using the SunPKCS11 Security Provider
The PKCS#11 provider included in the Java API, SunPKCS11, is the obvious choice for accessing PKCS#11 via a security provider as it’s included in the common Java runtime distributions.
In older Java versions, e.g. Java 8, you could instantiate and register SunPKCS11 for a specific PKCS#11 driver like this:
In later Java versions, e.g. Java 11, you instead do it like this:
config is the path and name of a configuration file. The exact contents of such a configuration file depend on your PKCS#11 driver and device or service. The general form is like this:
name = Utimaco
library = d:\Program Files\Utimaco\CryptoServer\Lib\cs_pkcs11_R2.dll
slot = 0
config may contain that configuration as a plain String prefixed by “--”:
config = "--name = Utimaco\nlibrary = "
+ "d:/Program Files/Utimaco/CryptoServer/Lib/cs_pkcs11_R2.dll\n"
+ "slot = 0\n";
You can find details in the Java documentation.
Using the IAIK PKCS#11 Security Provider
An alternative PKCS#11 provider has been implemented at the Graz University of Technology and can be retrieved here. It is an independent implementation and may be better fitted to your use case. In particular it has slightly different expectations on how keys and associated certificates are tagged in a PKCS#11 API implementation.
You instantiate and register the IAIK security provider like this:
The set of properties to set depend on your PKCS#11 driver and device or service. It might amount to something like this:
Using the IAIK PKCS#11 Wrapper
If you prefer to access the PKCS#11 driver using a Java API matching the PKCS#11 API, you can do so using a thin PKCS#11 wrapper.
Doing so will quite likely take more code to identify the matching private keys and certificates than to actually sign. If you use security providers instead, this job mostly is done under the hood. But security providers have to make certain assumptions how to recognize such matches, and some PKCS#11 drivers or the devices underneath present their keys and certificates differently. So in some cases you eventually will have to use a solution based on a PKCS#11 wrapper.
The Graz University of Technology provides such a wrapper here, which they actually use themselves in their PKCS#11 Java security provider.
If you use this wrapper library to access PKCS#11 drivers, you can utilize code similar to that in the
Pkcs11WrapperKeyAndCertificate class <https://git.itextsupport.com/projects/I7JS/repos/signing-examples/browse/pkcs11-java11/src/main/java/com/itextpdf/signingexamples/pkcs11/Pkcs11WrapperKeyAndCertificate.java> to open a session and reference the private key and certificate you want to work with. Depending on peculiarities of the PKCS#11 drivers and devices you use you might have to tweak it a bit. If you need the code to work for a single driver and device type only, your code may be considerably shorter and simpler.
Having selected a private key and a certificate entry, though, using them in a
IExternalSignature implementation is easy:
Pkcs11WrapperSignature; full code at <https://git.itextsupport.com/projects/I7JS/repos/signing-examples/browse/pkcs11-java11/src/main/java/com/itextpdf/signingexamples/pkcs11/Pkcs11WrapperSignature.java>)
IExternalSignatureContainer implementation you can use such a key and certificate utilizing the CMS signature container creation features of some security library, such as BouncyCastle which we have used already in earlier examples:
Pkcs11WrapperSignatureContainer; full code at <https://git.itextsupport.com/projects/I7JS/repos/signing-examples/browse/pkcs11-java11/src/main/java/com/itextpdf/signingexamples/pkcs11/Pkcs11WrapperSignatureContainer.java>)
buildContentSigner returns a custom
ContentSigner implementation using the key and certificate references from the PKCS#11 wrapper:
Pkcs11WrapperKeyAndCertificate; full code at <https://git.itextsupport.com/projects/I7JS/repos/signing-examples/browse/pkcs11-java11/src/main/java/com/itextpdf/signingexamples/pkcs11/Pkcs11WrapperKeyAndCertificate.java>)
How to Address PKCS#11 Drivers in .NET
.NET does not offer a “native” PKCS#11 integration. Apparently Microsoft counts on security device manufacturers and security service operators to make platform specific CryptoAPI or CNG providers available.
Many manufacturers and operators do offer such a provider but many others don’t. Furthermore, in some cases the providers do not allow to access the same key material as the PKCS#11 drivers. Thus, a number of independent software packages turned up that do allow accessing PKCS#11 drivers from .NET. As an example we use Pkcs11Interop, a managed .NET wrapper for unmanaged PKCS#11 libraries, available via NuGet and documented here.
Just like the IAIK PKCS#11 Wrapper for Java discussed above Pkcs11Interop offers a thin wrapper around the PKCS#11, in this case using .NET classes and methods. Consequently, just like in the Java case you may have to write more code to identify the matching private keys and certificates than to actually sign using them, at least if you try to make that not specific to some device or service.
Thus, you can use code like in the method
Select of the example
Pkcs11Signature (<https://git.itextsupport.com/projects/I7NS/repos/samples/browse/itext/itext.publications/itext.publications.signing-examples.pkcs11/iText/SigningExamples/Pkcs11/Pkcs11Signature.cs>) to open a session and reference the private key and certificate you want to work with. Depending on peculiarities of the PKCS#11 drivers and devices you use you might have to tweak it a bit. If you need the code to work for a single driver and device type only, your code may be considerably shorter and simpler.
Also just like in the case above, the Sign method turns out to be simple:
Pkcs11Signature; full code at <https://git.itextsupport.com/projects/I7NS/repos/samples/browse/itext/itext.publications/itext.publications.signing-examples.pkcs11/iText/SigningExamples/Pkcs11/Pkcs11Signature.cs>)
Also, similar to the Java case above we can create an
IExternalSignatureContainer implementation based on Pkcs11Interop and the CMS building of BouncyCastle or .NET core libraries themselves.
In this section we’ll take a look at a number of devices and services with PKCS#11 drivers in respect to how to use them for signing with iText.
BeID, the Belgium Identity Card
You can easily sign PDFs using the Belgian ID card via PKCS#11. E.g. by initializing the
Pkcs11Signature from above like this:
Of course you need to know where you installed your Belgium Identity Card software, which slot the card in question is in (usually only relevant if you have multiple card readers), and which PIN your card has.
For RSASSA-PSS signatures we need an
IExternalSignatureContainer implementation, so we use
Pkcs11SignatureContainer here and initialize it like this:
Again you need to know where you installed your Belgium Identity Card software, which slot the card in question is in, and which PIN your card has.
D-Trust Qualified Signature Card
In contrast to the BeID which could be used without issue, using the D-Trust Qualified Signature Card requires clearing a number of hurdles.
First of all the Bundesdruckerei does not provide a free PKCS#11 driver. We obtained one for Windows by buying the Nexus Personal suite.
The next hurdle is due to the fact that the D-Trust card features two signature mechanisms, one with an advanced certificate and one with a qualified signature. The Nexus Personal driver exposes them as distinct slots. If you have initialized them with different PINs and keep experimenting with the wrong slot, you can brick that mechanism.
Once you’ve got your driver and slot sorted out, you might imagine you can apply the
Pkcs11Signature class like this:
Unfortunately, though, this results in an invalid signature!
This is caused by a provider/driver incompatibility: The SunPKCS11 provider, when looking for keys and certificates on a PKCS11 device, iterates over the private keys and associates each of them with the certificate with the same ID. But the Nexus driver for D-Trust cards offers all certificates with the same ID and SunPKCS11 uses the one it retrieves first, by chance the root certificate, not the signer certificate. Thus, the private key and certificate do not cryptographically match, making the result signature invalid.
Fortunately we have an alternative provider, the IAIK PKCS#11 security provider:
signature instance we finally succeed. The IAIK provider does not only offer the pairing of the private key and the first certificate with the same ID like the SunPKCS11 provider but instead all pairings of the private key with such a certificate, with an alias derived from the label of the certificate. By selecting the pairing for the alias "Signaturzertifikat", therefore, the used private key and certificate do cryptographically match, making the result signature valid.
Unfortunately, though, the IAIK provider requires licensing, and if you don’t qualify for a free license (e.g. a research license), your project may not have the resources to afford a license.
Fortunately you can fall back on the IAIK PKCS#11 Wrapper which offers use under an Apache-style license. Thus, you can make use of the
Pkcs11WrapperSignature from above initialized like this:
and of the Pkcs11WrapperSignatureContainer initialized like this:
Also, if you want to create RSASSA-PSS signatures, simply request the signature algorithm like this instead
Entrust Signing Automation Service
The Entrust Signing Automation Service is a Cloud-based digital signing service that can be plugged in to your document applications and workflows in order to automatically generate Entrust-issued digital seals for your organisation's documents. (Quoted from the Entrust web site.)
Many other Cloud-based digital signing services provide a custom web API to access their service. In contrast to that Entrust provides a PKCS#11 driver to access their service. Thus, you can simply use the Java
Pkcs11Signature initialized like this:
SoftHSM is an implementation of a cryptographic store accessible through a PKCS #11 interface. You can use it to explore PKCS #11 without having a Hardware Security Module. It is being developed as a part of the OpenDNSSEC project. (Quoted from the OpenDNSSEC web site.)
SoftHSM can be used to represent a generic PKCS#11 device and driver in test or presentation scenarios.
This emulated device can be used without issues for signing with iText, e.g. initializing a
Pkcs11Signature like this:
Similarly easily you can initialize a
Pkcs11WrapperSignature, or a
Pkcs11WrapperSignatureContainer for signing with SoftHSM.
Utimaco is a world-leading manufacturer and specialized vendor of Hardware Security Modules. Utimaco offers fully functional HSM software simulators for download. The Utimaco HSM simulator enables evaluation, development and integration testing without purchase, delivery or installation of hardware. (Quoted from the Utimaco web site)
In addition to a PKCS#11 driver Utimaco also supports platform dependent providers (see part II in Signing With Custom JCA/JCE Security Providers (Java) and Signing With Custom CNG Integrations (.NET)). For a platform independent integration, though, one should use the PKCS#11 driver.
The following code has been tested using the Utimaco SecurityServer HSM simulator. By configuring the Utimaco PKCS#11 configuration (in particular the
cs_pkcs11_R2.cfg configuration file) accordingly, the code should run with actual Utimaco HSMs.
You can easily sign PDFs using Utimaco HSMs card via PKCS#11. E.g. in Java by initializing the
Pkcs11Signature from Using a PKCS#11 Security Provider like this:
For RSASSA-PSS signatures it suffices to initialize the
With iText version 7 you still needed an
IExternalSignatureContainer implementation for RSASSA-PSS signatures, you could use
Pkcs11SignatureContainer here and initialize it like this:
In .NET you can initialize the
Pkcs11Signature from "How to Address PKCS#11 Drivers in .NET" above like this: