Reputation: 417
I try to perform some ECDH key agreement, using the IAIK PKCS#11 wrapper (https://jce.iaik.tugraz.at/sic/Products/Core_Crypto_Toolkits/PKCS_11_Wrapper), explizitly NOT using the JCE provider. So far I did not find any example how to do so, especially setting up the key template and mechanism (and mechanism-parameters).
Do you have some example, how to perform this and verify the result, optimally using BouncyCastle?
thank you!
Upvotes: 2
Views: 1588
Reputation: 417
In the end, I made it work on my own.
First be aware, that the IAIK PKCS#11 wrapper does not support all key derivation functions of PKCS#11. DHKeyDerivationParameters.KeyDerivationFunctionType specifies what it supports and sadly, although you provide a long, it checks if the value is known, so you can not simply provide the values defined for other KDF functions. Still, if your PKCS#11-module supports it, you can use DHKeyDerivationParameters.KeyDerivationFunctionType.NULL and do the derivation on your own.
For the following to snippets let session be some iaik.pkcs.pkcs11.Session, which is properly authenticated to use the selected ECDH key.
Do the following to derive a key, in this case for AES (2Des and 3DES or other AES lengths are mostly the same):
final long CKA_VALUE_LEN = 0x00000161;
byte[] deriveKey(byte[] publicKey, byte[] salt, long keyDerivationFunction) throws Exception {
// setting up mechanism:
EcDH1KeyDerivationParameters params = new EcDH1KeyDerivationParameters(keyDerivationFunction, salt, publicKey);
Mechanism mechanism = Mechanism.get(PKCS11Constants.CKM_ECDH1_DERIVE );
mechanism.setParameters(params);
// setting up keyTemplate, specifying how the derived key looks like:
Key keyTemplate = new AESSecretKey();
keyTemplate.putAttribute(CKA_VALUE_LEN, new Long(32));
AESSecretKey derivedKey = ((AESSecretKey)session.deriveKey(mechanism, key, keyTemplate));
return derivedKey.getValue().getByteArrayValue();
}
To retrieve the plain ECDH shared secret, proceed as follows:
byte[] getSharedSecret(byte[] publicKey) throws Exception{
// setting up mechanism:
EcDH1KeyDerivationParameters params = new EcDH1KeyDerivationParameters(DHKeyDerivationParameters.KeyDerivationFunctionType.NULL, null, publicKey);
Mechanism mechanism = Mechanism.get(PKCS11Constants.CKM_ECDH1_DERIVE );
mechanism.setParameters(params);
// four our PKCS#11 module, using a GenericSecretKey without length returns
// the complete derived secret:
Key keyTemplate = new GenericSecretKey();
GenericSecretKey derivedKey = ((GenericSecretKey)session.deriveKey(mechanism, key, keyTemplate));
return derivedKey.getValue().getByteArrayValue();
}
To perform the 'other' side and validate, that the derived value is as expected, you can use BouncyCastle and the following code:
void testKeyDerivation(ECPublicKey otherPublic, byte[] salt) throws Exception{
// create some keypair, which fits to the EC key, IAIK is using:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDH", BouncyCastleProvider.PROVIDER_NAME);
keyGen.initialize(otherPublic.getParams());
KeyPair testKeyPair = keyGen.generateKeyPair();
ECPublicKey publicTestKey = (ECPublicKey) testKeyPair.getPublic();
// convert the JCE Publickey to the required format, using BouncyCastle:
byte[] encodedPublicTestKey = EC5Util.convertPoint(publicTestKey.getParams(), publicTestKey.getW(),false).getEncoded(false);
// format is 0x04 X Y where X and Y are byte[], containing the (padded) coordinates of the point,
// specifying the public key
// in fact, you need to do only one of these, but I want to show, how both works:
byte[] iaikDerivedKey = deriveKey(encodedPublicTestKey, salt, DHKeyDerivationParameters.KeyDerivationFunctionType.SHA1_KDF);
byte[] iaikDerivedSecret = getSharedSecret(encodedPublicTestKey);
// verify that both sides indeed agree:
KeyAgreement ka = KeyAgreement.getInstance("ECDH", BouncyCastleProvider.PROVIDER_NAME);
ka.init(testKeyPair.getPrivate());
ka.doPhase(otherPublic, true);
byte [] secret = ka.generateSecret();
Assert.assertTrue(Arrays.equals(iaikDerivedSecret, secret));
Digest digest = new SHA1Digest();
KDF2BytesGenerator kdf = new KDF2BytesGenerator(digest);
DerivationParameters derivationParameters = new KDFParameters(secret,salt);
kdf.init(derivationParameters);
byte[] derivedKey = new byte[iaikDerivedKey.length];
kdf.generateBytes(derivedKey, 0, iaikDerivedKey.length);
Assert.assertTrue(Arrays.equals(iaikDerivedKey, derivedKey));
}
This does work for me with IAIK PKCS#11 Wrapper Version 1.5 and BouncyCastle Version 1.59, using my companies PKCS#11 Middleware and some Smartcard. I hope it also helps somebody else, trying to do the same.
Upvotes: 2