Home > Mobile >  Java: Why do I fail to encode back a decoded JWT?
Java: Why do I fail to encode back a decoded JWT?

Time:04-01

I have this JWT token:

public final String WTOK = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwdWIiOiIyMzUzNiIsIndpZCI6IjAwMXcwMDAwMDFmbEN3MkFBRV9NMTQ0NDU5IiwidXJsIjpbIndvY2hpdC5jb20iXSwiZXhwaXJ5IjoiMjAyMy0wMy0yOFQxMjoxMToyNC4xODdaIiwiaWF0IjoxNjQ4NDY5NDg0fQ.r_xKkhqE2WCmqcrsdFvZsMFbWDoaV9AVGrg8pq270ZE";

Then I used the following simple method to decode it:

public class DecodedToken {

    Map<String, Object> headers;
    String payload;
    String signature;
}

private DecodedToken decodeToken(String token) {

    String[] chunks = token.split("\\.");

    Base64.Decoder decoder = Base64.getUrlDecoder();

    try {
        String headersStr = new String(decoder.decode(chunks[0]));
        ObjectMapper objectMapper = new ObjectMapper();
        Map<String, Object> headers = objectMapper.readValue(headersStr, Map.class);
        String payload = new String(decoder.decode(chunks[1]));
        System.out.println(payload);
        String signature = new String(decoder.decode(chunks[2]));
        System.out.println(signature);

        return new DecodedToken(headers, payload, signature);
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

and the parts where decoded successfully.

Then I tried to encode it back using the following method:

private String encodeToken( DecodedToken decodedToken) {

    byte[] decodedSecret = Base64.getDecoder().decode(SECRET_KEY_BASE64);
    byte[] encodedSignature = Base64.getEncoder().encode(decodedToken.getSignature().getBytes());
    System.out.println(new String(encodedSignature));

    Map<String, Object> headers = new HashMap<>();
    headers.put("alg", decodedToken.getHeaders().get("alg"));
    headers.put("typ", decodedToken.getHeaders().get("typ"));

    return Jwts.builder()
        .setPayload(decodedToken.getPayload())
        .setHeader(headers)
        .signWith(SignatureAlgorithm.HS256,decodedSecret)
        .compact();
}

which returned me a string. However, the signature part came out different from the original one.

Can you advise what is wrong with my code? My guess I should encode back the decoded signature. How can I do that?

(Please tell me if you need more information from me)


Update

I retried the methods above with a token I created by myself and a my secret key.

Would you please use them on my code and try to see where is the error?

token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwdWIiOiJwdWJwdWIiLCJ3aWQiOiJ3aWR3aWQiLCJleHBpcnkiOiIyMDIzLTAzLTMwVDE2OjExOjAwWiIsInVybCI6WyJsbCJdfQ.o8qD7CfvtWFvqE-Ti_hZq2Tcf4tdcIWutr6vFNRtwRw

Base64 secret key: MTIzNDU2NDU2NDMyMTMyMTY1NDY1ODc4OTc5NjQxMzIx

headers:

{ "alg": "HS256", "typ": "JWT" }

enc: A128CBC-HS256

CodePudding user response:

The resulting signature can be different even for the same payload and headers. It depends also on whitespace, newline, etc. characters used. Maybe the original token was formatted with whitespaces, that were removed in the resulting string?

Also, remember that the signature is calculated for payload and header. I can see that you're constructing the header yourself. Is it exactly the same as in the original?

CodePudding user response:

I found another way to solve the problem:

private String encodeToken(DecodedToken decodedToken) {

        Key hmacKey = new SecretKeySpec(Base64.getDecoder().decode(SECRET_KEY_BASE64),
            SignatureAlgorithm.HS256.getJcaName());

        ObjectMapper objectMapper = new ObjectMapper();
        Map<String, Object> payload = objectMapper.readValue(decodedToken.getPayload(), Map.class);

        return Jwts.builder()
            .setHeaderParam("alg", decodedToken.getHeaders().get("alg"))
            .setHeaderParam("typ", decodedToken.getHeaders().get("typ"))
            .claim("pub", payload.get("pub"))
            .claim("wid", payload.get("wid"))
            .claim("expiry", payload.get("expiry"))
            .claim("url", payload.get("url"))
            .signWith(hmacKey)
            .compact();
}

With this I got the same jwt back. Another matter that wasted my time was that at the beggining I set the headers with this order:

.setHeaderParam("typ", decodedToken.getHeaders().get("typ"))
.setHeaderParam("alg", decodedToken.getHeaders().get("alg"))

whereas as I should have set them with this order:

.setHeaderParam("alg", decodedToken.getHeaders().get("alg"))
.setHeaderParam("typ", decodedToken.getHeaders().get("typ"))

That was creating a different signature... :(

Thank you all!!

  • Related