Validate and Process JWT tokens with Java

Lets see how we can process and validate the JWT token using simple java code.   We have generated a sample JWT token from WSO2IS.. You can find it in following

 

eyJ0eXAiOiJKV1QiLCJhbGciOiJTSEEyNTZ3aXRoUlNBIiwieDV0IjoiTm1KbU9HVXhNelpsWWpNMlpEUmhOVFpsWVRBMVl6ZGhaVFJpT1dFME5XSTJNMkptT1RjMVpBIn0.eyJpc3MiOiJodHRwOi8vd3NvMi5vcmcvZ2F0ZXdheSIsImV4cCI6MTQyNjczODI1MDkzNSwiaHR0cDovL3dzbzIub3JnL2dhdGV3YXkvc3Vic2NyaWJlciI6ImFkbWluIiwiaHR0cDovL3dzbzIub3JnL2dhdGV3YXkvYXBwbGljYXRpb25uYW1lIjoiT3BlbmlkQ29ubmVjdCIsImh0dHA6Ly93c28yLm9yZy9nYXRld2F5L2VuZHVzZXIiOiJhc2VsYUBjYXJib24uc3VwZXIiLCAiaHR0cDovL3dzbzIub3JnL2NsYWltcy9jb3VudHJ5IjoiVW5pdGVkIFN0YXRlcyIsICJodHRwOi8vd3NvMi5vcmcvY2xhaW1zL2VtYWlsYWRkcmVzcyI6ImFzZWxhQHNvYXNlY3VyaXR5Lm9yZyIsICJodHRwOi8vd3NvMi5vcmcvY2xhaW1zL2Z1bGxuYW1lIjoiYXNlbGEiLCAiaHR0cDovL3dzbzIub3JnL2NsYWltcy9naXZlbm5hbWUiOiJBc2VsYSIsICJodHRwOi8vd3NvMi5vcmcvY2xhaW1zL2xhc3RuYW1lIjoiUGF0aGJlcml5YSIsICJodHRwOi8vd3NvMi5vcmcvY2xhaW1zL29yZ2FuaXphdGlvbiI6InNvYXNlY3VyaXR5Lm9yZyIsICJodHRwOi8vd3NvMi5vcmcvY2xhaW1zL3JvbGUiOiJJbnRlcm5hbC9ldmVyeW9uZSIsICJodHRwOi8vd3NvMi5vcmcvY2xhaW1zL3Nob3ciOiJVbml0ZWQgU3RhdGVzIiwgImh0dHA6Ly93c28yLm9yZy9jbGFpbXMvc3R1ZGlvIjoiQXNlbGEiLCAiaHR0cDovL3dzbzIub3JnL2NsYWltcy90ZWxlcGhvbmUiOiIrOTQ3Nzc2MjU5MzMifQ.d57VGVAhZmTpIMl8hiIUO8D7hAZl-bZm5TnDW9si3qnHFliMHsxlE6HJ7bSjmoobIgdqJ7xToWtOm2orrQKFxzF4xxkpNeU1-qGFoG6-IyRF-JAJao0xq6WIGk8fR2BSN_zxsNbR84-3FMWd6mljPnImWYLe_8mOBFyDcsuDCkk
It has been signed using RS256 (RSA algorithm using SHA-256).  WSO2IS uses its primary keystore to sign the JWT token. By default primary keystore is “wso2carbon.jks” file.  Therefore this JWT token has been signed using private key of “wso2carbon.jks” file.  So,  To validate the signature of JWT, we need the public certificate of “wso2carbon.jks” file.
Please note, we are  using Apache common codec library for Base64 Url decoding and simple json  library for building the JSON  object.  You can find them from here.
You just need to add above libraries in to following code and run it.  Please note,  you need to configure a trust store which contains the  public certificate of wso2carbon.jks” file.  You can find it from here.   You can use this for validation and processing purpose of JWT token (specially retrieved from WSO2IS )..  Java class can be found from here as well.

