2

I'm trying to validate the RS256 token that I receive upon successful user authorization on the AD server.

I am looking at the id_token property value of the response.

Here's what I have -

String[] split_string = idToken.split("\\.");
String base64EncodedHeader = split_string[0];
String base64EncodedBody = split_string[1];
String base64EncodedSignature = split_string[2];
Base64 base64Url = new Base64(true);
String header = new String(base64Url.decode(base64EncodedHeader));
JSONObject jsonObjHeader = JSONObject.fromObject(header);
String body = new String(base64Url.decode(base64EncodedBody));
JSONObject jsonObjBody = JSONObject.fromObject(body);
String signatre = new String(base64Url.decode(base64EncodedSignature));

//kid for signature validation
String kid = jsonObjHeader.getString("kid");

//open-id-config endpoint
String openIdConfigEndPoint = "https://login.microsoftonline.com/{tenantID}/.well-known/openid-configuration";
OAuthRequest requestOpenId = constructOAuthRequest(
                    openIdConfigEndPoint, oauthNonce, oauthTimestamp, Verb.GET);
Response responseOpenId = requestOpenId.send();
String openIdString = responseOpenId.getBody();
JSONObject jsonObjopenId = JSONObject.fromObject(openIdString);

String keysEndPoint = jsonObjopenId.getString("jwks_uri");
OAuthRequest requestkeysEndPoint = constructOAuthRequest(keysEndPoint,
                    oauthNonce, oauthTimestamp, Verb.GET);
Response responsekeysEndPoint = requestkeysEndPoint.send();
String keysEndPointString = responsekeysEndPoint.getBody();
JSONObject jsonKeysEndPointString = JSONObject.fromObject(keysEndPointString);

JSONArray keysEndPointArray = jsonKeysEndPointString.getJSONArray("keys");
String x5t = null;
String n = null;
String e = null;        
for (int i = 0; i < keysEndPointArray.size(); i++) {
    JSONObject obj1 = keysEndPointArray.getJSONObject(i);
    if (obj1.getString("kid").equals(kid)) {

        n = obj1.getString("n");
        e = obj1.getString("e");
        x5t = obj1.getString("x5t");                
    }

}

byte[] modulusBytes = Base64.decodeBase64(n.getBytes("UTF-8"));
byte[] exponentBytes = Base64.decodeBase64(e.getBytes("UTF-8"));

BigInteger modulusInt = new BigInteger(1, modulusBytes);
BigInteger exponentInt = new BigInteger(1, exponentBytes);

try {
    Signature signature = Signature.getInstance("SHA256withRSA");
    RSAPublicKeySpec publicSpec = new RSAPublicKeySpec(modulusInt, exponentInt);

    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    PublicKey pubKey = keyFactory.generatePublic(publicSpec);

    signature.initVerify(pubKey);
    signature.update(base64Url.decode(base64EncodedBody));

    boolean isVerified = signature.verify(base64Url .decode(base64EncodedSignature));
    // isVerified is false , should be true on successful validation
} catch (Exception ex) {
    ex.printStackTrace();
}

//trying with JJWT


try {
    KeyFactory factory = KeyFactory.getInstance("RSA");
    PublicKey pub = factory.generatePublic(publicSpec);
    Jwt<Header, String> c = Jwts.parser().setSigningKey(pub).parsePlaintextJwt(idToken);
    System.out.println();
} catch (Exception e1) {
    e1.printStackTrace();
}

This is the exception I get -

io.jsonwebtoken.SignatureException: Unable to verify RSA signature using configured PublicKey. Signature length not correct: got 255 but was expecting 256
    at io.jsonwebtoken.impl.crypto.RsaSignatureValidator.isValid(RsaSignatureValidator.java:50)
    at io.jsonwebtoken.impl.crypto.DefaultJwtSignatureValidator.isValid(DefaultJwtSignatureValidator.java:47)
    at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:339)
    at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:458)
    at io.jsonwebtoken.impl.DefaultJwtParser.parsePlaintextJwt(DefaultJwtParser.java:480)
    at edu.umn.faip.web.spring.controller.OAuthSSOTestController.referenceData(OAuthSSOTestController.java:221)
    at org.springframework.web.servlet.mvc.SimpleFormController.referenceData(SimpleFormController.java:214)
    at org.springframework.web.servlet.mvc.AbstractFormController.showForm(AbstractFormController.java:574)
    at org.springframework.web.servlet.mvc.SimpleFormController.showForm(SimpleFormController.java:198)
    at org.springframework.web.servlet.mvc.SimpleFormController.showForm(SimpleFormController.java:175)
    at org.springframework.web.servlet.mvc.AbstractFormController.showNewForm(AbstractFormController.java:338)
    at org.springframework.web.servlet.mvc.AbstractFormController.handleRequestInternal(AbstractFormController.java:278)
    at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:153)
    at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:48)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:875)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:807)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:501)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.security.SignatureException: Signature length not correct: got 255 but was expecting 256
    at sun.security.rsa.RSASignature.engineVerify(Unknown Source)
    at java.security.Signature$Delegate.engineVerify(Unknown Source)
    at java.security.Signature.verify(Unknown Source)
    at io.jsonwebtoken.impl.crypto.RsaSignatureValidator.doVerify(RsaSignatureValidator.java:63)
    at io.jsonwebtoken.impl.crypto.RsaSignatureValidator.isValid(RsaSignatureValidator.java:47)
    ... 38 more

I've tried using the jjwt library for this, but that doesn't seem to work either. I've tried to get the modulus and the exponent from the public keys posted on the endpoint to construct my own key object in java and then use it to validate the signature.

Any help is much appreciated!

Peter Pan
  • 23,476
  • 4
  • 25
  • 43
  • 1
    Please elaborate on "doesn't seem to work". Preferably with an error message or some other expected vs observed action. – Joe C Nov 16 '16 at 20:33
  • @JoeC Are you referring to the validation using java security, or using jjwt? – Prateek Hiremath Nov 16 '16 at 20:36
  • 1
    Whichever of the two you want to go with. Both if you have no preference. – Joe C Nov 16 '16 at 20:37
  • For the java security code, the isVerified variable is false which should ideally be true because the token is valid. That's the problem. For JJWT, I'll just modify the post to include the exception I'm getting. – Prateek Hiremath Nov 16 '16 at 20:42

1 Answers1

0

Can you try

signature.update((split_string[0]+ "." +  
                  split_string[0]).getBytes(StandardCharsets.UTF_8)); 

OpenID Connect - how to verify id token in Java?

You can refer this link. It worked for me.

Mebin Joe
  • 2,172
  • 4
  • 16
  • 22