5

I have a signed JWT String that i get from the server. I do not know the key or rather I don't want to put the key on the client/device.

When using this site with my token: https://jwt.io/ I get the desired result it tells me the Header and payload information.

I cannot find a library on android that does what this website does and i have tried all of them that i could find. The most recognized one to use is: https://github.com/jwtk/jjwt

But this gives me an exception that i cannot parse a signed token which as proven by the other website above is false. another resource i have used is: https://bitbucket.org/b_c/jose4j/wiki/Home

This one at least gave me the header information which is the furthest i have been able to get.

To give context why it is the way it is, the payload contains an expiration time and on the device when the token is about to expire i should prompt the user to re enter their credentials to get a new token. Now this might seem like a security threat because a hacker could manipulate the token on the device, but the server checks the token to make sure it is authentic which is why i do not want the key on the device because this can be mined by a hacker, and make the entire application vulnerable.

Delta
  • 869
  • 6
  • 12

5 Answers5

7

If you wanna parse signatured text without using signature :

1- add below dependency :

//JWT
compile 'io.jsonwebtoken:jjwt:0.7.0'

2 - Add below imports :

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.Jwts;

3- Use below code block :

//ref : https://github.com/jwtk/jjwt/issues/135
//you can put your signatured text instead of jws variable.
String jws = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
int i = jws.lastIndexOf('.')
String withoutSignature = jws.substring(0, i+1);
Jwt<Header,Claims> untrusted = Jwts.parser().parseClaimsJwt(withoutSignature);
/*untrusted.getBody().getSubject();
  untrusted.getBody().getExpiration();
  etc.
*/
oguzhan
  • 2,073
  • 1
  • 26
  • 23
  • yes this works. but instead of Jwt
    untrusted = Jwts.parser().parseClaimsJwt(withoutSignature); we can use Claims untrusted = Jwts.parser().parseClaimsJwt(withoutSignature).getBody();
    – Ujjwal Jung Thapa May 13 '19 at 09:48
3

Well the answer was pretty simple although in my opinion should be in the framework. But a simple non signed JWT still has the 2 required periods in it but there is nothing after the last one, so i split my JWT on the periods and combined the first and second one with periods and put a period at the end.

String[] splitToken = result.Value.Content.AuthorizationJWTToken.split("\\.");
Jwt parsedToken = Jwts.parser().parse(splitToken[0] + "." + splitToken[1] + ".");

This was usings the https://github.com/jwtk/jjwt library.

Delta
  • 869
  • 6
  • 12
1

You can use jose4j's JWT consumer to do parsing only and not check the claims or signature. Something like the following will parse the token and compare the expiration time to the current time to see if it's expired.

String jwt = "eyJhbGciOiJIUzI1NiJ9" +
        ".eyJzdWIiOiIxMjM0NTY3ODkwIiwiZXhwIjoxNDUzODE0NjA0LCJuYW1lIjoiSm9obiBEb2UifQ" +
        ".IXcDDLXEpGN9Po5C-Mz88jUCNYrHxu6TVJLavf0NgT8";

JwtConsumer consumer = new JwtConsumerBuilder()
        .setSkipAllValidators()
        .setDisableRequireSignature()
        .setSkipSignatureVerification()
        .build();
JwtClaims claims = consumer.processToClaims(jwt);
NumericDate expirationTime = claims.getExpirationTime();

if (NumericDate.now().isAfter(expirationTime))
{
    System.out.println("Token expired at " + expirationTime);
}
else
{
    System.out.println("Token is still good until " + expirationTime);
}
Brian Campbell
  • 2,293
  • 12
  • 13
1

parseClaimsJwt() method of Jwt will require a signature. So, right way to do it is:

String jws = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
int i = jws.lastIndexOf('.')
String withoutSignature = jws.substring(0, i+1);

Claims claims = (Claims) = Jwts.parser().parse(withoutSignature).getBody();
Rodrigo
  • 365
  • 4
  • 10
0

A JWT is just a pair of base 64 encoded JSON objects followed by a signature.

You can simply:

  1. Extract the header or claims, delimited by the '.' character
  2. Decode the base 64 string into a byte[]
  3. Deserialize the byte[] into a Map or POJO

For example, here I'm interested in reading the "sub" claim of a JWT that failed validation:

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;    
import io.jsonwebtoken.io.Decoders;
// ...
private static final ObjectMapper MAPPER = new ObjectMapper();
// ...

/**
 * Returns the username claimed in the JWT if possible.
 *
 * @param jwt an invalid jwt, possibly malformed
 * @return the value of the "sub" claim if the jwt was parsable, empty otherwise
 */
public Optional<String> untrustedUsername(String jwt) {
    var claimsBase64 = jwt.substring(jwt.indexOf('.') + 1, jwt.lastIndexOf('.'));

    try {
        var claimsByte = Decoders.BASE64.decode(claimsBase64);
        var claimsMap = MAPPER.readValue(claimsByte, new TypeReference<Map<String, Object>>() {
        });
        return Optional.of((String) claimsMap.get("sub"));
    } catch (Exception exception) {
        return Optional.empty();
    }
}

I think it's a good idea that such code isn't integrated in a JWT library to avoid confusing users with an insecure API.

Adrien H
  • 643
  • 6
  • 21