In this post, I answered my own problem about unwrapping a private key which has been wrapped out of a HSM using an EC master key. It implies using a derivation mechanism and a derivation function to issue a AES session key.
The code to implement the unwrapping with Bouncy Castle is:
KeyAgreement recipientAgreement =
KeyAgreement.getInstance("ECDHWithSHA256KDF", BouncyCastleProvider.PROVIDER_NAME);
recipientAgreement.init(recipientPrivateKey,
new UserKeyingMaterialSpec(sharedData));
recipientAgreement.doPhase(transportPublicKey), true);
final byte[] secret = recipientAgreement.generateSecret();
SecretKeySpec sharedSecret = new SecretKeySpec(secret , "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING");
final InitializationVectorParameters initializationVectorParameters = new InitializationVectorParameters(new byte[16]);
byte[] ivb = initializationVectorParameters.getInitializationVector();
cipher.init(Cipher.DECRYPT_MODE, sharedSecret, new IvParameterSpec(ivb));
byte[] decryptedPrivateKey = cipher.doFinal(privateKeyWrappedBySessionKey);
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(decryptedPrivateKey);
KeyFactory keyFactory = KeyHelper.keyFactory(wrappedPrivateKeyType);
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
Unfortunately, if this works for almost all derivation functions (you just have to replace ECDHWithSHA256KDF
with the appropriate name like ECDHWithSHA1KDF
or whatever else), this does not work for CKD_NULL
and AES key size shorter than 256 bits.
I used ECDH
for the init of the KeyAgreement
, generate a secret and got the first 128 or 192 bits of this secret, for example:
KeyAgreement recipientAgreement =
KeyAgreement.getInstance("ECDH", BouncyCastleProvider.PROVIDER_NAME);
recipientAgreement.init(recipientPrivateKey);
(...)
final byte[] secret = recipientAgreement.generateSecret();
System.arraycopy(secret, 0, shortSecret, 0, 16);
SecretKeySpec sharedSecret = new SecretKeySpec(shortSecret , "AES");
(...)
but it fails at cipher.doFinal(privateKeyWrappedBySessionKey)
with:
Cipher initialization exception: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
It seems obviously that the issued secret is not the expected one
Can anyone help me?
Please note that, with function such as CKD_SHA256_KDF
and session key size of 128 or 192 bits, I can succesfully unwrap my key.