I have been attempting to use the Nitrokey HSM with Java as a proof of concept for integration into some commercial devices, but without much luck.
If I instantiate a SunPKCS11 provider like this:
String pkcs11Config = "name = Nitrokey\n" + "library = \"C:/Program Files/OpenSC Project/OpenSC/pkcs11/opensc-pkcs11.dll\"\n" + "slot = 2\n"; byte pkcs11ConfigBytes = pkcs11Config.getBytes(); ByteArrayInputStream pkcs11ConfigStream = new ByteArrayInputStream(pkcs11ConfigBytes); Provider pkcs11Provider = new sun.security.pkcs11.SunPKCS11(pkcs11ConfigStream); Security.addProvider(pkcs11Provider); pkcs11KeyStore = KeyStore.getInstance("PKCS11", pkcs11Provider);
Then the call to get a PKCS11 keystore instance fails with this exception:
java.security.NoSuchAlgorithmException: no such algorithm: PKCS11 for provider SunPKCS11-Nitrokey at sun.security.jca.GetInstance.getService(Unknown Source) at sun.security.jca.GetInstance.getInstance(Unknown Source) at java.security.Security.getImpl(Unknown Source) ... 4 more
Examining the resulting pkcs11Provider object, I can see that it has no registered SPIs for PKCS #11 services.
Set<Service> pkcs11Services = pkcs11Provider.getServices(); // empty set!
I saw in other threads that there was a reference to a JCE provider in the scsh package and I have tried using it. However, it’s clear that the implementation in OpenSC-Java (and contained in the scsh distribution) is incomplete and out of date.
If I instantiate the provider like this:
Provider pkcs11Provider = new PKCS11Provider("C:/Program Files/OpenSC Project/OpenSC/pkcs11/opensc-pkcs11.dll","Nitrokey"); Security.addProvider(pkcs11Provider);
Then I can get a keystore instance and create RSA keys. However, OpenSC-Java doesn’t have support for ECC keys, only RSA and DSA keys. There are some other odd limitations of the OpenSC-Java libraries. For example, I have not yet found a way to set the alias when creating a key pair. There are also several places where OpenSC-Java doesn’t implement SPI methods that Java 8 relies on.
For example, PKCS10.encodeAndSign fails with this exception:
java.lang.UnsupportedOperationException at java.security.SignatureSpi.engineGetParameters(Unknown Source) at java.security.Signature$Delegate.engineGetParameters(Unknown Source) at java.security.Signature.getParameters(Unknown Source) at sun.security.pkcs10.PKCS10.encodeAndSign(Unknown Source)
This is because PKCS11SignatureSpi doesn’t implement the getParameters method (which was added in Java 1.4).
Also, KeyStore.setCertificateEntry fails with an error from the native library:
org.opensc.pkcs11.wrap.PKCS11Exception: C_CreateObject failed. (CKR_GENERAL_ERROR) at org.opensc.pkcs11.wrap.PKCS11Object.createObjectNative(Native Method) at org.opensc.pkcs11.wrap.PKCS11Object.createObject(Unknown Source) at org.opensc.pkcs11.wrap.PKCS11Certificate.storeCertificate(Unknown Source) ... 6 more
I see that there are plenty of examples of certificate storage using the pkcs11-tool, so I don’t see why this would fail from Java.
Is there a way to get all this to work in Java? Or does the Nitrokey HSM rely solely on OpenSC-Java which is unsuitable and out of date?