NitrokeyHSM and Java Sun PKCS#11

Hi,

I am trying to test the NitrokeyHSM with our Java application to ensure compatibility. I am using the OpenSC 0.17.0 PKCS#11 driver with the Java Sun PKCS#11 interface (JDK 8 PKCS#11 Reference Guide). I have not performed any custom configuration of either - just setup the PKCS#11 configuration pointing at the opensc-pkcs11.so and using slot 0.

I am running into difficulties performing anything other than basic RSA decryption or RSA/ECDSA signing using the card in this setup. For example, ECDH key agreement always fails with CKR_KEY_TYPE_INCONSISTENT. Also, attempting to use RSA for key wrapping/unwrapping (for hybrid encryption using AES) also fails during the unwrap step. Can somebody let me know if I am doing something wrong or if these functions are just not supported using the PKCS#11 driver?

If I use the custom JCE provider from Smart Card Shell then ECDH key agreement works as expected using exactly the same code. However, RSA unwrap always fails with both providers.

Here is the output using the Sun PKCS#11 provider (source code follows):
Loading keystore
Loading static ECDH keys
Static Private: SunPKCS11-NitrokeyHSM EC private key, 256 bits (id 140678806112736, token object, sensitive, unextractable)
Static Public : Sun EC public key, 256 bits
public x coord: 23919995184559860976253713360680914112848079777551416149635514637024520745309
public y coord: 17120898397917041930631477206293964934850084751907000036557497273114856567487
parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)
Generating ephemeral ECDH keys
Ephemeral Priv: SunPKCS11-NitrokeyHSM EC private key, 256 bits (id 140678806192720, token object, not sensitive, unextractable)
Ephemeral Pub : SunPKCS11-NitrokeyHSM EC public key, 256 bits (id 140678806226736, session object)
public x coord: 97449259143949072573844538684056027183607805363410967416942108362277540249769
public y coord: 115640378721741984368022032270838032861978611587357263759325321358099294564788
parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)
Performing ECDH key agreement
java.security.ProviderException: Could not derive key
at sun.security.pkcs11.P11ECDHKeyAgreement.engineGenerateSecret(P11ECDHKeyAgreement.java:144)
at javax.crypto.KeyAgreement.generateSecret(KeyAgreement.java:586)
at hsm.main(hsm.java:52)
Caused by: sun.security.pkcs11.wrapper.PKCS11Exception: CKR_KEY_TYPE_INCONSISTENT
at sun.security.pkcs11.wrapper.PKCS11.C_DeriveKey(Native Method)
at sun.security.pkcs11.P11ECDHKeyAgreement.engineGenerateSecret(P11ECDHKeyAgreement.java:133)
… 2 more
Loading RSA keys
Wrapping AES key
Unwrapping AES key
Exception in thread “main” java.security.InvalidKeyException: unwrap() failed
at sun.security.pkcs11.P11RSACipher.engineUnwrap(P11RSACipher.java:536)
at javax.crypto.Cipher.unwrap(Cipher.java:2550)
at hsm.main(hsm.java:73)
Caused by: sun.security.pkcs11.wrapper.PKCS11Exception: CKR_FUNCTION_NOT_SUPPORTED
at sun.security.pkcs11.wrapper.PKCS11.C_UnwrapKey(Native Method)
at sun.security.pkcs11.P11RSACipher.engineUnwrap(P11RSACipher.java:527)
… 2 more

With the Smart Card Shell JCE provider then ECDH succeeds and RSA unwrap fails with the following error:

Exception in thread "main" java.lang.UnsupportedOperationException
at de.cardcontact.smartcardhsmprovider.SmartCardHSMCipher.engineSetMode(SmartCardHSMCipher.java:183)
at javax.crypto.Cipher$Transform.setModePadding(Cipher.java:374)
at javax.crypto.Cipher.chooseProvider(Cipher.java:862)
at javax.crypto.Cipher.init(Cipher.java:1249)
at javax.crypto.Cipher.init(Cipher.java:1186)
at hsm.main(hsm.java:72)

The source code I am using (with Java 1.8):

import java.security.*;
import java.security.interfaces.*;
import javax.crypto.*;
import javax.crypto.spec.*;

import java.util.*;
import javax.xml.bind.DatatypeConverter;

