Created
October 15, 2018 15:06
-
-
Save justindav1s/8a2bd46268cec34aef42dc84cc452229 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 DirectGrantTokenValidationTests { | |
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 = "tpp1"; | |
String reg_clientSecret = "b38eae9d-d5ef-4a98-b1e6-6b5084b09d91"; | |
String user = "test_user2"; | |
String password = "123456"; | |
private Log log = LogFactory.getLog(DirectGrantTokenValidationTests.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()); | |
System.out.println("Type : "+parsedToken.getPreferredUsername()); | |
} 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=password&client_id="+reg_clientId+"&client_secret="+reg_clientSecret+"&username="+user+"&password="+password; | |
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); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You can also do so using the Keycloak SDK