2

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!

Daniel Heldt
  • 417
  • 5
  • 15

1 Answers1

2

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.

Daniel Heldt
  • 417
  • 5
  • 15
  • Hi @Daniel, getting a Shared Secret seems to be the same as deriving a key? I noticed that you are deriving the public key and not the private key? How it's possible that you are getting the private key from the public key: ka.init(publicTestKey.getPrivate());? It's not possible. Thank you. – Ahmed MANSOUR Mar 11 '19 at 09:01
  • How can we perform a Key Agreement with a Private Key (EC) stored in the HSM using Bouncy Castle? – Ahmed MANSOUR Mar 11 '19 at 17:16
  • @AhmedMANSOUR: session is able to use the private key, so if I hand in the public one, it takes one private and one public key to compute a shared secret. This shared secret is used to derive an AES key. – Daniel Heldt Mar 12 '19 at 10:23
  • You cannot to a key agreement with a private key in the HSM in bouncycastle. The private key is in the HSM and will (hopefully) never leave it, so bouncycastle can not use it. – Daniel Heldt Mar 12 '19 at 10:23
  • Thank you for your answer,ka.init(publicTestKey.getPrivate()) in your code is not workinf, there is no getPrivate method in ECPublicKey. – Ahmed MANSOUR Mar 12 '19 at 17:03
  • Thank you. I changed it to testKeyPair.getPrivate(), which is probably right... I can not test it, since I do not have the corresponding toolchain at my hand any more... Does it work that way? – Daniel Heldt Mar 14 '19 at 07:30