public class hsm {

    public static void main(String...args) throws Exception {
        if (args.length != 1) {
            System.err.println("Usage: java hsm <pin>");
            System.exit(1);
        }
        char[] pin = args[0].toCharArray();

        Security.addProvider(new de.cardcontact.smartcardhsmprovider.SmartCardHSMProvider());
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        
        // Load the HSM as a PKCS#11 keystore
        System.out.println("Loading keystore");
        KeyStore hsm = KeyStore.getInstance("SmartCardHSMKeyStore");
        //KeyStore hsm = KeyStore.getInstance("PKCS11");
        hsm.load(null, pin);
        Arrays.fill(pin, ' ');

        // Load an EC P-256 private key from the keystore
        System.out.println("Loading static ECDH keys");
        Key staticPrivateKey = hsm.getKey("p256", new char[0]); // No password
        Key staticPublicKey = hsm.getCertificate("p256").getPublicKey();

        System.out.println("Static Private: " + staticPrivateKey);
        System.out.println("Static Public : " + staticPublicKey);

        // Generate an ephemeral key-pair on the device
        System.out.println("Generating ephemeral ECDH keys");
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", hsm.getProvider());
        kpg.initialize(((ECPublicKey) staticPublicKey).getParams()); // Will default to P-256 curve
        KeyPair ephemeralKeys = kpg.generateKeyPair();

        System.out.println("Ephemeral Priv: " + ephemeralKeys.getPrivate());
        System.out.println("Ephemeral Pub : " + ephemeralKeys.getPublic());

        // Now perform ECDH key agreement between static private and ephemeral public keys
        System.out.println("Performing ECDH key agreement");
        KeyAgreement ecdh = KeyAgreement.getInstance("ECDH", hsm.getProvider());
        ecdh.init(staticPrivateKey);
        ecdh.doPhase(ephemeralKeys.getPublic(), true);

        try {
            byte[] sharedSecret = ecdh.generateSecret();
            System.out.println("Derived shared secret: " + DatatypeConverter.printHexBinary(sharedSecret));
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Try wrapping/unwrapping
        System.out.println("Loading RSA keys");
        Key rsaPrivateKey = hsm.getKey("jwtEncRsa", new char[0]);
        Key rsaPublicKey = hsm.getCertificate("jwtEncRsa").getPublicKey();

        Key aesKey = new SecretKeySpec(new byte[16], "AES");

        System.out.println("Wrapping AES key");
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.WRAP_MODE, rsaPublicKey);
        byte[] wrapped = cipher.wrap(aesKey);

        System.out.println("Unwrapping AES key");
        cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.UNWRAP_MODE, rsaPrivateKey);
        Key unwrappedKey = cipher.unwrap(wrapped, "AES", Cipher.SECRET_KEY);

        System.out.println("Unwrapped key: " + unwrappedKey);
    }
}

Another issue I have when using the Sun PKCS#11 provider is that the code to generate the ephemeral key pair (KeyPairGenerator) creates the keys permanently on the device, even though I did not save them to the KeyStore (and they are not visible through the KeyStore). This causes the device to eventually run out of memory. There seems to be no way to cleanup these ephemeral keys in Java and I have to resort to manually deleting them using pkcs11-tool. The Smart Card Shell provider does not leave such keys behind, so seems much better in that regard. There is no reason for these ephemeral keys to exist on the device, so I will just create the ephemeral keys in-memory instead (I can just drop the second argument to the KeyPairGenerator.getInstance() call), but I thought it was worth mentioning.

This issue is rooted to OpenSC. Please collect a debug trace from OpenSC (Linux: start with
OPENSC_DEBUG=9 in front; Windows: Activate logging in opensc.conf) and submit it with a new issue ticket.

Regarding JCE, we will come back to you.

1 Like

Thanks, I will raise a ticket with OpenSC

I have raised an issue for the ECDH problem with OpenSC. I found in the source code that the C_WrapKey and C_UnwrapKey functions are completely not supported in OpenSC PKCS#11 driver, so that is why the RSA unwrap does not work.

2 Likes

The OpenSC ticket is now closed. In case anyone else runs into this, it was my error - the key I was using did not have the derive flag set (only sign) so was quite reasonably being rejected by the driver as not suitable for ECDH.

2 Likes