Nitrokey HSM and Java

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?

There is a native JCE-Provider available for the SmartCard-HSM. The signed jar is included in the Smart Card Shell distribution and source code is available in the CDN.

The SunPKCS#11 JCEProvider is not a reliable piece of software, which is why opensc-java was developed. We maintain a version of opensc-java that is also included in the Smart Card Shell.

If you happen to use Apache Ivy for dependency management, then you could also obtain jars from our IVY repo.

I have been using the signed jar from the scsh3.17.453 distribution.

I can see from the source code at https://github.com/CardContact/opensc-java/blob/master/java/src/org/opensc/pkcs11/spi/PKCS11SignatureSpi.java that this implementation also does not implement the engineGetParameters method which is invoked by PKCS10.encodeAndSign.

Do you have any other advice?

Seems to me that sun.security.pkcs10.PKCS10 is deprecated. I can’t find it in Java 11.

Maybe you should try Bouncycastle to build a PKCS10 structure.

I’m currently using Java 15. And it still seems odd to me that OpenSC-Java would not implement
SignatureSpi.engineGetParameters since that is a defined interface method.

But regardless, attempting to create a PKCS10 object with Bouncy Castle fails with this exception:

Exception in thread "main" java.lang.IllegalArgumentException: unknown object in getInstance: org.bouncycastle.asn1.ASN1Integer
    at org.bouncycastle.asn1.ASN1Sequence.getInstance(ASN1Sequence.java:105)
    at org.bouncycastle.asn1.x509.AlgorithmIdentifier.getInstance(AlgorithmIdentifier.java:34)
    at org.bouncycastle.asn1.x509.SubjectPublicKeyInfo.<init>(SubjectPublicKeyInfo.java:80)
    at org.bouncycastle.asn1.x509.SubjectPublicKeyInfo.getInstance(SubjectPublicKeyInfo.java:43)
    at org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder.<init>(Unknown Source)

I have used this method with other PKCS #11 implementations without this issue, but I need to take a closer look to see if this is something I can work around.

In the meantime, do you have any advice about ECC support. The Nitrokey HSM apparently supports ECC key pairs, but the OpenSC-Java libraries don’t have wrappers for this key type. Any suggestions?

That is why I recommended to use the native JCE Provider which does not need a PKCS#11 module.

It talks to the HSM via OCF and supports all the neat feature of the SmartCard-HSM, including RSA, ECC, AES, PKA, Key Domains.

As we use the provider in a number of projects, it’s well supported.

OCF doesn’t seem to play well with Java 15 due to its use of disabled crypto primitives.

If I revert back to Java 8, it seems like I can make a little more progress, but the keystore load fails while attempting to initiate secure messaging:

java.security.SignatureException: Could not verify signature
    at sun.security.ec.ECDSASignature.engineVerify(ECDSASignature.java:414)
    at java.security.Signature$Delegate.engineVerify(Unknown Source)
    at java.security.Signature.verify(Unknown Source)
    at de.cardcontact.tlv.cvc.CardVerifiableCertificate.verify(CardVerifiableCertificate.java:663)
    at de.cardcontact.tlv.cvc.CardVerifiableCertificate.verify(CardVerifiableCertificate.java:635)
    at de.cardcontact.opencard.service.smartcardhsm.SmartCardHSMCardService.getDevAutPK(SmartCardHSMCardService.java:3690)
    at de.cardcontact.opencard.service.smartcardhsm.SmartCardHSMCardService.initSecureMessaging(SmartCardHSMCardService.java:703)
    at de.cardcontact.smartcardhsmprovider.SmartCardHSMProvider.cardInserted(SmartCardHSMProvider.java:620)
    at opencard.core.event.EventGenerator.createEventsForPresentCards(EventGenerator.java:140)
    at de.cardcontact.smartcardhsmprovider.SmartCardHSMProvider.checkCardState(SmartCardHSMProvider.java:460)
    at de.cardcontact.smartcardhsmprovider.SmartCardHSMProvider.getSmartCardHSMCardService(SmartCardHSMProvider.java:444)
    at de.cardcontact.smartcardhsmprovider.SmartCardHSMKeyStore.engineLoad(SmartCardHSMKeyStore.java:214)
    at java.security.KeyStore.load(Unknown Source)

Is there some sample code that shows the SmartCardHSMProvider in use with the Nitrokey HSM?

Yes, in the git Repo in the CDN you find a couple of examples.