JWT signature does not match locally computed signature - java

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.

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

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.

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)

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.

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

Two types of problems meight exists:
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;
}
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.

Related

Can't specify JWKSet URL when validating confidential token coming from Azure AD

Connecting to Azure AD v2.0 endpoint I cannot validate my token which jwt.io and jwt.ms declare as valid
A public token is no problem:
IDTokenValidator validator = new IDTokenValidator(issuer, clientId, JWSAlgorithm.RS256, jwkSetUrl);
IDTokenClaimsSet validatedClaimsSet;
try {
validatedClaimsSet = validator.validate(jwt, null);
} catch (BadJOSEException | JOSEException e) {
LOGGER.error(e.getMessage());
return;
}
but validating a token coming from a confidential client will throw the exception
Signed JWT rejected: Another algorithm expected, or no matching key(s) found
with the following code
IDTokenValidator validator = new IDTokenValidator(issuer, clientId, JWSAlgorithm.RS256, new Secret(authMethod.getSecret()));
No matching keys are found because (JWKMatcher.java:1258)
keytype does not match: OCT instead of RSA
use is null
ops is null
It seems an inadequate JWKSet is created from the secret in ImmutableSecret.java:47 but I seem to lack understanding as to how the secret can contain the necessary data found in the jwks_uri from Azure
Any advice would be appreciated
Validating an ID token works the same way for both public and confidential clients. So just get rid of the secret parameter and your code will work - using the token's signing public key.
The secret parameter is for JWTs that are signed with a symmetric algorithm such as HMACSHA256 - which is almost never a good idea. More about this topic in JWT Security Best Practices.

How to check JWT token expiration time without secret?

I need to check JWT token before sending it in request. But I not generated this token, I just reseived it by authorization, than, I have no secret for it.
I use io.jsonwebtoken.jjwt library.
How to check token expiration time with this library?
You do not need the secret to read the token. The secret is only required to ensure that the token was not modified. From what it looks like, however, the library, ensure you can not skip the signature check. So we need to trick it.
Accessing the Expiration while ignoring the Signature
A JWT consists of three parts, base64 encoded and separated by dots: Header.Body.Signature
If we remove the signature, there is nothing the library can check against.
We must, however, also access the raw body , as signatureless claims are not supported.
var signedToken = "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9...";
var tokenOnly = signedToken.substring(0, signedToken.lastIndexOf('.') + 1);
var expiration = ((Claims)Jwts.parser().parse(tokenOnly).getBody()).getExpiration();
However, without verifying the signature, you will not know if someone modified the token. I can not stress this enough: Never rely on any token information if you can not verify the signature.
How to do it better
Have a look at asymmetric algorithms (the RS, ES and PS family). This allows an issuer to generate JWTs with a private key and anyone with the corresponding public key can verify that the token is valid. This way you can validate and access the claims you want with the assurance that they where issued by an issuer you trust and that they where not modified in any way.

Generated with Java JJWT signature fails at jwt.io debugger

I am using the jjwt Java library for server side generation of jwt in on servlets, the code snipper below straight from the jjwt GitHub page https://github.com/jwtk/jjwt generates and prints out this token.
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.XIKER3owR8BS3Krhsksg9INh9VBSejdn_qN-ONtPans
String compactJws = Jwts.builder()
.setSubject("Joe")
.signWith(SignatureAlgorithm.HS256, "secret")
.compact();
PrintWriter out = response.getWriter();
out.println(compactJws);
However, when I try to verify this token on jwt.io's debugger, it fails the signature check.
Both checking and unchecking secret base64 encoded didn't work
Am I using the library wrongly?
Try with secr and check the base64 option :)
It is due to .signWith(SignatureAlgorithm.HS256, "secret"). It is implemented by DefaultJwtBuilder class
public JwtBuilder signWith(SignatureAlgorithm alg, String base64EncodedSecretKey)
This method assumes that you are providing a key in base64 and secret is not base64. When the method decodes from base64 to byte[] the java converter used by jjwt provides a representation of the string secr which is different to the JavaScript decoder used at jwt.io
You can test yourself with
System.out.println(
javax.xml.bind.DatatypeConverter.printBase64Binary(
javax.xml.bind.DatatypeConverter.parseBase64Binary("secret")));

How to collect the signature, header and body from JWT on Android