import org.apache.commons.codec.binary.Base64;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.Signature;
import java.security.cert.Certificate;
import java.util.Enumeration;

/**
 * Simple JWT processor. This is specially written for JWT generated by WSO2IS
 */
public class SimpleJWTProcessor {

 private static final Base64 base64Url = new Base64(true);

 private static final String trustStore = "/trust-store/client-truststore.jks" ;

 private static final String trustStorePassword = "wso2carbon";

 private JSONObject jsonHeaderObject;

 private JSONObject jsonClaimObject;

 private String jwtToken;

 public SimpleJWTProcessor(String jwtToken) {
 this.jwtToken = jwtToken;
 }

 protected boolean isValid(){

 String[] jwtTokenValues = jwtToken.split("\\.");
 String jwtAssertion = null;
 byte[] jwtSignature = null;

 if(jwtTokenValues.length > 0){
 String value = new String(base64Url.decode(jwtTokenValues[0].getBytes()));
 System.out.println("JWT Header : " + value);

 JSONParser parser = new JSONParser();
 try {
 jsonHeaderObject = (JSONObject) parser.parse(value);
 } catch (Exception e) {
 e.printStackTrace();
 }
 }

 if(jwtTokenValues.length > 1){

 String value = new String(base64Url.decode(jwtTokenValues[1].getBytes()));
 System.out.println("JWT Body : " + value);
 jwtAssertion = jwtTokenValues[0] + "." + jwtTokenValues[1];

 JSONParser parser = new JSONParser();
 try {
 jsonClaimObject = (JSONObject) parser.parse(value);
 } catch (Exception e) {
 e.printStackTrace();
 }
 }

 if(jwtTokenValues.length > 2){
 jwtSignature = base64Url.decode(jwtTokenValues[2].getBytes());
 }

 KeyStore keyStore = null;
 String thumbPrint = new String(base64Url.decode(((String) jsonHeaderObject.get("x5t")).getBytes()));
 String signatureAlgo = (String) jsonHeaderObject.get("alg");

 if("RS256".equals(signatureAlgo)){
 signatureAlgo = "SHA256withRSA";
 } else if("RS515".equals(signatureAlgo)){
 signatureAlgo = "SHA512withRSA";
 } else if("RS384".equals(signatureAlgo)){
 signatureAlgo = "SHA384withRSA";
 } else {
 // by default
 signatureAlgo = "SHA256withRSA";
 }

 if(jwtAssertion != null && jwtSignature != null) {

 try {
 keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
 keyStore.load(new FileInputStream(trustStore), trustStorePassword.toCharArray());
 String alias = getAliasForX509CertThumb(thumbPrint.getBytes(), keyStore);
 Certificate certificate = keyStore.getCertificate(alias);
 Signature signature = Signature.getInstance(signatureAlgo);
 signature.initVerify(certificate);
 signature.update(jwtAssertion.getBytes());
 return signature.verify(jwtSignature);
 } catch (Exception e) {
 e.printStackTrace();
 }
 } else {
 System.err.println("Signature is null");
 }
 return false;

 }

 private String getAliasForX509CertThumb(byte[] thumb, KeyStore keyStore) {

 Certificate cert = null;
 MessageDigest sha = null;

 try {
 sha = MessageDigest.getInstance("SHA-1");
 for (Enumeration e = keyStore.aliases(); e.hasMoreElements();) {
 String alias = (String) e.nextElement();
 Certificate[] certs = keyStore.getCertificateChain(alias);
 if (certs == null || certs.length == 0) {
 cert = keyStore.getCertificate(alias);
 if (cert == null) {
 return null;
 }
 } else {
 cert = certs[0];
 }
 sha.update(cert.getEncoded());
 byte[] data = sha.digest();
 if (new String(thumb).equals(hexify(data))) {
 return alias;
 }
 }
 } catch (Exception e) {
 e.printStackTrace();
 }
 return null;
 }

