Search code examples
javajwtjjwt

Cannot get JWT token generation to work on jwt.io debugger


I am having some trouble using the JWT debugger at https://jwt.io/ I am using the JWT library from https://github.com/jwtk/jjwt The use case is to send requests to an API that requires that I pass an API key in the token payload and sign it using the client secret that they give me. So far, using the client secret they sent me (one secret appeared to be base64, but the second one they sent was definitely not base 64). So far generating the token using the above library fails on the REST API, and it also fails at jwt.io. If I can get that working, I can probably get it to work on the REST API as well.

Here is a block of code I use to generate my code. I used a very simple secret string for this example but it is 32 bytes long (the client secret they gave me was 43 bytes long):

public class TestHarness {

public static void main(String[] args) {
    
    
    String apiKey = "39999999-ba25-476a-957e-806f9f726e39";
    
    String secret = "123456789-123456789_123456789_12";
    //long timeNow = JWTutil.getUnixTime();
    // Using same time so that each time we run we should get the exact same 
    // token
    long timeNow =  1672761664338l;
    System.out.println(String.format("Now in UNIX is %d", timeNow));
    
    String token = JWTutil.getToken(apiKey, secret, 300, timeNow);
    System.out.println(token);
    String[] parts = token.split(token, '.');
    System.out.println(String.format("Token has %d parts", parts.length));
    for (int i=0;i<parts.length;i++) {
        System.out.println(String.format("Part %d : %s", i, parts[i]));
    }
    if (parts.length > 2) {
        System.out.println(parts[2]);
    }
    
}

And here is my JWT utils class with the methods used above to generate the token


import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
import java.nio.charset.StandardCharsets;
import java.util.*;
import javax.crypto.SecretKey;

public class JWTutil {
    private static SignatureAlgorithm sigHS256 = SignatureAlgorithm.HS256;
    
    public static String getToken(String appID, String secret, int duration ) {
        long currTime = getUnixTime();
            
        return getToken(appID, secret, duration, currTime);
    }
    
    public static String getToken(String appID, String secret, int duration, long currTime) {
        String token = Jwts.builder().setHeader(getHeader()).addClaims(getClaims(appID, currTime, duration)).signWith(getSigningKey(secret), sigHS256).compact();
        return token;
    }
    
    private static Map<String,Object> getHeader() {
        Map<String, Object> map = new HashMap<String,Object>();
        map.put("alg", "HS256");
        map.put("typ", "JWT");
        return map;
            
    }
    
    private static Map<String,Object> getClaims(String appId, long now, int duration) {
        Map<String,Object> claims = new HashMap<String,Object>();
        
        claims.put("sub", appId);
        claims.put("iat", Long.valueOf(now));
        claims.put("exp", Long.valueOf(now + (long)duration));
        return claims;
    }
    private static Key getSigningKey(String secret) {
        
        byte[] theBytes = secret.getBytes(StandardCharsets.UTF_8);
        //byte[] encodedBytes = Base64.getEncoder().encode(secret.getBytes(StandardCharsets.UTF_8));
        //Key key = new SecretKeySpec(encodedBytes, sigHS256.getJcaName());
        SecretKey key = Keys.hmacShaKeyFor(theBytes);
        return key;
    }
    public static long getUnixTime() {
        
        Date current = new Date();
        return current.getTime();
    }
    
}

I did look at the source on JJWT (I couldn't find the link to proper JavaDocs but the source comments on each function served the same purpose), the signWith() function takes 1 or 2 arguments - either a Key (1 argument) which will "guess" the appropriate algorithm to use, or 2 args - A key and the signing algorithm (presumably for when you want to explicitly set the signature). In an older post, it quoted signWith() taking 1 arg as the Signing algorithm and the second as a base64 string but I seem to recall that is deprecated - it's a key or nothing.

On jwt.io, I provide the client secret I used to create the secret with. I suspect that I may be misunderstanding what I'm supposed to provide. When answering, assume that I'm given a human readable string which may or may not already be Base64 encoded. When I toggle the Base64 Encoded Secret checkbox on jwt.io, the signature changes - both ways to something other than what was in original token, even with the text staying the same for the key.


Solution

  • I think I discovered the problem. I looked at the code sample that K. Nicholas provided. The Nimbus-Jose library is one that I had previously tried. However the problem with the jwt.io site was more low-tech than that. WHen entering your client secret, make sure you erase the text that says "your 256 bit secret". signature section of jwt.io site

    I thought it was an underlay but no it's actual text content. If you paste your secret in that box, it just prepends it to the text "your 256 bit secret".

    In this case the solution was very low tech.