- changed status to open
MacSigner with secret key from AWS cloudHSM not usable.
My goal is to create a (detached) jws signature using a symmetric key from an aws cloud hsm.
The MacSigner puts the SecretKey into a SecretKeySpec
(see HMAC.java line 94).
This leads to an error within the aws cloudHSM JCE provider, because this needs a key as loaded from the keystore provided for Mac.init()
.
Library versions used:
- nimbus-jose-jwt:9.31
- aws cloudHSM JCE: cloudhsm-jce:5.10.0
- Java 17: Corretto-17.0.8.8.1
Kotlin-Code to reproduce this error with Nimbus MacSigner:
if (Security.getProvider(CloudHsmProvider.PROVIDER_NAME) == null) {
Security.addProvider(CloudHsmProvider())
}
// Load the HSM as a Java crypto provider
val hsmProvider: Provider = Security.getProvider(CloudHsmProvider.PROVIDER_NAME)
// Get a handle to the private key for signing
val hsmKeyStore: KeyStore = KeyStore.getInstance(CloudHsmProvider.CLOUDHSM_KEYSTORE_TYPE, hsmProvider)
hsmKeyStore.load(null)
val secretKey = hsmKeyStore.getKey("KEY-ID", "".toCharArray()) as SecretKey
val signer = MACSigner(secretKey)
signer.jcaContext.provider = hsmProvider
// prepare header and payload
val header = JWSHeader
.Builder(JWSAlgorithm.HS256)
.base64URLEncodePayload(false)
.criticalParams(setOf("b64"))
.keyID("KEY-ID)
.build()
val payload = Payload("This is the signed payload.")
val jws = JWSObject(header, payload)
jws.sign(signer) // <-- Exception
val isDetached = true
val token = jws.serialize(isDetached)
The above code snippet throws the following exception, when JWSObject.sign() is called:
Invalid HMAC key: The provided key is an instance of class javax.crypto.spec.SecretKeySpec when it should be of class com.amazonaws.cloudhsm.jce.provider.GenericSecretKey
com.nimbusds.jose.JOSEException: Invalid HMAC key: The provided key is an instance of class javax.crypto.spec.SecretKeySpec when it should be of class com.amazonaws.cloudhsm.jce.provider.GenericSecretKey
at com.nimbusds.jose.crypto.impl.HMAC.getInitMac(HMAC.java:65)
at com.nimbusds.jose.crypto.impl.HMAC.compute(HMAC.java:118)
at com.nimbusds.jose.crypto.impl.HMAC.compute(HMAC.java:94)
at com.nimbusds.jose.crypto.MACSigner.sign(MACSigner.java:193)
at com.nimbusds.jose.JWSObject.sign(JWSObject.java:315)
The following code snippet demonstrates the java crypto api calls issued by Nimbus, which lead to this error, and an alternative call sequence which does not wrap the secret inside a SecretKeySpec which works fine with AWS cloudHSM JCE provider:
val message = "The message.".toByteArray(StandardCharset.UTF_8)
if (Security.getProvider(CloudHsmProvider.PROVIDER_NAME) == null) {
Security.addProvider(CloudHsmProvider())
}
val hsmProvider: Provider = Security.getProvider(CloudHsmProvider.PROVIDER_NAME)
val hsmKeyStore: KeyStore = KeyStore.getInstance(CloudHsmProvider.CLOUDHSM_KEYSTORE_TYPE, hsmProvider)
hsmKeyStore.load(null)
val secretKey = hsmKeyStore.getKey("KEY-ID", "".toCharArray()) as SecretKey
val alg = "HMACSHA256"
// this works:
val macOk : Mac = Mac.getInstance(alg, hsmProvider)
macOk.init(secretKey)
val resultOk = macOk.doFinal();
println("result = $resultOk")
// this not:
val secretKeySpec = javax.crypto.spec.SecretKeySpec(secretKey.getEncoded(), alg)
val mac : Mac = Mac.getInstance(secretKeySpec.getAlgorithm(), hsmProvider)
mac.init(secretKeySpec) // <----java.security.InvalidKeyException: The provided key is an instance of class javax.crypto.spec.SecretKeySpec when it should be of class com.amazonaws.cloudhsm.jce.provider.GenericSecretKey
mac.update(message)
val result = mac.doFinal();
println("result = $result")
Is there a way to use a Nimbus MacSigner with a key from AWS cloudHSM?
Is the wrapping of the SecretKey into a SecretKeySpec required?
If not, I suggest that nimbus uses the SecretKey directly without wrapping it when performing a MacSinger operation.
Comments (17)
-
-
reporter Yes, testing is possible.
-
reporter Hi Vladimir, thank you for your fast response.
I currently do not understand your remark on the support of the JCE provider.
There is this document section, which describes the use of the SunPKCS11 jce provider together with nimbus-jose-jwt:
https://connect2id.com/products/nimbus-jose-jwt/examples/pkcs11AWS cloudHSM provides two different apis for the Java platform:
- The JCE provider, which I used. This provider from the current sdk v5 is recommended for containerized workloads.
- The PKCs
library, which in turn can be used together with the SunPKCS11 provider (in theory).#11
I also tried using the PKCS11 api together with the SunPKCs11 provider instead of the cloudHSM JCE provider, but I did not get it to work.
So from my point of view, I currently do NOT apply the PKCS
api in the above example. I do not know, what the JCE provider does internally.#11
-
reporter - attached allow-aws-hsm-for-mac.patch
The attached patchfile contains the changes, which made the example code work.
base of the patch is the current master version (9.32.0-SNAPSHOT) as of commit 0ada4dbe00b2bfd7bc1d2db3ab08c76961d07a87
-
Thanks Ulrich, this makes my job easier
-
When the
getEncoded()
is called on the AWS HSMSecretKey
what gets returned / thrown?When I wrote about the HMAC facility previously not being PKCS
#11provider capable, I meant “HSM” capable. The underlying JCE provider can be SunPKCs11 or Amazon’s. -
Committed refactored HMAC class for PKCS
#11providers: b3072a0078dee62975bacf6f2a1404735a8d52b8 -
Note to self:
The
MACProvider.getSecret()
andgetSecretString()
methods, JavaDocs and tests need to be reworked depending on what the AWS HSMSecretKey.getEncoded()
returns or throws.The
SecretKey.getEncoded()
contract says JCE providers that do not or cannot return the key material must returnnull
. -
- changed component to Crypto package
-
reporter When the
getEncoded()
is called on the AWS HSMSecretKey
what gets returned / thrown?The AWS CloudHSM does not throw any exception but just returns
null
One can enable to extract keys from the HSM, but this is not intended, as a purpose of the HSM is to keep the secret keys save.
I suggest that the nimbus lib never calls getEncoded() for any SecretKey, because otherwise it still cannot be used with a key from an HSM.
The drawback is, that then the size of the secret cannot be determined.
I think, that the nimbus lib should just assume that the key has the required size and that the Mac operation throws an error, if the key is too small for the algorithm chosen.
-
Updated the
MACProvider
andMACSigner
classes: 24c99d94913e7b4233ba1ca4e2851ac0c19c1d76Thanks for verifying the
SecretKey.getEncoded
output. I reintroduced the key length checking when the key material is available. This is to give developers clear guidance when the key material is available and not compliant, rather than ending up asking questions here -
reporter Thanks very much for this quick solution.
Let me know, when there is a (SNAPSHOT) release to be tested against the hsm. -
The update was just pushed out into Maven Central as
version 9.33 (2023-09-14) * Updates the MACSigner to support JCE providers, such as PKCS#11 providers or Amazon's CloudHSM, that don't expose the underlying SecretKey material (iss #520). * Refactors the HMAC class to support PKCS#11 providers. * Adds new MACProvider.MACProvider(SecretKey,Set<JWSAlgorithm>) constructor. * The MACProvider.getSecret and getSecretString methods will return null when the provider was constructed with a SecretKey that doesn't expose its key material.
I want to complete the MACVerifier next. Would you be able to test the HSxxx verification with the Amazon CloudHSM was well?
-
reporter Using
nimbus-jose-jwt:9.33
both
MACSigner
andMACVerifier
are working fine with a 256 bit long generic secret from the AWS CloudHSM but only as long as the HSM is configured to allow extraction of the encoded secrets.When extracting secrets is disabled, I get the following exception:
Cannot read the array length because the return value of "com.nimbusds.jose.crypto.MACSigner.getSecret()" is null com.nimbusds.jose.JOSEException: Cannot read the array length because the return value of "com.nimbusds.jose.crypto.MACSigner.getSecret()" is null at com.nimbusds.jose.JWSObject.sign(JWSObject.java:346) ... Caused by: java.lang.NullPointerException: Cannot read the array length because the return value of "com.nimbusds.jose.crypto.MACSigner.getSecret()" is null at com.nimbusds.jose.crypto.MACSigner.sign(MACSigner.java:203) at com.nimbusds.jose.JWSObject.sign(JWSObject.java:315) ... 424 more
Sorry that I did not check this before.
My patch has the same error.
-
It’s not a useful HSM if the secret extraction has to be enabled
The bug revealed in your testing of the
MACSigner
was located and fixed: c695b11The
MACVerifier
should also be capable now of dealing withSecretKey
instances that don't expose their key material: 45f15d1Let me know how the tests work out with this new release:
version 9.34 (2023-09-14) * Updates the MACVerifier to support JCE providers, such as PKCS#11 providers or Amazon's CloudHSM, that don't expose the underlying SecretKey material (iss #520). * Fixes the MACSigner.sign method for SecretKey instances that don't expose their key material (iss #520). * Deprecates HMAC.compute(String,byte[],byte[],Provider), use HMAC.compute(String,SecretKey,byte[],Provider) instead.
-
reporter version 9.34 works fine with disabled key extraction..
-
- changed status to resolved
Hallelujah!
- Log in to comment
Hi Ulrich,
The HMAC facility is not designed to work with PKCE
JCE providers.#11We'll check what can be done about the that. Hopefully the required changes will be minimal.
Will you be able to test the code after it's updated? At present we don't have a way to test HMAC in PKCS
. Only RSA and EC operations.#11