18

I am using

JwtBuilder builder = Jwts.builder()
                    .setId(user.getEmail())
                    .signWith(signatureAlgorithm, signingKey);

to create a token then

Jwts.parser().setSigningKey(secret).parse(token);

to authenticate. When I run this in a JUnit test, it works fine. However, when I authenticate token passed as a header over REST call, authentication fails with SignatureException. I have verified the token on both ends of the HTTP call and the token string is identical. Code to create/authenticate is static, therefore, the secret is same on each side.

Dharman
  • 30,962
  • 25
  • 85
  • 135
stanlick
  • 1,442
  • 3
  • 16
  • 29
  • Could you post an example token and the secret key? – pedrofb Feb 22 '17 at 17:45
  • static Key secret = MacProvider.generateKey(); SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; byte[] apiKeySecretBytes = secret.getEncoded(); Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName()); – stanlick Feb 22 '17 at 18:46

6 Answers6

16

static Key secret = MacProvider.generateKey(); will generate a new random key each time your server is reloaded because static variables are initialized when the class is loaded

It means that if you issue a JWT, it is only valid as long as the server does not reboot. The SignatureException you got is because the signing key it is different

You need to store the signing key secret.getEncoded() after first generation and load it when your module starts

pedrofb
  • 37,271
  • 5
  • 94
  • 142
  • Great observation. However, I have a single utility class on the server that is the only reference to this static Key. It uses this single reference for both create and authenticate operations. This is why it is so confusing. – stanlick Feb 22 '17 at 20:08
  • Just to check that there is no other hidden problem: could you print the key content before signing and verification? E.g `System.out.println(DatatypeConverter.printHexBinary(secret.getEncoded()))` – pedrofb Feb 22 '17 at 20:15
  • @stanlick, Did you check if the secret code was the same when signing and verifying? – pedrofb Feb 24 '17 at 08:15
  • 1
    you nailed it brother! It was an issue with two different VM's. Even though the token generated was exactly the same on both ends, the verification was failing because token wasn't generated on the same VM that was performing verification – stanlick Feb 24 '17 at 13:54
  • I have similar problem? Could you tell me how to resolve it? https://stackoverflow.com/questions/45142800/spring-spock-integration-test-of-authentication – user Jul 17 '17 at 11:57
13

I have had the same problem, I noticed that in sources whenever they convert the signing key they explicitly specify UTF-8 encoding. I tried changing the encoding while both decoding the token:

 private Jws<Claims> decodeToken(String token) {
        return Jwts.parser()
                .setSigningKey(securityProperties.getTokenSecret().getBytes(Charset.forName("UTF-8")))
                .parseClaimsJws(token);
 }

And when signing the token:

private String getSignedToken(UserDetailsAdapter user, List<String> roles, byte[] signingKey) {
        return Jwts.builder()
                .signWith(Keys.hmacShaKeyFor(signingKey), SignatureAlgorithm.HS512)
                .setHeaderParam("typ", securityProperties.getTokenType())
                .setIssuer(guiServerSecurityProperties.getTokenIssuer())
                .setAudience(guiServerSecurityProperties.getTokenAudience())
                .setSubject(user.getUsername())
                .setExpiration(new Date(System.currentTimeMillis() + 864000000))
                .claim("rol", roles)
                .compact();
    }

This is the only thing that fixed this for me.

8

I had a similar problem. In my case it was wrong token validation. I set sign as bytes:

.signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret().getBytes())

But when i was parsing the token and setting signKey i setted it as a String, not as bytes:

Jwts.parser().setSigningKey(signingKey).parseClaimsJws(this.token)

Also always check quotes and spaces when checking token, there often can be excess space/quote in the start/end of the token (use trim() method)

S.Daineko
  • 1,790
  • 1
  • 20
  • 29
6

I had a similar problem. In my case both keys were the same, but for some reason I was receiving a token within quotes (e.g "Syasda.da3das.aDjty6" instead of just Syasda.da3das.aDjty6).

It took me quite some time to realize this since most of the time while testing on jwt.io I would just copy the token manually without the brackets to verify it.

token = token.replace("\"",""); 

Removing those quotes solved the problem for me. Hopefully this will help someone else as well.

Lefty G Balogh
  • 1,771
  • 3
  • 26
  • 40
Nuper
  • 59
  • 2
  • 3
1

I solved the problem modifying the HOST in the URL REST endpoint. It had a wrong host which was returned error HTTP 401 unauthorized.

jis83
  • 11
  • 3
1

Two types of problems meight exists:

  1. Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256); generates a different random key each time the function secretKeyFor is run. So if you want to generate a constant key, try this one
 public SecretKey generalKey(){
        String stringKey = Global.JWT_SECRET;
        byte[] encodedKey =Base64.decodeBase64(stringKey);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length,
                "HmacSHA512");
        return key;
    }
  1. In my code, I recieve the token transferred by ajax, and JSON.stringify() is used to convert the javasript object to string, which wrapped additional quota on the origin string, try to eliminate the quota as Nuper said.
Arrow.Tao
  • 33
  • 7