Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save justindav1s/cf3f405e1aab4cc74550c25b8a84d384 to your computer and use it in GitHub Desktop.
Save justindav1s/cf3f405e1aab4cc74550c25b8a84d384 to your computer and use it in GitHub Desktop.
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.keycloak.RSATokenVerifier;
import org.keycloak.common.VerificationException;
import org.keycloak.representations.AccessToken;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.Base64;
@RunWith(SpringRunner.class)
@SpringBootTest
public class TokenValidationTests {
String realm = "demo";
String baseUrl = "http://127.0.0.1:8080";
String realmUrl = baseUrl+"/auth/realms/"+realm;
String certs = baseUrl+"/auth/realms/"+realm+"/protocol/openid-connect/certs";
String reg_clientId = "tpp-registration-client";
String reg_clientSecret = "ee01f93a-5dcc-451d-bba3-25ef2a996245";
private Log log = LogFactory.getLog(TokenValidationTests.class);
@Test
public void verifyToken() {
String accessToken = getToken();
log.info("Access Token : "+accessToken);
String kid = getKeyId(accessToken);
log.info("KID : "+kid);
PublicKey publicKey = getPublicKey(kid);
try {
RSATokenVerifier rsTV = RSATokenVerifier.create(accessToken);
System.out.println("JWS Algorithm : "+rsTV.getHeader().getAlgorithm());
System.out.println("JWS Type : "+rsTV.getHeader().getType());
System.out.println("JWS Key Id : "+rsTV.getHeader().getKeyId());
AccessToken parsedToken = RSATokenVerifier.verifyToken(accessToken, publicKey, realmUrl,false, true);
System.out.println("Issued for : "+parsedToken.issuedFor);
System.out.println("Issuer : "+parsedToken.getIssuer());
System.out.println("Type : "+parsedToken.getType());
} catch (VerificationException e) {
e.printStackTrace();
}
}
public String getToken () {
RestTemplate restTemplate = new RestTemplate();
log.info("new token");
String uri = baseUrl+"/auth/realms/"+realm+"/protocol/openid-connect/token";
log.info("Token URL : "+uri);
String post_body = "grant_type=client_credentials&client_id="+reg_clientId+"&client_secret="+reg_clientSecret;
log.info("Post body : "+post_body);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/x-www-form-urlencoded");
HttpEntity<String> request = new HttpEntity<>(post_body, headers);
ResponseEntity<String> exchange =
restTemplate.exchange(
uri,
HttpMethod.POST,
request,
String.class);
String response = exchange.getBody();
log.info("Token Response : "+response);
String accessToken = null;
try {
JSONObject obj = new JSONObject(response);
accessToken = obj.getString("access_token");
} catch (JSONException e) {
e.printStackTrace();
}
log.info("Access Token : "+accessToken);
return accessToken;
}
public String getKeyId(String accessToken) {
//get the JWT header
String tokenHeader = accessToken.split("\\.")[0];
log.info("Token Header : "+tokenHeader);
//Base64 decode it
tokenHeader = new String(Base64.getDecoder().decode(tokenHeader.getBytes()));
log.info("Token Header JSon : "+tokenHeader);
String kid = null;
try {
JSONObject obj = new JSONObject(tokenHeader);
// extract the kid value from the header
kid = obj.getString("kid");
} catch (JSONException e) {
e.printStackTrace();
}
log.info("KID : "+kid);
return kid;
}
public PublicKey getPublicKey(String kid) {
PublicKey publicKey = null;
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> exchange =
restTemplate.exchange(
certs,
HttpMethod.GET,
null,
String.class);
String response = exchange.getBody();
try {
String modulusStr = null;
String exponentStr = null;
JSONObject obj = new JSONObject(response);
// extract the kid value from the header
JSONArray keylist = obj.getJSONArray("keys");
for (int i = 0; i < keylist.length(); i++) {
JSONObject key = keylist.getJSONObject(i);
String id = key.getString("kid");
if (kid.equals(id)) {
modulusStr = key.getString("n");
exponentStr = key.getString("e");
}
}
log.info("Modulus : "+modulusStr);
log.info("Exponent : "+exponentStr);
BigInteger modulus = new BigInteger(1, base64Decode(modulusStr));
BigInteger publicExponent = new BigInteger(1, base64Decode(exponentStr));
try {
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(new RSAPublicKeySpec(modulus, publicExponent));
} catch (Exception e) {
throw new RuntimeException(e);
}
} catch (Exception e) {
e.printStackTrace();
}
return publicKey;
}
// The Base64 strings that come from a JWKS need some manipilation before they can be decoded.
// we do that here
public byte[] base64Decode(String base64) throws IOException {
base64 = base64.replaceAll("-", "+");
base64 = base64.replaceAll("_", "/");
switch (base64.length() % 4) // Pad with trailing '='s
{
case 0:
break; // No pad chars in this case
case 2:
base64 += "==";
break; // Two pad chars
case 3:
base64 += "=";
break; // One pad char
default:
throw new RuntimeException(
"Illegal base64url string!");
}
return Base64.getDecoder().decode(base64);
}
}
@justindav1s
Copy link
Author

Requires Maven dependencies :

	<!-- https://mvnrepository.com/artifact/org.keycloak/keycloak-core -->
	<dependency>
		<groupId>org.keycloak</groupId>
		<artifactId>keycloak-core</artifactId>
		<version>3.4.3.Final</version>
	</dependency>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment