Obtaining certificate from thumbprint of a JWT

I think you are familiar with JWT (Jason Web Token) . There is a use case that JWT is used to pass user information from WSO2 API Manager to ESB. You can get more knowledge on this from here.

First go through the above link and create the working setup for the above mentioned use case. Then we can have a look at how to extend it to get the certificate used to sign the JWT.

Certificate that is used to sign the JWT can not be obtained from message context directly , you will have to more than reading a property from the message context. As an abstract ,steps are as below.
1. Get the JWT
2.Decode it
3.get Thumbprint
4.get alias of thumprint
5.get certificate from the keystore of the tenant while providing alias acquired in thumbprint.

So let’s get started….

Encoded JWT is coming as a HTTP header ,so first you have to set the a encoded JWT to the message context as a property. It can be done using following code segment at the appropriate place in the synapse-config.


<property name="authheader" expression="get-property('transport','X-JWT-Assertion')"></property>

So now, This property can be retrieved from the message context programmatically as mentioned below.


String auth = (String) context.getProperty("authheader");
 

now in the String auth, there is encoded JWT. It can be decoded using as mentioned below.

byte[] decoded = Base64.decode(auth);

String dec = new String(decoded, "UTF-8").trim();//get jwt token
 

now in the variable dec , you can get the decoded jwt. which is look like

{"typ":"JWT","alg":"SHA256withRSA","x5t":"NmJmOGUxMzZlYjM2ZDRhNTZlYTA1YzdhZTRiOWE0NWI2M2JmOTc1ZA=="}{"iss":"wso2.org/products/am","exp":1386239361238,"http://wso2.org/claims/subscriber":"admin","http://wso2.org/claims/applicationid":"1","http://wso2.org/claims/applicationname":"DefaultApplication","http://wso2.org/claims/applicationtier":"Unlimited","http://wso2.org/claims/apicontext":"/test1","http://wso2.org/claims/version":"1.0.0","http://wso2.org/claims/tier":"Unlimited","http://wso2.org/claims/keytype":"PRODUCTION","http://wso2.org/claims/usertype":"APPLICATION","http://wso2.org/claims/enduser":"admin","http://wso2.org/claims/enduserTenantId":"-1234", "http://wso2.org/claims/role":"everyone,admin"}

In this JWT, Thumprint is available as element “x5t” ,in this case it is

"x5t":"NmJmOGUxMzZlYjM2ZDRhNTZlYTA1YzdhZTRiOWE0NWI2M2JmOTc1ZA=="

Thumbprint can be retrieved by following method ,which requires decoded string of the jwt.

private String getThumbPrint(String jwt) {
		String[] elements = jwt.split(",");
		for (String string : elements) {
			if (string.indexOf("x5t") == 1) {
				String tmpResult = string.split(":")[1].trim();
				String result = StringUtils.substringsBetween(tmpResult, "\"", "\"")[0];
				return result;

			}
		}
		return null;
	}

Now let’s keep this aside for a while….

We need the keystore manager of the tenant. Following line of code can be used to get the key store manager of the tenant.

		KeyStoreManager tenantKSM = KeyStoreManager.getInstance(tenantId);

in wso2 products there is convetion used to name keystores for tenant, so key store for tenant is a wso2 product can be used as below.


String ksName = tenantDomain.trim().replace(".", "-");
String jksName = ksName + ".jks";
keyStore = tenantKSM.getKeyStore(jksName);

Right, let’s get back to thumbprint….

This thumbprint we get from the JWT ,it is also encoded before getting the alias ,it must be decoded. in the following code segment it is shown that how to decode it and retrieving the alias, which is required when obtaining the certificate.


String alias = getAliasForX509CertThumb(decodedThumb, keyStore);//get the alias
 

getAliasForX509CertThumb method gives the alias of the thumbprint, this method requires decoded thumb print,which is a byte array and the keystore we obtained in earlier step.


private String getAliasForX509CertThumb(byte[] thumb, KeyStore keyStore) {
		Certificate cert = null;
		MessageDigest sha = null;

		try {
			sha = MessageDigest.getInstance("SHA-1");
		} catch (NoSuchAlgorithmException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
		}

		try {
			for (Enumeration e = keyStore.aliases(); e.hasMoreElements();) {
				String alias = (String) e.nextElement();
				Certificate[] certs = keyStore.getCertificateChain(alias);
				if (certs == null || certs.length == 0) {
					// no cert chain, so lets check if getCertificate gives us a
					// result.
					cert = keyStore.getCertificate(alias);
					if (cert == null) {
						return null;
					}
				} else {
					cert = certs[0];
				}
				if (!(cert instanceof X509Certificate)) {
					continue;
				}
				sha.reset();
				try {
					sha.update(cert.getEncoded());
				} catch (CertificateEncodingException e1) {
				}
				byte[] data = sha.digest();
				if (new String(thumb).equals(hexify(data))) {
					return alias;
				}
			}
		} catch (KeyStoreException e) {
		}
		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' };

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

		for (int i = 0; i < bytes.length; ++i) {
			buf.append(hexDigits[(bytes[i] & 0xf0) >> 4]);
			buf.append(hexDigits[bytes[i] & 0x0f]);
		}

		return buf.toString();
	}

 

Now we can get the certificate by passing the alias to the keystore.


Certificate certificate = keyStore.getCertificate(alias);//get the certificate for the particular thumb print
 

Now we are done … you can do whatever you want to do with the certificate now 🙂 ..
You can find the source code for the custom mediator written for wso2esb at [1]. If you are running the sample ,you have to do it as below in the synapse config.

    <property name="authheader" expression="get-property('transport','X-JWT-Assertion')"/>
    <class name="org.wso2.carbon.esb.jwthandler.JWTHandler"/>

[1] https://svn.wso2.org/repos/wso2/people/asankad/JWTHandler/

Advertisements

One thought on “Obtaining certificate from thumbprint of a JWT

  1. Thank you very much, Asanka. This post is very helpful. We have an instance of API Manager and a backend API service (published as an API in APIM) running on separate servers. Is this the way to verify the validity of the JWT or is there another method? In other words, how can the backend service trust that the request came from APIM?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s