This question already has answers here:
JWT (JSON Web Token) library for Java [closed]
(9 answers)
Closed 3 years ago.
I have to create my token with JWT but I don't know to do it.
You have to use a library for that.
I personally use nimbus-jose-jwt.
This is an example from their page using HS256 for sign the JWT:
// Generate random 256-bit (32-byte) shared secret
SecureRandom random = new SecureRandom();
byte[] sharedSecret = new byte[32];
random.nextBytes(sharedSecret);
// Create HMAC signer
JWSSigner signer = new MACSigner(sharedSecret);
// Prepare JWT with claims set
JWTClaimsSet claimsSet = new JWTClaimsSet();
claimsSet.setSubject("alice");
claimsSet.setIssuer("https://c2id.com");
claimsSet.setExpirationTime(new Date(new Date().getTime() + 60 * 1000));
SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claimsSet);
// Apply the HMAC protection
signedJWT.sign(signer);
// Serialize to compact form, produces something like
// eyJhbGciOiJIUzI1NiJ9.SGVsbG8sIHdvcmxkIQ.onO9Ihudz3WkiauDO2Uhyuz0Y18UASXlSc1eS0NkWyA
String s = signedJWT.serialize();
You can also use jose4j.
An example from their page using RSA for sign the JWT (public + secret key):
// Generate an RSA key pair, which will be used for signing and verification of the JWT, wrapped in a JWK
RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
// Give the JWK a Key ID (kid), which is just the polite thing to do
rsaJsonWebKey.setKeyId("k1");
// Create the Claims, which will be the content of the JWT
JwtClaims claims = new JwtClaims();
claims.setIssuer("Issuer"); // who creates the token and signs it
claims.setAudience("Audience"); // to whom the token is intended to be sent
claims.setExpirationTimeMinutesInTheFuture(10); // time when the token will expire (10 minutes from now)
claims.setGeneratedJwtId(); // a unique identifier for the token
claims.setIssuedAtToNow(); // when the token was issued/created (now)
claims.setNotBeforeMinutesInThePast(2); // time before which the token is not yet valid (2 minutes ago)
claims.setSubject("subject"); // the subject/principal is whom the token is about
claims.setClaim("email","mail#example.com"); // additional claims/attributes about the subject can be added
List<String> groups = Arrays.asList("group-one", "other-group", "group-three");
claims.setStringListClaim("groups", groups); // multi-valued claims work too and will end up as a JSON array
// A JWT is a JWS and/or a JWE with JSON claims as the payload.
// In this example it is a JWS so we create a JsonWebSignature object.
JsonWebSignature jws = new JsonWebSignature();
// The payload of the JWS is JSON content of the JWT Claims
jws.setPayload(claims.toJson());
// The JWT is signed using the private key
jws.setKey(rsaJsonWebKey.getPrivateKey());
// Set the Key ID (kid) header because it's just the polite thing to do.
// We only have one key in this example but a using a Key ID helps
// facilitate a smooth key rollover process
jws.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId());
// Set the signature algorithm on the JWT/JWS that will integrity protect the claims
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
// Sign the JWS and produce the compact serialization or the complete JWT/JWS
// representation, which is a string consisting of three dot ('.') separated
// base64url-encoded parts in the form Header.Payload.Signature
// If you wanted to encrypt it, you can simply set this jwt as the payload
// of a JsonWebEncryption object and set the cty (Content Type) header to "jwt".
String jwt = jws.getCompactSerialization();
In the question that Erik Gillespie indicates are more options.
The jwt.io page allows you to paste the generated token and see his payload. Also, if you put your secret key it would say if the integrity of the token.
Try using solution mentioned in below url:
https://dev.to/keysh/spring-security-with-jwt-3j76
You can use following method:
public String jwtToken(String name) {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
Date expireDate = new Date(nowMillis);
Key key = MacProvider.generateKey();
String compactJws = Jwts.builder()
.setSubject(name)
.setAudience("users")
.setIssuedAt(now)
.setExpiration(expireDate)
.signWith(SignatureAlgorithm.HS512, key)
.signWith(S)
.compact();
return compactJws;
}
Related
I need to extract the expiry date of firebase tokens. How can I extract the "exp" from the token in java?
I tried to use code from https://connect2id.com/products/nimbus-jose-jwt/examples/validating-jwt-access-tokens using com.nimbusds:nimbus-jose-jwt:9.23 but it fails:
String accessToken = "...";
// Create a JWT processor for the access tokens
ConfigurableJWTProcessor<SecurityContext> jwtProcessor =
new DefaultJWTProcessor<>();
// Set the required "typ" header "at+jwt" for access tokens issued by the
// Connect2id server, may not be set by other servers
jwtProcessor.setJWSTypeVerifier(
new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType("at+jwt")));
// The public RSA keys to validate the signatures will be sourced from the
// OAuth 2.0 server's JWK set, published at a well-known URL. The RemoteJWKSet
// object caches the retrieved keys to speed up subsequent look-ups and can
// also handle key-rollover
// I changed it to what I think should work for firebase, but it doesn't seem to matter what I put here:
JWKSource<SecurityContext> keySource =
new RemoteJWKSet<>(new URL("https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken#system.gserviceaccount.com"));
// The expected JWS algorithm of the access tokens (agreed out-of-band)
JWSAlgorithm expectedJWSAlg = JWSAlgorithm.RS256;
// Configure the JWT processor with a key selector to feed matching public
// RSA keys sourced from the JWK set URL
JWSKeySelector<SecurityContext> keySelector =
new JWSVerificationKeySelector<>(expectedJWSAlg, keySource);
jwtProcessor.setJWSKeySelector(keySelector);
// Set the required JWT claims for access tokens issued by the Connect2id
// server, may differ with other servers
jwtProcessor.setJWTClaimsSetVerifier(new DefaultJWTClaimsVerifier(
null,
new HashSet<>(Arrays.asList("exp"))));
// Process the token
SecurityContext ctx = null; // optional context parameter, not required here
JWTClaimsSet claimsSet = jwtProcessor.process(accessToken, ctx);
// Print out the token claims set
System.out.println(claimsSet.toJSONObject());
I get this error:
JOSE header "typ" (type) "JWT" not allowed
com.nimbusds.jose.proc.BadJOSEException: JOSE header "typ" (type) "JWT" not allowed
at com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier.verify(DefaultJOSEObjectTypeVerifier.java:149)
at com.nimbusds.jwt.proc.DefaultJWTProcessor.process(DefaultJWTProcessor.java:341)
at com.nimbusds.jwt.proc.DefaultJWTProcessor.process(DefaultJWTProcessor.java:303)
at com.nimbusds.jwt.proc.DefaultJWTProcessor.process(DefaultJWTProcessor.java:294)
I want to pass a JWT token from a NodeJS service to another service in Java. However, based on the what I've tried, the token is always invalid whenever I try to verify it on the Java side. I do understand the JWT is platform-independent but I'm not able to figure out why is the token not able to get verified on the Java side.
Error: io.jsonwebtoken.security.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted
NodeJS signing the token (using express-jwt library)
const jwtPayload = { id: "admin" };
const secret = " ... some secret ...";
const jwtData = { expiresIn: 2h };
const access_token = jwt.sign(jwtPayload, secret, jwtData);
Java verifying the token (using io.jsonwebtoken)
String secret = "...same as on the nodejs side"
String accessToken = " .. access_token from nodejs .. "
String username = Jwts.parserBuilder().setSigningKey(secret).build().parseClaimsJws(accessToken).getBody().getSubject();
I have not set any additional options either on the NodeJS side or the Java side. Am I missing some configuration on either of the platforms? Or should I be using a different library?
In my case, solved like this:
GENERATE JWT IN EXPRESS:
jwt.sign({ issuer: new Date(), roles, exp: Math.floor(Date.now() / 1000) + (43200) }, SECRET, {
algorithm: 'HS256',
subject: username,
jwtid: jwtId,
}, (err, token) => {
if (err) {
logger.error(util.inspect(err));
}
resolve({ token, jwtId });
});
IN JAVA I READ USING "SECRET".getBytes("UTF-8"):
Claims claims = Jwts.parser().setSigningKey("SECRET".getBytes("UTF-8")).parseClaimsJws(token).getBody();
request.setAttribute("claims", claims);
I think the issue is the string secret. By calling getBytes("UTF-8") on your secret and providing signWith() with the byte[], everything will work out.
String username = Jwts.parser()
.setSigningKey(secret.getBytes("UTF-8"))
.parseClaimsJws(accessToken)
.getBody()
.getSubject();
(Also need to catch the UnsupportedEncodingException!)
I'm implementing a JWT solution and I'm using jose4j. I'm facing the problem that I have non-standard/custom key-pair value (such as "application-id" : 124123) in the body of the JWT that I want to verify, but I can't find the way to do it in Jose4J. I'd not mind an example.
The code below is from https://bitbucket.org/b_c/jose4j/wiki/JWT%20Examples#markdown-header-producing-and-consuming-a-signed-jwt and was modified slightly to show 'application-id' being included when creating the JWT and extracted after validation. You can verify the value at that point. Or you could also implement your own https://www.javadoc.io/doc/org.bitbucket.b_c/jose4j/latest/org/jose4j/jwt/consumer/Validator.html and set up your JwtConsomer with it when using the builder https://www.javadoc.io/static/org.bitbucket.b_c/jose4j/0.7.4/org/jose4j/jwt/consumer/JwtConsumerBuilder.html#registerValidator(org.jose4j.jwt.consumer.Validator)
//
// This example demonstrates producing and consuming a signed JWT
//
// Generate an RSA key pair, which will be used for signing and verification of the JWT, wrapped in a JWK
RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
// Give the JWK a Key ID (kid), which is just the polite thing to do
rsaJsonWebKey.setKeyId("k1");
// Create the Claims, which will be the content of the JWT
JwtClaims claims = new JwtClaims();
claims.setIssuer("Issuer"); // who creates the token and signs it
claims.setAudience("Audience"); // to whom the token is intended to be sent
claims.setExpirationTimeMinutesInTheFuture(10); // time when the token will expire (10 minutes from now)
claims.setGeneratedJwtId(); // a unique identifier for the token
claims.setIssuedAtToNow(); // when the token was issued/created (now)
claims.setNotBeforeMinutesInThePast(2); // time before which the token is not yet valid (2 minutes ago)
claims.setSubject("subject"); // the subject/principal is whom the token is about
claims.setClaim("email","mail#example.com"); // additional claims/attributes about the subject can be added
List<String> groups = Arrays.asList("group-one", "other-group", "group-three");
claims.setStringListClaim("groups", groups); // multi-valued claims work too and will end up as a JSON array
claims.setClaim("application-id", 124123);
// A JWT is a JWS and/or a JWE with JSON claims as the payload.
// In this example it is a JWS so we create a JsonWebSignature object.
JsonWebSignature jws = new JsonWebSignature();
// The payload of the JWS is JSON content of the JWT Claims
jws.setPayload(claims.toJson());
// The JWT is signed using the private key
jws.setKey(rsaJsonWebKey.getPrivateKey());
// Set the Key ID (kid) header because it's just the polite thing to do.
// We only have one key in this example but a using a Key ID helps
// facilitate a smooth key rollover process
jws.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId());
// Set the signature algorithm on the JWT/JWS that will integrity protect the claims
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
// Sign the JWS and produce the compact serialization or the complete JWT/JWS
// representation, which is a string consisting of three dot ('.') separated
// base64url-encoded parts in the form Header.Payload.Signature
// If you wanted to encrypt it, you can simply set this jwt as the payload
// of a JsonWebEncryption object and set the cty (Content Type) header to "jwt".
String jwt = jws.getCompactSerialization();
// Now you can do something with the JWT. Like send it to some other party
// over the clouds and through the interwebs.
System.out.println("JWT: " + jwt);
// Use JwtConsumerBuilder to construct an appropriate JwtConsumer, which will
// be used to validate and process the JWT.
// The specific validation requirements for a JWT are context dependent, however,
// it typically advisable to require a (reasonable) expiration time, a trusted issuer, and
// and audience that identifies your system as the intended recipient.
// If the JWT is encrypted too, you need only provide a decryption key or
// decryption key resolver to the builder.
JwtConsumer jwtConsumer = new JwtConsumerBuilder()
.setRequireExpirationTime() // the JWT must have an expiration time
.setAllowedClockSkewInSeconds(30) // allow some leeway in validating time based claims to account for clock skew
.setRequireSubject() // the JWT must have a subject claim
.setExpectedIssuer("Issuer") // whom the JWT needs to have been issued by
.setExpectedAudience("Audience") // to whom the JWT is intended for
.setVerificationKey(rsaJsonWebKey.getKey()) // verify the signature with the public key
.setJwsAlgorithmConstraints( // only allow the expected signature algorithm(s) in the given context
AlgorithmConstraints.ConstraintType.PERMIT, AlgorithmIdentifiers.RSA_USING_SHA256) // which is only RS256 here
.build(); // create the JwtConsumer instance
try
{
// Validate the JWT and process it to the Claims
JwtClaims jwtClaims = jwtConsumer.processToClaims(jwt);
System.out.println("JWT validation succeeded! " + jwtClaims);
long appId = jwtClaims.getClaimValue("application-id", Long.class);
System.out.println(appId);
}
catch (InvalidJwtException e)
{
// InvalidJwtException will be thrown, if the JWT failed processing or validation in anyway.
// Hopefully with meaningful explanations(s) about what went wrong.
System.out.println("Invalid JWT! " + e);
// Programmatic access to (some) specific reasons for JWT invalidity is also possible
// should you want different error handling behavior for certain conditions.
// Whether or not the JWT has expired being one common reason for invalidity
if (e.hasExpired())
{
System.out.println("JWT expired at " + e.getJwtContext().getJwtClaims().getExpirationTime());
}
// Or maybe the audience was invalid
if (e.hasErrorCode(ErrorCodes.AUDIENCE_INVALID))
{
System.out.println("JWT had wrong audience: " + e.getJwtContext().getJwtClaims().getAudience());
}
}`
// Custom key-pair
String publicKeyStr = "encoded public key";
String privateKeyStr = "encoded private key";
KeyPair keyPair = RSAUtil.parser(publicKeyStr, privateKeyStr);
RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
rsaJsonWebKey.setPrivateKey(keyPair.getPrivate());
JwtClaims claims = new JwtClaims();
// ...
JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(claims.toJson());
jws.setKey(rsaJsonWebKey.getPrivateKey());
jws.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId());
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
String jwt = jws.getCompactSerialization();
JwtConsumer jwtConsumer = new JwtConsumerBuilder()
// ...
.setVerificationKey(keyPair.getPublic())
// ...
.build();
JwtClaims jwtClaims = jwtConsumer.processToClaims(jwt);
I'm trying to integrate Apple Map Web Snapshot which needs a signature query parameter in the URL. I able to successfully generate and validate ES256 signature in JWA package from NPM but not in Java. Please help me on finding equivalent lib to generate valid signature, I have tried few JWA libs in Java.
// Required modules.
const { readFileSync } = require("fs");
const { sign } = require("jwa")("ES256");
/* Read your private key from the file system. (Never add your private key
* in code or in source control. Always keep it secure.)
*/
const privateKey = readFileSync("[file_system_path]");
// Replace the team ID and key ID values with your actual values.
const teamId = "[team ID]";
const keyId = "[key ID]";
// Creates the signature string and returns the full Snapshot request URL including the signature.
function sign(params) {
const snapshotPath = `/api/v1/snapshot?${params}`;
const completePath = `${snapshotPath}&teamId=${teamId}&keyId=${keyId}`;
const signature = sign(completePath, privateKey);
// In this example, the jwa module returns the signature as a Base64 URL-encoded string.
// Append the signature to the end of the request URL, and return.
return `${completePath}&signature=${signature}`;
}
// Call the sign function with a simple map request.
sign("center=apple+park")
// The return value expected is: "/api/v1/snapshot?center=apple+park&teamId=[team ID]&keyId=[key ID]&signature=[base64_url_encoded_signature]"
Apache CXF - This lib generates similar to JWA module in node but failed to authenticate.
String teamId = [Team Id];
String keyId = [Key id];
String privateKey = [private key path];
String privateKeyContent = getKeyFileContent(privateKey);
String API_VERSION_PATH = "/api/v1/snapshot?";
String param = [QueryParam];
//example -> param = "center=[city,country or lat,lang]&size=90x90&lang=en&radius=2";
String params = param + "&teamId="+ teamId + "&keyId=" + keyId;
String payload = API_VERSION_PATH + params;
PrivateKey key = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(
Base64.decodeBase64(privateKeyContent)));
JwsCompactProducer compactProducer = new JwsCompactProducer(payload);
compactProducer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.ES256);
//compactProducer.getJwsHeaders().setKeyId(keyId);
compactProducer.signWith(key);
String signed = compactProducer.getEncodedSignature();
String encodedSignature = new String(Base64.encodeBase64URLSafe(compactProducer.getEncodedSignature().getBytes()));
System.out.println(SNAPSHOT_API_PATH + payload + "&signature=" + signed);
JJWT - This lib generates big signature then the signature generated in node module.
String signed = new String(Base64.encodeBase64URLSafe(Jwts.builder().setPayload(payload)
.signWith(io.jsonwebtoken.SignatureAlgorithm.ES256, key).compact().getBytes()));
System.out.println(SNAPSHOT_API_PATH + payload + "&signature=" + signed);
sample output signature
compactProducer.getEncodedSignature() signed --> qQ5G9_lwGJ9w158FVSmtPx_iH43xlg2_gx9BlHEJbER73xpAeIHtDRnT8wnveH_UEPxNe7Zgv4csJ48Oiq-ZIQ
Base64.encodeBase64URLSafe(signature) --> cVE1RzlfbHdHSjl3MTU4RlZTbXRQeF9pSDQzeGxnMl9neDlCbEhFSmJFUjczeHBBZUlIdERSblQ4d252ZUhfVUVQeE5lN1pndjRjc0o0OE9pcS1aSVE
JJWT signed -> ZXlKaGJHY2lPaUpGVXpJMU5pSjkuTDJGd2FTOTJNUzl6Ym1Gd2MyaHZkRDlqWlc1MFpYSTlRM1Z3WlhKMGFXNXZMRlZUUVNaMFpXRnRTV1E5V0ZaWU5GWlhSbEZUTXlaclpYbEpaRDFWUVRWTlNGWlhWMWhMLlExUEtoeGwzSjFoVWVUWGtmeXRLckliYm5zeDdZem5lZVpxTVc4WkJOVU9uLVlYeFhyTExVU05ZVTZCSG5Xc3FheFd3YVB5dlF0Yml4TVBSZGdjamJ3
The signature in the NodeJS code is generated by the jwa('ES256')#sign method, which has the following functionality:
ES256: ECDSA using P-256 curve and SHA-256 hash algorithm [1].
The signature will be the pair (r, s), where r and s are 256-bit unsigned integers [2].
The signature is base64url-encoded [3].
Ad 1: A corresponding implementation for ES256 is possible in Java using on-board means (SunEC provider, Java 1.7 or higher), [4]:
Signature ecdsa = Signature.getInstance("SHA256withECDSA");
ecdsa.initSign(privateKey);
String payload = "The quick brown fox jumps over the lazy dog";
ecdsa.update(payload.getBytes(StandardCharsets.UTF_8));
byte[] signatureDER = ecdsa.sign();
Here privateKey is the private key of type java.security.PrivateKey, analogous to key in the CXF code.
Ad 2: The Java code returns the signature in the ASN.1 DER format and must therefore be converted into the (r,s) format [5]. Either a user-defined method can be implemented or a method from a supporting library can be used, e.g. the method com.nimbusds.jose.crypto.impl.ECDSA.transcodeSignatureToConcat of the Nimbus JOSE + JWT library [6][7][8]:
byte[] signature = transcodeSignatureToConcat(signatureDER, 64);
Ad 3: Base64url encoding is possible in Java with on-board means [9]:
String signatureBase64url = Base64.getUrlEncoder().withoutPadding().encodeToString(signature);
Since a different signature is generated each time, a direct comparison of the signatures generated in both codes isn't possible. However, compatibility with the jwa-npm library can be tested by verifying the signature generated in the Java code with the jwa-npm library:
const jwa = require("jwa");
const ecdsa = jwa('ES256');
var message = "The quick brown fox jumps over the lazy dog";
var verify = ecdsa.verify(message, signatureBase64url, publicKey);
Here, signatureBase64url is the signature generated with the Java code. publicKey is the corresponding public key in X.509 PEM format (-----BEGIN PUBLIC KEY-----...) [10].
The functionality of the jwa('ES256')#sign method is different from that of the posted JJWT or Apache CXF code: The last two generate a JWT [11]. The header is the base64url encoding of {"alg": "ES256"}. Accordingly the signature is that for the Base64url encoded header and the Base64url encoded payload, both separated by a dot:
String payload = "The quick brown fox jumps over the lazy dog";
//JJWT
String jwtJJWT = Jwts.builder().setPayload(payload).signWith(io.jsonwebtoken.SignatureAlgorithm.ES256, privateKey).compact();
//CXF
JwsCompactProducer compactProducer = new JwsCompactProducer(payload);
compactProducer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.ES256);
String jwtCXF = compactProducer.signWith(privateKey);
String signatureCXF = compactProducer.getEncodedSignature(); // signature, 3. portion of JWT
Example for a JWT generated by this:
eyJhbGciOiJFUzI1NiJ9.VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw.rcrzqr3ovu7SH9ci-S6deLn6BuiQkNv9CmeOnUPwva30pfK9BOX0YOgjZP5T08wjxCBTTHV3aex0M76toL8qpw
I am working to integrate my application with the Withings api with spring rest template.
But,while generating the Oauth signature I am getting the "Invalid signature". I am trying to devise a signature according to the API specification but I am not able to generate it successfully. I have mentioned the code that I used. Please, provide me some solutions.
private String generateSignature(String baseString, String secret) throws UnsupportedEncodingException {
String secretKey = consumerSecret + "&";
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), HMAC_SHA1SignatureMethod.SIGNATURE_NAME);
HMAC_SHA1SignatureMethod hmacsha = new HMAC_SHA1SignatureMethod(keySpec);
String signatureString = hmacsha.sign(baseString);
String base64Encode = new String((signatureString.getBytes()));
signature = URLEncoder.encode(base64Encode, "UTF-8");
For reference, http://oauth.withings.com/api
i faced the same issues before , it seems the signature need you params ( api params + oauth params ) to be ordered alphabetically .
You need also to give a correct secret word in making you signature based uri .
you can check if you want my php oauth lib ( more particulary in AbstractService.php ) for withing here
https://github.com/huitiemesens/PHPoAuthLib ( it s a fork of the original phpoauthlib with specific order for withings apis ... )