Paulo Paulo - 1 month ago 18
Java Question

Static secret as byte[], Key or String?

I have started to work with JJWT to handle JWT on my server application.

My JWT secret will be stored at

resources
folder and I will load the secret with
Properties
class.

The JJWT provides three methods to sign the JWT, one uses
byte[]
, other uses
String
and the other uses
Key
:

JwtBuilder signWith(SignatureAlgorithm var1, byte[] var2);

JwtBuilder signWith(SignatureAlgorithm var1, String var2);

JwtBuilder signWith(SignatureAlgorithm var1, Key var2);


The question: Regarding security, charset and other things, there are any recommendations of which one I should use?

For while, I stand with
String
, since
Properties
return a
String
.

Answer

The JavaDoc for signWith(SignatureAlgorithm var1, String var2) indicates expectations, and even names the method parameter intuitively:

/**
 * Signs the constructed JWT using the specified algorithm with 
 * the specified key, producing a JWS.
 *
 * <p>
 * This is a convenience method: the string argument is first
 * BASE64-decoded to a byte array and this resulting byte array is
 * used to invoke {@link #signWith(SignatureAlgorithm, byte[])}.
 * </p>
 *
 * @param alg                    the JWS algorithm to use to digitally
 *                               sign the JWT, thereby producing a JWS.
 *
 * @param base64EncodedSecretKey the BASE64-encoded algorithm-specific
 *                               signing key to use to digitally sign
 *                               the JWT.
 *
 * @return the builder for method chaining.
 */
JwtBuilder signWith(SignatureAlgorithm alg, String base64EncodedSecretKey);

So, this method expects the string argument to be a Base64-encoded secret key byte array. It does not assume a general string, like a user password for example, as the signing key. JJWT assumes Base64 encoding because if you're specifying a string password that is not Base64-encoded, you're probably using a poorly formed or weak key.

The JWT JWA specification REQUIRES that HMAC signing keys have lengths equal to or greater than the signature byte array length.

That means that:

| If you're signing with: | your key (byte array) length MUST be: |
| ----------------------- | ------------------------------------- |
| HMAC SHA 256            | >= 256 bits (32 bytes)                |
| HMAC SHA 384            | >= 384 bits (48 bytes)                |
| HMAC SHA 512            | >= 512 bits (64 bytes)                |

Many online JWT sites and tools just just get this plain wrong - they allow you to think that you could type in or use any old string and you're good. Some go as far as even pre-populating the key with the word secret (clearly a bad idea and not even spec-compliant because it's too short!).

So, to help simplify this for you, JJWT provides a utility to help you generate sufficient secure-random keys suitable for spec-compliant signing via the io.jsonwebtoken.impl.crypto.MacProvider class.

Look at the various generateKey methods to see how to generate a good, spec-compliant key for HMAC signatures. For example:

//creates a 256-bit secure-random key:
MacProvider.generateKey(SignatureAlgorithm.HS256);

//creates a 384-bit secure-random key:
MacProvider.generateKey(SignatureAlgorithm.HS384);

//creates a 512-bit secure-random key (the default):
MacProvider.generateKey();

If you wanted to store these generated keys as a String, you could presumably Base64 encode them:

SecretKey key = MacProvider.generateKey();
byte[] keyBytes = key.getEncoded();

String base64Encoded = TextCodec.BASE64.encode(keyBytes);

But note: the resulting base64Encoded string is not considered safe to show to anyone. Base64 encoding is not encryption - the value still needs to be kept secret. How you do this is up to you (encrypt it, etc).

Now, when it is time to create a JWS, you can just pass in that base64Encoded value, and JJWT knows to base64 decode it first to get the real bytes, which are then used to compute the signature:

Jwts.builder()
    //...
    .signWith(SignatureAlgorithm.HS512, base64Encoded)
    .compact();
Comments