- edited description
NullPointerException in HmacUsingShaAlgorithm.validateKey() in case of non-extractable key data
If an HMAC is used for JWT signing/verification, the verification throws a NullPointerException if the key data is not accessible via SecretKey.getEncoded() (e.g. because the key resides in a PKCS11 key store and is marked as non-extractable). In HSM-scenarios, keys are usually non-extractable - the implementation fails in those cases.
IMHO, the library should handle this internally by skipping the check if the required data is not available. An alternative is to require affected users to apply the workaround mentioned below. The latter strategy would effectively mean that the behavior must be configurable in software solutions using the library because the key might happen to be non-extractable at some point. In any case, it should not throw a NullPointerException. As for PKCS11, there is the attribute CKA_VALUE_LEN that is readable even for non-extractable keys. I'm not aware of a way to access that information in the JCE context, however.
HMAC-SHA-256 (extractable):
CKA_CLASS: CKO_SECRET_KEY
CKA_LABEL: test
CKA_ID: test
CKA_TOKEN: CK_TRUE
CKA_PRIVATE: CK_TRUE
CKA_MODIFIABLE: CK_TRUE
CKA_KEY_TYPE: CKK_GENERIC_SECRET
CKA_DERIVE: CK_FALSE
CKA_LOCAL: CK_TRUE
CKA_ENCRYPT: CK_FALSE
CKA_VERIFY: CK_TRUE
CKA_VERIFY_RECOVER: CK_FALSE
CKA_WRAP: CK_TRUE
CKA_TRUSTED: CK_FALSE
CKA_DECRYPT: CK_FALSE
CKA_SIGN: CK_TRUE
CKA_WRAP_WITH_TRUSTED: CK_FALSE
CKA_UNWRAP: CK_TRUE
CKA_SENSITIVE: CK_FALSE
CKA_ALWAYS_SENSITIVE: CK_FALSE
CKA_EXTRACTABLE: CK_TRUE
CKA_NEVER_EXTRACTABLE: CK_FALSE
CKA_CHECK_VALUE: 3 bytes (24 bits)
33 83 0e
CKA_VALUE_LEN: 32
CKA_VALUE: 32 bytes (256 bits)
6b 65 e8 59 10 28 bc ad 9a 1f c6 8e 30 dc c6 cc
92 45 21 64 4c 3a 5a 47 04 9b 05 da e1 cd 2d b8
HMAC-SHA-256 (non-extractable):
CKA_CLASS: CKO_SECRET_KEY
CKA_LABEL: test
CKA_ID: test
CKA_TOKEN: CK_TRUE
CKA_PRIVATE: CK_TRUE
CKA_MODIFIABLE: CK_TRUE
CKA_KEY_TYPE: CKK_GENERIC_SECRET
CKA_DERIVE: CK_FALSE
CKA_LOCAL: CK_TRUE
CKA_ENCRYPT: CK_FALSE
CKA_VERIFY: CK_TRUE
CKA_VERIFY_RECOVER: CK_FALSE
CKA_WRAP: CK_TRUE
CKA_TRUSTED: CK_FALSE
CKA_DECRYPT: CK_FALSE
CKA_SIGN: CK_TRUE
CKA_WRAP_WITH_TRUSTED: CK_FALSE
CKA_UNWRAP: CK_TRUE
CKA_SENSITIVE: CK_TRUE
CKA_ALWAYS_SENSITIVE: CK_TRUE
CKA_EXTRACTABLE: CK_FALSE
CKA_NEVER_EXTRACTABLE: CK_TRUE
CKA_CHECK_VALUE: 3 bytes (24 bits)
e0 40 84
CKA_VALUE_LEN: 32
CKA_VALUE: N/A (object is not extractable)
Workarounds:
Using JwtConsumer.setRelaxVerificationKeyValidation(), the key validation can be disabled.
Test case:
I attached a test case that illustrates the issue without PKCS11 dependencies. The strack trace:
Exception in thread "main" org.jose4j.jwt.consumer.InvalidJwtException: Unexpected exception encountered while processing JOSE object (java.lang.NullPointerException): JsonWebSignature{"alg":"HS256"}->eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0NDcyNzI5MTMsInN1YiI6InN1YmplY3QiLCJpc3MiOiJpc3N1ZXIifQ.ZwGoOYezN9TcqGFh1IjNbdCwFQuGMLVOr4h8CKyvVuM
at org.jose4j.jwt.consumer.JwtConsumer.processContext(JwtConsumer.java:223)
at org.jose4j.jwt.consumer.JwtConsumer.process(JwtConsumer.java:345)
at org.jose4j.jwt.consumer.JwtConsumer.processToClaims(JwtConsumer.java:128)
at Test.main(Test.java:28)
Caused by: java.lang.NullPointerException
at org.jose4j.lang.ByteUtil.bitLength(ByteUtil.java:136)
at org.jose4j.jws.HmacUsingShaAlgorithm.validateKey(HmacUsingShaAlgorithm.java:77)
at org.jose4j.jws.HmacUsingShaAlgorithm.validateVerificationKey(HmacUsingShaAlgorithm.java:93)
at org.jose4j.jws.JsonWebSignature.verifySignature(JsonWebSignature.java:101)
at org.jose4j.jwt.consumer.JwtConsumer.processContext(JwtConsumer.java:163)
Comments (7)
-
reporter -
reporter - edited description
-
repo owner Thanks for the detailed description and test case. And yes, I agree that it shouldn't NPE in the case that the raw key data isn't available due to being non-extractable (I typically think of private keys as being the non-extractable things for HSMs but apparently better handling is needed for secret keys too). Will probably have to just skip the length check in this case.
Couple questions:
What does SecretKey.getFormat() return for non-extractable secret keys in your HSM?
Are you using JWE (encryption) from this library at all? There are probably similar problems with AES key wrapping and direct encryption algs.
-
reporter In case of PKCS11 and a non-extractable secret key, SecretKey.getFormat() returns null - so does SecretKey.getAlgorithm().
I'm not using JWE at the moment but I agree that this issue should also affect encryption use cases if Key.getEncoded() is used for checking length constraints.
-
repo owner - changed status to resolved
fix w/ 5271d54 for HMAC and AES key wrapping
-
repo owner - attached jose4j-0.4.5-SNAPSHOT.jar
Attached is a snapshot build, if you want to test it out in your environment. It should work but HSMs can be surprising at times.
-
repo owner - changed status to closed
released with v 0.5.0
- Log in to comment