I recently got to know about Json Web Token (JWT). Since I liked how it works I have started to implement it on my project. My project involves two apps to communicate. One is an android app and the other is Laravel web application.
The mobile app logs in after the user credential is authenticated from the server side.
I have sent the username and password to server from the mobile app and I have got the JWT in string format. But from this point onward I couldn't find a way to collect the JWT content.
I have gone through almost all possible shown (googled results) but I couldn't manage to get the contents, signature and header.
One of the method I have got a little bit further with, was using the following code, notice I have removed the setSigningKey():
try {
Claims claims = Jwts.parser().parseClaimsJwt(jwtHeaderAndClaim).getBody();
System.out.println("ID of the claims: " + claims.getId().toString());
}catch (Exception e){
Log.e("Exception: ", e.toString());
}
The above code generates the following error:
Exception: io.jsonwebtoken.PrematureJwtException: JWT must not be accepted before 2016-06-14T10:20:09+0300. Current time: 2016-06-14T10:19:37+0300´
the jwtHeaderAndClaim is the JWT String after removing the signature part only (i.e: "xxxxxx.yyyyyyyy."). if i put the jwtString (xxxxxxx.yyyyyyyy.ccccccc) instead of jwtHeaderAndClaim the following error will occur:
Exception: io.jsonwebtoken.UnsupportedJwtException: Signed JWSs are not supported
If I put the setSigningKey as shown in stormpath example:
Claims claims = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(apiKey.getSecret())).parseClaimsJwt(jwtString).getBody();.
The above code will not work for two reasons:
1. I don't have the library
import javax.xml.bind.DatatypeConverter;
2. I don't know how to get the key.
But know that I don't need the key since this time I am trying to login and collect the user information's (like firstname, lastname, phone, etc), and the signature (token) so that the next time I send data to be stored to the server side I have the token to get access to the backend.
Can anyone please help me?
You have many questions. I try to answer some of them
io.jsonwebtoken.PrematureJwtException: JWT must not be accepted before
2016-06-14T10:20:09+0300. Current time: 2016-06-14T10:19:37+0300´
You are using nbf (not before) attribute in JWT. Do not use it (it is optional) or sets a range of validity given that the clocks of the devices will not be synchronized
From RFC 7519
The "nbf" (not before) claim identifies the time before which the JWT
MUST NOT be accepted for processing. The processing of the "nbf" claim requires that the current date/time MUST be after or equal to the not-before date/time listed in the "nbf" claim. Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.
Signed JWS
Exception: io.jsonwebtoken.UnsupportedJwtException: Signed JWSs are
not supported
Do you want to validate the signing key at client side or at server side?
If you use the JWT for authentication replacing user & password, and you are sending token in each request, you can validate the signature at server side.
If you want to validate the key on the app, do not use a symmetric key, because it could be a big vulnerability if it fell into the wrong hands. See. You can use and asymmetric key pair. Sign the JWT in server with the private key and validate on device with public key.
I don't have the library import javax.xml.bind.DatatypeConverter
String base64 = Base64.encodeToString(data, Base64.DEFAULT);
byte[] data = Base64.decode(base64, Base64.DEFAULT);
I don't know how to get the key.
Your key probably was generated on server side in this way
Key key = MacProvider.generateKey(SignatureAlgorithm.HS256);
byte data[] = key.getEncoded();
Make available the key data[] to client in the way you prefer. Using assymetric keys, you only need to make available the public key.
KeyPair keyPair = RsaProvider.generateKeyPair();
byte data[] = keyPair.getPublic().getEncoded();

Decrypt / Verify Firebase token

I'm using Firebase with a Email & Password authentication. Once the user has been signed in successfully I'll receive an AuthData object which contains a token.
I wanna send this token to my backend, verify it and extract the uid from it - unfortunately I don't know how to do this.
I'm aware of the Firebase secret and if I go to jwt.io, enter the token and the secret the signature is verified and I see the correct payload - so this actually works.
Since I'm running Java on my backend I've been using jjwt for the decryption process unfortunately it always throws a SignatureException:
io.jsonwebtoken.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.
Following the code I've been using:
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
byte[] encodedKey = Base64.decode("my-firebase-secret", Base64.DEFAULT);
Key k = new SecretKeySpec(encodedKey, signatureAlgorithm.getJcaName());
Claims claims = Jwts.parser()
.setSigningKey(k)
.parseClaimsJws("the-token").getBody();
I've also tried it with the following snippet:
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary("my-firebase-secret"))
.parseClaimsJws(jwt).getBody();
But got the same exception. So what am I doing wrong? Thanks in advance.
Have you tried to use getBytes instead of parseBase64Binary? Here is the example:
Claims claims = Jwts.parser().setSigningKey("my-firebase-secret".getBytes("UTF-8")).parseClaimsJws(jwt).getBody();

Categories