In contexts with higher security requirements the private keys usually are not available in local key stores but instead in external devices which are hardened against attempts to copy the private keys (e.g. smart cards or HSMs) or even in remote servers. To sign data with such private keys you have to use an API provided by the manufacturer of the device or the operator of the service respectively.
Such APIs may follow some standard. E.g.
- PKCS#11 drivers - the PKCS#11 standard defines a platform-independent API to cryptographic tokens.
- Java security providers / MS Windows CryptoAPI or CNG key storage providers - the Java and MS Windows platforms offer their specific security modules which are easy to access from Java and .NET respectively. These modules allow plugging in external devices and remote services via a provider architecture.
But these APIs may also be completely proprietary.
The manufacturer/operator actually might offer access via multiple APIs, e.g., they might offer both a platform independent PKCS#11 driver and platform specific providers. Be aware though, that this does not mean that you can freely switch back and forth between them. In particular individual keys or information associated with them might only be accessible via the API which was used to create or add them in the first place. So if you plan to implement an application on multiple platforms in parallel, you should use the same platform independent API in all implementations, not the (probably easier-to-use) respective platform specific ones.
In this part we take a look at the platform specific providers and proprietary APIs. The next part will focus on PKCS#11 drivers.
Signing With External Services (proprietary APIs)
In particular in the context of external signing services you often find proprietary web APIs. Some of them want the original data to sign as input, some want a hash of that data as-is, some want that hash packaged in a specific data structure. Some of them return naked signature bytes, some a full signature container. Some merely require a normal username-password authorization, some require a two-factor authorization. Etc.
Thus, one can hardly provide any general rules here. Merely that based on the signature structure returned by the API in question you need to implement either
IExternalSignatureContainer. And that you should study the documentation of the API to know exactly what to provide and what to expect in return.
As an example you might want to take a look at the technical note “Using iText 7 and AWS KMS to digitally sign a PDF document” which explains how to use the Key Management Service (KMS) of the Amazon Web Services (AWS) to sign PDFs.
Another example is the usage of Singapore's National Digital identity (NDI) project to create PDF signatures with iText. You can request example code and documentation here. (Beware, that example code is based on an earlier version of NDI; thus, it most likely won’t work out of the box with the current NDI.)
Signing With Custom JCA/JCE Security Providers (Java)
If a security token manufacturer or a signing service operator provides a Java security provider for signing, you’re essentially in the situation of section “Signing with Java JCA/JCE keys” of Part I except that you may have to do some configuration beforehand (please consult the documentation of the token or service).
Utimaco, for example, provides a Java security provider to access their HSMs. Using this provider one can sign PDFs as follows:
(Full code in test method
testSignSimpleGeneric in <https://git.itextsupport.com/projects/I7JS/repos/signing-examples/browse/jce-utimaco/src/test/java/com/itextpdf/signingexamples/jce/utimaco/TestSignSimple.java>)
For RSASSA-PSS signatures (which as we recall cannot be created using
IExternalSignature implementations) an
IExternalSignatureContainer implementation is required. As the Utimaco security provider uses standard JCA/JCE classes, it would be natural to assume that the
PrivateKeySignatureContainerBC mentioned in the first part section Signing with Java JCA/JCE keys could be used as-is, requiring at most an option to indicate the provider instance to use.
Unfortunately it turns out that the situation is not that easy and the two cryptographic components involved communicate past each other:
- The Utimaco provider (at least in the version I have) for RSASSA-PSS signature creation only accepts regular SHAxxxWITHRSA signature algorithm names in combination with a PSSParameterSpec as AlgorithmParameterSpec parameter. In particular it does not accept SHAxxxWITHRSAANDMGF1 algorithm names.
- The BouncyCastle CMS signature container building classes (as used in
PrivateKeySignatureContainerBC) on the other hand take the requested signature algorithm name and parameters and transform them to and fro. As a consequence the private key provider is eventually asked for a signature for SHAxxxWITHRSAANDMGF1 no matter whether one originally used SHAxxxWITHRSA or SHAxxxWITHRSAANDMGF1.
Thus, we create a custom
IExternalSignatureContainer implementation which replaces the BouncyCastle class doing that to and fro transformation with its own one that doesn’t. The BouncyCastle class in question is a ContentSigner generated by JcaContentSignerBuilder. So we build our own ContentSigner:
UtimacoJceSignatureContainer.java; full code at <https://git.itextsupport.com/projects/I7JS/repos/signing-examples/browse/jce-utimaco/src/main/java/com/itextpdf/signingexamples/jce/utimaco/UtimacoJceSignatureContainer.java>)
If you set the algorithm in this way, you can sign with RSASSA-PSS like this:
(Full code in test method
testSignSimpleUtimacoJceSignatureContainerRsaSsaPss in <https://git.itextsupport.com/projects/I7JS/repos/signing-examples/browse/jce-utimaco/src/test/java/com/itextpdf/signingexamples/jce/utimaco/TestSignSimple.java>)
Signing With Custom CNG Integrations (.NET)
If a security token manufacturer or a signing service operator provides a .NET CryptoAPI or CNG key storage provider for signing, you’re essentially in the situation of the “Signing with .NET CryptoAPI/CNG Keys” section of Part I except that you may have to do some configuration beforehand (please consult the documentation of the token or service).
Utimaco, for example, provides a CNG key storage provider to access their HSMs. Let’s assume you have associated a certificate in your personal certificate store with a Utimaco CNG key using "
Utimaco CNG Signing Test" as the subject common name.
X509Certificate2Signature class from part I one can then sign PDFs like this:
(Full test code in method
TestCngSignSimpleGeneric in <https://git.itextsupport.com/projects/I7NS/repos/samples/browse/itext/itext.publications/itext.publications.signing-examples.cng-utimaco-test/iText/SigningExamples/CngUtimaco/TestSignSimple.cs>)
Using the Utimaco provider with the
X509Certificate2SignatureContainer class from Part I you can sign PDFs like this:
(Full test code in method
TestCngSignSimpleGenericContainer in <https://git.itextsupport.com/projects/I7NS/repos/samples/browse/itext/itext.publications/itext.publications.signing-examples.cng-utimaco-test/iText/SigningExamples/CngUtimaco/TestSignSimple.cs>)