 private String hexify(byte bytes[]) {

 char[] hexDigits =
 { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
 'e', 'f' };

 StringBuilder buf = new StringBuilder(bytes.length * 2);

 for (byte aByte : bytes) {
 buf.append(hexDigits[(aByte & 0xf0) >> 4]);
 buf.append(hexDigits[aByte & 0x0f]);
 }

 return buf.toString();
 }

 public JSONObject getJsonHeaderObject() {
 return jsonHeaderObject;
 }

 public JSONObject getJsonClaimObject() {
 return jsonClaimObject;
 }

 public static void main(String[] args){

 // sample JWT from WSO2IS

 String jwtToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJTSEEyNTZ3aXRoUlNBIiwieDV0IjoiTm1KbU9HVXhNelpsWWpNMlpEUmhOVFpsWVRBMVl6ZGhaVFJpT1dFME5XSTJNMkptT1RjMVpBIn0.eyJpc3MiOiJodHRwOi8vd3NvMi5vcmcvZ2F0ZXdheSIsImV4cCI6MTQyNjczODI1MDkzNSwiaHR0cDovL3dzbzIub3JnL2dhdGV3YXkvc3Vic2NyaWJlciI6ImFkbWluIiwiaHR0cDovL3dzbzIub3JnL2dhdGV3YXkvYXBwbGljYXRpb25uYW1lIjoiT3BlbmlkQ29ubmVjdCIsImh0dHA6Ly93c28yLm9yZy9nYXRld2F5L2VuZHVzZXIiOiJhc2VsYUBjYXJib24uc3VwZXIiLCAiaHR0cDovL3dzbzIub3JnL2NsYWltcy9jb3VudHJ5IjoiVW5pdGVkIFN0YXRlcyIsICJodHRwOi8vd3NvMi5vcmcvY2xhaW1zL2VtYWlsYWRkcmVzcyI6ImFzZWxhQHNvYXNlY3VyaXR5Lm9yZyIsICJodHRwOi8vd3NvMi5vcmcvY2xhaW1zL2Z1bGxuYW1lIjoiYXNlbGEiLCAiaHR0cDovL3dzbzIub3JnL2NsYWltcy9naXZlbm5hbWUiOiJBc2VsYSIsICJodHRwOi8vd3NvMi5vcmcvY2xhaW1zL2xhc3RuYW1lIjoiUGF0aGJlcml5YSIsICJodHRwOi8vd3NvMi5vcmcvY2xhaW1zL29yZ2FuaXphdGlvbiI6InNvYXNlY3VyaXR5Lm9yZyIsICJodHRwOi8vd3NvMi5vcmcvY2xhaW1zL3JvbGUiOiJJbnRlcm5hbC9ldmVyeW9uZSIsICJodHRwOi8vd3NvMi5vcmcvY2xhaW1zL3Nob3ciOiJVbml0ZWQgU3RhdGVzIiwgImh0dHA6Ly93c28yLm9yZy9jbGFpbXMvc3R1ZGlvIjoiQXNlbGEiLCAiaHR0cDovL3dzbzIub3JnL2NsYWltcy90ZWxlcGhvbmUiOiIrOTQ3Nzc2MjU5MzMifQ.d57VGVAhZmTpIMl8hiIUO8D7hAZl-bZm5TnDW9si3qnHFliMHsxlE6HJ7bSjmoobIgdqJ7xToWtOm2orrQKFxzF4xxkpNeU1-qGFoG6-IyRF-JAJao0xq6WIGk8fR2BSN_zxsNbR84-3FMWd6mljPnImWYLe_8mOBFyDcsuDCkk";

 SimpleJWTProcessor processor = new SimpleJWTProcessor(jwtToken);

 if(processor.isValid()){
 JSONObject body = processor.getJsonClaimObject();

 // retrieve claims
 String email = (String) body.get("http://wso2.org/claims/emailaddress");

 System.out.println("Email : " + email);
 } else {
 System.err.println("Signature verification failed.");
 }

 }
}
Thanks for reading….!!!
Discuss this article on Stack Overflow

Leave a Reply

Your email address will not be published. Required fields are marked *