How to make HMAC_SHA256 key from secret string to use it with JWT in jose4j?

foki picture foki · Aug 14, 2015 · Viewed 16k times · Source

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.

  • The primary issue - how to sign tokens with HMAC_SHA512 using jose4j?
  • Issue created by my approach solving issue above - how to make 512bit long secret key based on secret string?

Answer

Brian Campbell picture Brian Campbell · Aug 23, 2015

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);