Generated with Java JJWT signature fails at jwt.io debugger - java

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")));

Related

Not able to generate correct payload hash to connect to Amazon SQS from JMeter

I am trying to push messages to Amazon SQS from Jmeter.
But i am not able to generate the proper payload hash.
I am sending a message to the queue using a POST request.
curl --location --request POST 'https://host_url/queue_name?Action=SendMessage&MessageBody=Trial&MessageGroupId=1&Expires=2021-01-31T12%3A00%3A00Z&Version=2012-11-05'
The request is not having a body. So I was expecting the payload hash to be generated on an empty String, which would be
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
But it is expecting a different one.
Why is it so?
On what argument do I have to generate the payload hash?
Here is my code to generate the hash.
public static String getHexDigest(String text) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(text.getBytes(StandardCharsets.UTF_8));
byte[] digest = md.digest();
String hex = String.format("%064x", new BigInteger(1, digest));
return hex;
}
It depends on the algorithm your application is using, looking at Authenticating requests chapter it could be:
Derive a Signature Version 4 Signing Key
Base64-encode the request signature
Set timestamp for the request expiration
The above links contain Java code examples.
However according to the aforementioned guide:
Amazon SQS supports Signature Version 4, which provides improved SHA256-based security and performance over previous versions. When you create new applications that use Amazon SQS, use Signature Version 4.
If this is the case you can refer to How to Handle Dynamic AWS SigV4 in JMeter for API Testing article

How to make sure your files are encrypted when using AWS S3 KMS encryption?

I'm trying to test the most basic use cases around encryption/decryption with AWS S3 and AWS java SDK (trying both v1 and v2).
So this is what I'm doing :
I upload a small json file using aws console, and then I check that in Properties > crypt, "AWS-KMS" is selected and my key alias is selected. I assume this tells me the file is encrypted with my key, but I have no way to check this, since if I try to open the file using aws console, it's in clear text.
I try to download the file using various methods, and I expect to get an encrypted file when I use the most basic method.
So by using this client (sdk v2) :
#Bean
public S3Client s3Clientv2(AppProperties appProperties, CustomAwsCredentialsProvider customAwsCredentialsProvider) {
return S3Client.builder()
.httpClientBuilder(httpClientBuilder)
.credentialsProvider(customAwsCredentialsProvider)
.region(Region.EU_WEST_3)
.build();
}
and this download method :
public void downloadFile(String bucket, String key) {
s3Client.getObject(GetObjectRequest.builder().bucket(bucket).key(key).build(), ResponseTransformer.toFile(Paths.get("test_aws.json")));
}
I expected to get an encrypted file, but it was not.
Then I tried to use a client able to "encrypt/decrypt" by itself "Any objects you get from Amazon S3 using this client are automatically decrypted" source : https://docs.aws.amazon.com/en_pv/sdk-for-java/v1/developer-guide/examples-crypto-kms.html
AmazonS3Encryption s3Encryption = AmazonS3EncryptionClientBuilder
.standard()
.withRegion(Regions.US_WEST_2)
.withCryptoConfiguration(new CryptoConfiguration(CryptoMode.EncryptionOnly).withAwsKmsRegion(Region.getRegion(Regions.US_WEST_2)))
// Can either be Key ID or alias (prefixed with 'alias/')
.withEncryptionMaterials(new KMSEncryptionMaterialsProvider("alias/s3-kms-key"))
.build();
But using :
S3Object file = s3Encryption.getObject(new GetObjectRequest(bucket, key));
with this client call gets me a warning : "Unable to detect encryption information for object '%s' in bucket '%s'. Returning object without decryption."
So what am I doing wrong here ?
How can I check my file is really encrypted ?
What would be the right config to download it and decrypt it without a "file not encrypted" warning ?
Update : of course I've checked the object metadata, which do contain the KMS information and the KMS key id, but the encryption client is expecting some other informations about this :
/** Initialization vector (IV) header that is used in the symmetric and envelope encryption mechanisms */
public static final String CRYPTO_IV = "x-amz-iv";
and this :
/**
* Encrypted symmetric key header that is used in the Encryption Only (EO) envelope
* encryption mechanism.
*/
public static final String CRYPTO_KEY = "x-amz-key";
Ok, I think I get it : I got confused between server-side encryption and client-side encryption.
So AmazonS3Encryption client is only about client-side encryption. When you are using server-side encryption, any access to the file will decrypt it.
So the answer to my question would be : disable server-side encryption and use client-side encryption if you need fine-grained control about encryption, and to be able to download still encrypted content.

JWT signature does not match locally computed signature

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.

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