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);
}
}