How to verify custom key-pair value in a JWT body - java

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

Related

Get expiry date from firebase token (JOSE header "typ" (type) "JWT" not allowed)

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)

JWT Signing in NodeJS and but unable to verify in Java

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!)

Send embedded signing email with setting signing order

So basically I need to use embedded signing feature to get the URL and embed into my application, and then my customer can sign the document from my side. Apart from that, after my customer signed on the doc, he needs to ask his debtor to sign on the same doc as well.
So on DocuSign UI, I found that I can set a signing order, which means the second recipient receives the email right after the first recipient signed (perfect match my requirement).
setting on UI
However, the second recipient can not receive the email after the first signer signed even though on UI it says sent.
public Envelope embeddedSigning(Long debtorId, String signerEmail, String signerName, String templateId) throws ApiException, IOException {
// create an envelop
EnvelopeDefinition envelope = makeEnvelope(debtorId, signerEmail, signerName, templateId);
ApiClient apiClient = baseRestApiClient();
apiClient.addDefaultHeader("Authorization", "Bearer " + getToken());
EnvelopesApi envelopesApi = new EnvelopesApi(apiClient);
EnvelopeSummary summary = envelopesApi.createEnvelope(accountId, envelope);
RecipientViewRequest viewRequest = makeRecipientViewRequest(debtorId, signerEmail, signerName);
ViewUrl viewUrl = envelopesApi.createRecipientView(accountId, summary.getEnvelopeId(), viewRequest);
// #formatter:off
return Envelope.builder()
.envelopId(summary.getEnvelopeId())
.redirectUrl(viewUrl.getUrl()).build();
// #formatter:on
}
private EnvelopeDefinition makeEnvelope(Long debtorId, String signerEmail, String signerName, String templateId) throws IOException {
EnvelopeDefinition envelopeDefinition = new EnvelopeDefinition();
envelopeDefinition.setEmailSubject("Please sign this document");
envelopeDefinition.setTemplateId(templateId);
TemplateRole signer = new TemplateRole();
signer.setEmail(signerEmail);
signer.setName(signerName);
signer.clientUserId(String.valueOf(debtorId));
signer.setRoleName("signer0");
signer.setRoutingOrder("1");
TemplateRole signer1 = new TemplateRole();
signer1.setEmail("xxx");
signer1.setName("xxx");
signer1.clientUserId(String.valueOf(xxx));
signer1.setRoleName("signer1");
signer1.setRoutingOrder("2");
envelopeDefinition.setTemplateRoles(Arrays.asList(signer, signer1));
envelopeDefinition.setStatus("sent");
return envelopeDefinition;
}
You are setting signer1.clientUserId(String.valueOf(xxx)); which means you are making signer an embedded signer. By Default, DocuSign does not send email to the embedded signer. By making a signer as embedded signer, you are telling DocuSign that calling App will take care of deciding when to host signing ceremony for this signer so DocuSign will not send an email as they will not be doing signing from the email, instead it be your App which will be generating Signing URL when that signer is on your App. So if you remove signer1.clientUserId(String.valueOf(xxx)); code then you will see that signer1 will get an email from DocuSign.
Docs has more details about embedded signing.
Typically routing order starts at 1. so it should be 1 and 2, not 0 and 1.
Apart from that, "Sent" is the status for the entire envelope. The envelope goes to routing order 1 first. Then, when all recipients of routing order 1 finished signing, it goes to 2 etc. I'm not sure if you actually have an issue here, but please confirm after you changed to 1 and 2 what exactly do you see that you don't expect.

Subject must match Issuer claim in the client assertion

I want to integrate office365 service management API for collecting events from it.I want to use client credential way to use service to service call but i am getting following error,
{
"error":"invalid_client",
"error_description":"AADSTS50048: Subject must match Issuer claim in the client assertion.
\r\nTrace ID: 1ad7acd8-3945-4fe0-a313-07638eb76e42\r\nCorrelation ID: a6c3a3c9-b737-4bfc-894f-3086c3ce8dfa\r\nTimestamp: 2016-06-09 07:20:15Z",
"error_codes":[50048
],
"timestamp":"2016-06-09 07:20:15Z",
"trace_id":"1ad7acd8-3945-4fe0-a313-07638eb76e42",
"correlation_id":"a6c3a3c9-b737-4bfc-894f-3086c3ce8dfa"
}
i use following doc to integration,
For getting client assersion,
https://msdn.microsoft.com/en-us/library/azure/dn645543.aspx I am getting this. But for Access token,
https://msdn.microsoft.com/en-us/library/office/dn707383.aspx I not getting this as a response getting above error.
Somebody help me please :)
How did you get the client_assertion? The link you provide doesn’t describe how to get the ‘client_assertion’. It acquire the token with the app’s id and secret which is doesn’t support for the Office 365 Management API. You can refer the blog to about the ‘client_assertion’.
And here is an C# code sample which use the ADAL to get the access token for the client credentials flow:
string clientId = "{clientId}";
string certThumbprint = "‎{copy from mmc}";
certThumbprint = certThumbprint.Replace("\u200e", string.Empty).Replace("\u200f", string.Empty).Replace(" ", string.Empty);
string apiResourceId = "https://manage.office.com";
X509Certificate2 cert = null;
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
string authority = "https://login.windows.net/{yourTentant}";
var authContext = new AuthenticationContext(authority);
try
{
store.Open(OpenFlags.ReadOnly);
cert = store.Certificates.Find(X509FindType.FindByThumbprint, certThumbprint, false)[0];
}
finally
{
store.Close();
}
var certCred = new ClientAssertionCertificate(clientId, cert);
AuthenticationResult result = null;
try
{
result = await authContext.AcquireTokenAsync(apiResourceId, certCred);
}
catch (Exception ex)
{
}

Creating token with JWT in JAVA [duplicate]

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;
}

Categories