Search code examples
javajava-8jwtbase64url

Java 8 Base64 JWT token into JSON


So, I have done some research on this on the Net and here on StackOverflow, and I have tried many, multiple suggestions that I have found. The problem is that I am logging into one of our Oauth2 services which is working well. I get an Oath2 JWT token. I know this is Base64 encoded, and I can drop the token into jwt.io and www.base64decode.org and both of these sites parse the token correctly.

I am using Java 8 Base64 tools, and the code looks as follows:

public String getTokenProperty(String token, String propertyName)
{
    byte[] bytes = Base64.getUrlDecoder().decode(token);
    String decodedString = new String(bytes, StandardCharsets.UTF_8);
    System.out.println("Decoded: " + decodedString);
    return (new JSONObject(decodedString)).getString(propertyName);
}

The error occurs on the decoder line as follows:

java.lang.IllegalArgumentException: Illegal base64 character 2e

I tried this with a token from my Oauth2 Service, I got a token from Syncope, and I got a token from Auth0 ... all return with JWT Base64 encoded tokens. With ALL these tokens from these different servers, I get the same error.

I would like to use the Java 8 Base64 which is standard, but I am thinking that I may need to use an external third-party Base64 decoder.

Any help would be great. Thanks!

The token is as follows:

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik1qSTRRVFEwT1VRNU9VSXlSVEV6TlRBd05UVXpSVVExTlVOR05FVkVORGRDTlRnM016VXdRZyJ9.eyJodHRwczovL2JpdG9vbXRyYWRlci5uZXQvYXV0aG9yaXphdGlvbiI6eyJncm91cHMiOlsiQ29uc3VtZXJzIl0sInJvbGVzIjpbIlVzZXIiXX0sImlzcyI6Imh0dHBzOi8vYml0em9vbS5hdXRoMC5jb20vIiwic3ViIjoiYXV0aDB8NWNhNTE5NzZjYzMzZjUxMTBhYWNkYmM0IiwiYXVkIjpbImh0dHBzOi8vYml0em9vbS5hdXRoMC5jb20vYXBpL3YyLyIsImh0dHBzOi8vYml0em9vbS5hdXRoMC5jb20vdXNlcmluZm8iXSwiaWF0IjoxNTU5MzIzNDI1LCJleHAiOjE1NTk0MDk4MjUsImF6cCI6IlliRGFSelRVQkFtZEFrSExqdjZ0bEI3U05xSTF1RlNtIiwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBlbWFpbCBhZGRyZXNzIHBob25lIHJlYWQ6Y3VycmVudF91c2VyIHVwZGF0ZTpjdXJyZW50X3VzZXJfbWV0YWRhdGEgZGVsZXRlOmN1cnJlbnRfdXNlcl9tZXRhZGF0YSBjcmVhdGU6Y3VycmVudF91c2VyX21ldGFkYXRhIGNyZWF0ZTpjdXJyZW50X3VzZXJfZGV2aWNlX2NyZWRlbnRpYWxzIGRlbGV0ZTpjdXJyZW50X3VzZXJfZGV2aWNlX2NyZWRlbnRpYWxzIHVwZGF0ZTpjdXJyZW50X3VzZXJfaWRlbnRpdGllcyIsImd0eSI6InBhc3N3b3JkIn0.St7097L1ZAlBWcAPrie-8CGV2F3Fr8uNYpSDVKSPVPF4zBZrmm62_UAj7Ssux8AjUy0LhjiF3kLpNph2L7yrpUREw6TyGJwQasfdVtM5VzRYUcy-fOGyRSqPQorbzxJQZzs2pyDJm-2hMQ0McJ37ubKIWrHFD5McMedN6THK7g5TExX47XCRPcOuCEWm3bf3zdWF2LEGhCw_c-lcZDwlb4ePkO721XjSWtrXEBvxc8scFNaHDt7VOnrSze4XK_LO8eE8bHRq6qUrWf1csYucK--aHazBsvfdl-6QDRk-tOBM-LdXJMT7H8Ih6trxVmZofQjr2dQ4j_3DTVoU3eLdog

UPDATE:

I switched from java,util.Base64 to org.apache.commons.codec.binary.Base64 and this seems to work somewhat, I don't get an error now.

String decodedString = new String(bytes, StandardCharsets.UTF_8);

gives me back a string of the header, payload, and signature data. So, when I do:

JSONObject jsonObject = new JSONObject(decodedString);
    System.out.println("getTokenProperty: jsonObject = " + jsonObject.toString());

I am only getting back the header data, and what I really need is the payload.

For the record ... the code I inherited from a "proof of concept" project was:

public static String getTokenProperty(String token, String propertyName)
{
    return (new JSONObject(new String(Base64.getDecoder().decode(token)))).getString(propertyName);
}

and there was no unit testing at all. So, when I went to unit test it, of course it completely broke. So, now I have a better understanding of how to parse this token, and I will remember this lesson for a long time.

Thanks very much!


Solution

  • The reason it doesn't parse is because you are trying to Base64URLDecode the ENTIRE token.. But you have to decode PARTS of the token which is separated by a DOT "." character (0x2e in hex, 46 in dec, &#46 in html -- ASCII/UTF8) ..

    Example:

    public static void decodeTokenParts(String token)
    {
        String[] parts = token.split("\\.", 0);
    
        for (String part : parts) {
            byte[] bytes = Base64.getUrlDecoder().decode(part);
            String decodedString = new String(bytes, StandardCharsets.UTF_8);
    
            System.out.println("Decoded: " + decodedString);
        }
    }
    

    This is because a JWT token is made up of parts:

    Base64URLEncode({HeaderJSON}) + "." + Base64URLEncode({PayloadJSON}) + "." + Signature for example..

    So to decode it.. you need to split it by "." and decode each part. Note: The signature will usually be binary that is encoded as base64 so once you decode it, don't try printing it.. it'll print bytes. You'd need to verify the signature.

    For example, if you go to: https://jwt.io/#debugger-io?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

    You will see how each "part" is encoded. It is colour coded.