I want to produce JWTs and sign them with HMAC_SHA256. For that task I must use jose4j. I have tried to generate key based on secret with:
SecretKeySpec key = new SecretKeySpec(("secret").getBytes("UTF-8"), AlgorithmIdentifiers.HMAC_SHA512);
but it generates 40bits key while 512bit one is required for signing using HMAC_SHA256.
Section 3.2 of JWA / RFC 7518 says that a key of the same size as the hash output or larger must be used with the JWS HMAC SHA-2 algorithms (i.e, 256 bits for "HS256", 384bits/"HS384", & 512 bits/"HS512"). It's generally a good idea to follow this advice from the IETF and NIST. Roughly speaking the security of an HMAC comes from the size of the hash output and the key length, whichever is smaller. So using the bytes of "secret" as the key gives you a key that's only 48 bits long and, in practice, provides considerably less security than even that because it's a dictionary word, regardless of the strength of the HMAC SHA-2 algorithm you chose.
By default jose4j enforces the minimum key sizes mandated by JWA/RFC 7518. However, as Hans points out, there are ways to tell jose4j to relax the key length requirement. This can be done with JwtConsumer
by calling .setRelaxVerificationKeyValidation()
on JwtConsumerBuilder
and on JsonWebSignature
directly with .setDoKeyValidation(false)
. Below is a quick example producing and consuming a JWT using HMAC SHA256 that shows both.
JwtClaims claims = new JwtClaims();
claims.setExpirationTimeMinutesInTheFuture(5);
claims.setSubject("foki");
claims.setIssuer("the issuer");
claims.setAudience("the audience");
String secret = "secret";
Key key = new HmacKey(secret.getBytes("UTF-8"));
JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(claims.toJson());
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.HMAC_SHA256);
jws.setKey(key);
jws.setDoKeyValidation(false); // relaxes the key length requirement
String jwt = jws.getCompactSerialization();
System.out.println(jwt);
JwtConsumer jwtConsumer = new JwtConsumerBuilder()
.setRequireExpirationTime()
.setAllowedClockSkewInSeconds(30)
.setRequireSubject()
.setExpectedIssuer("the issuer")
.setExpectedAudience("the audience")
.setVerificationKey(key)
.setRelaxVerificationKeyValidation() // relaxes key length requirement
.build();
JwtClaims processedClaims = jwtConsumer.processToClaims(jwt);
System.out.println(processedClaims);
A common approach is to hash the secret before using it as a signing key.
MessageDigest md = MessageDigest.getInstance("SHA-256");
String secret = "secret";
md.update(secret.getBytes("UTF-8"));
byte[] key = md.digest();
The alternative is to relax the requirement on the key length with something like:
JwtConsumer jwtConsumer = new JwtConsumerBuilder()
.setVerificationKey(new HmacKey(secret.getBytes()))
.setRelaxVerificationKeyValidation() // allow shorter HMAC keys when used w/ HSxxx algs
.build();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With