3
votes

I'm generating my JWT token using JJWT library. I generate my token as follows. I'm using dummy values as my secret key.

we can assume that jwt.security.key=security-key

   @Value("${jwt.security.key}")
    private String key;

    @Value("${ws.issuer}")
    private String issuer;

    static final long ONE_MINUTE_IN_MILLIS=60000;

    static final long TOKEN_DURATION_IN_MIN=30L;

    private SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

    @Override
    public String issueToken(String userName) {

        long nowMillis = System.currentTimeMillis();
        long expMillis = nowMillis + (ONE_MINUTE_IN_MILLIS * TOKEN_DURATION_IN_MIN);

        return  Jwts
                .builder()
                .setId("01")
                .setIssuedAt(new Date(nowMillis))
                .setHeaderParam("typ","JWT")
                .setSubject(userName)
                .setIssuer(issuer)
                .setExpiration(new Date(expMillis))
                .signWith(signatureAlgorithm, key).compact();

    }

Although the token can be successfully decoded. Everytime I verify it's signature from jwt.io debugger it always result's to an invalid signature. Which can be seen here.

1
Did you try setting the secret used to encode your token on the "Verify signature" of the "Decode" tab ? It should work that way. To verify a JWT you need to know the secret, only the client should assume that the body is correct.Fabien Thouraud
I consider that validator broken by jwt.io (Still, apparently). Bypassing their validation check, and doing so in my application proved my secret key to be validating correctly.Shawn

1 Answers

3
votes

security-key is not a valid Base64-encoded string. Read the JavaDoc and the argument names of the signWith(SignatureAlgorithm, String) method:

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

Additionally, why not consider that jwt.io might be broken? It is not an authoritative jwt tool.

Finally, I'll note that you should never use plaintext strings or random strings as signing keys. Digital signatures are always computed with byte arrays. If you want cryptography to be safe, you should always use a secure-random byte array of sufficient length for the signing algorithm you are using.

Look at JJWT's Keys.secretKeyFor method to generate a sufficiently long and strong enough key.

The jwt.io site is misleading because it doesn't make this obvious and implies you can use any old string as a signing key. While it is techically possible, you definitely shouldn't. You should always use secure-random byte arrays that are then Base64 encoded if you need to represent them as Strings. This is why JJWT's methods that accept a String as a key assume it is Base64-encoded - because if it's not, you're probably using an invalid or poorly-formed key.