I want to wrap a private key out of a HSM, using an external EC key pair (master key) and then verify that I can recover it.
The wrapping occurs as follow:
CKM_ECDH1_DERIVE
. The derivation parameters for this mechanism are: a derivation function CKD_SHA256_KDF
, shared data and public data (public data are taken from the public EC master key).CKM_AES_GCM
, CKM_AES_KEY_WRAP_PAD
or CKM_AES_CBC_PAD
.Then I would like to verify if the wrapped private is the expected one.
I know how to decrypt the private key once I have recovered the secret key used ot protect it. Because it is not like RSA, I have to derive the same secret key using some elements I have, but I don't know how to do this with BC.
I'm trying to use something like this, trying to find an concrete implementation of AlgorithmParamSpec
:
KeyAgreement agreement = KeyAgreement.getInstance("ECCDHwithSHA256CKDF", "BC");
agreement.init(externalEcMasterKey.getPrivate(), someAlgorithmParamSpec);
agreement.doPhase(internalEcKeyPair.asJavaPublic(), true);
SecretKey agreedKey = agreement.generateSecret("AES[256]");
Unfortunately, with UserKeyingMaterialSpec
for example, it returns a different key at each time, which is not what I want :)
Thanks in advance
In the HSM response, I have:
transportPublicKey
;privateKeyWrappedBySessionKey
;To decrypt the private key:
CKM_ECDH1_DERIVE
and the derivation function CKD_SHA256_KDF
, the private part of the EC master key, shared data (which can be null
) and the public key of the ephemeral EC transport key: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);
Additional information:
CKD_NULL
derivation function, AlgorithmParameterSpec
MUST NOT be provided, as stated in PKCS#11(§ 2.3.20) With the key derivation function CKD_NULL, pSharedData must be NULL and ulSharedDataLen must be zero.
recipientAgreement.init(recipientPrivateKey);
(§ 2.3.20) ... and gets the first ulAESKeyBits bits of the derived key to be the temporary AES key.
CKM_AES_KEY_WRAP
or CKM_AES_KEY_WRAP_PAD
mechanisms, no initialization vector is required:cipher.init(Cipher.DECRYPT_MODE, sharedSecret);
CKD_NULL
and session key size other than 256 bits.