locked
B2C JWT Token Signature Validation RRS feed

  • Question

  • Hi security experts,

    I am trying to validate the JWT token returned from ADAL (Experimental version) .acquireToken() after a successful login against a B2C tenant. I have obtained the runtime JWK token from ../discovery/v2.0/keys?p={myloginpolicy}.  I am then trying to extract the public key and validate the signature of the JWT token received earlier via:

    WebClientwc = newWebClient();

    varsigningTokenJwt = wc.DownloadString("https://login.microsoftonline.com/{myrealm}.onmicrosoft.com/discovery/v2.0/keys?p={myloginpolicy}");

    dynamicdynObj = JsonConvert.DeserializeObject(signingTokenJwt);

    n = dynObj.keys[0].n;

    exp = dynObj.keys[0].e;

    RSAParametersRSAKeyInfo = newRSAParameters();

    RSAKeyInfo.Modulus = Encoding.UTF8.GetBytes(n);

    RSAKeyInfo.Exponent = Encoding.UTF8.GetBytes(exp);

    RSACryptoServiceProvidercsp = newRSACryptoServiceProvider();

    csp.ImportParameters(RSAKeyInfo);

    RSAPKCS1SignatureDeformatterRSADeformatter = newRSAPKCS1SignatureDeformatter(csp);

    RSADeformatter.SetHashAlgorithm("SHA256");

    // Bit hacky this bit for the time being to get the parts of the jwt token received that would have been signed.

    byte[] data = Encoding.UTF8.GetBytes(jwtToken.Split('.')[0]+"."+ jwtToken.Split('.')[1]);

    byte[] signature = Encoding.UTF8.GetBytes(jwtToken.Split('.')[2]);

    byte[] hash = sha1.ComputeHash(data);

    System.Diagnostics.Trace.WriteLine(csp.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA256"), signature));

    However, this is currently throwing an exception when I try to import the parameters into the CyptoProviderService.

    You may ask why I am doing this. I have a set of SOAP web services that will be validating the bearer tokens (so cant use OWIN :-( but still have the need to confirm the token has come from a valid source and has not been tampered with).

    Two question then:

    1. Am I on the right track in terms of validating the signature?

    2. If the above strategy is correct, what am I missing in terms of being able to successfully execute the validation?

    Thanks in advance


    edit: Fixed the exception, e needed to base64 decoded doh!
    • Edited by NZSnark Friday, November 13, 2015 2:53 AM
    Friday, November 13, 2015 1:51 AM

Answers

  • Basically, I do this

          JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
          _jwtPayload = JsonConvert.DeserializeObject<dictionary<string, object="">>(Jose.JWT.Payload(jwt));
    ...
         TokenValidationParameters validationParams = new TokenValidationParameters() { ValidAudiences = [my validAudiences] };
    
            OpenIdConnectCachingSecurityTokenProvider issuerCredentialProvider = new OpenIdConnectCachingSecurityTokenProvider(_jwtPayload["iss"] + ".well-known/openid-configuration?p=" + _jwtPayload["acr"]);
            await issuerCredentialProvider.RetrieveMetadata();
            IEnumerable<SecurityToken> securityTokens = issuerCredentialProvider.SecurityTokens;
            validationParams.IssuerSigningTokens = securityTokens;
            validationParams.ValidIssuer = issuerCredentialProvider.Issuer;
    ...
    
    _claimsPrincipal = tokenHandler.ValidateToken(jwt, validationParams, out _validatedSecurityToken);</dictionary<string,>

    The important part is OpenIdConnectCachingSecurityTokenProvider.


    Tom Schulte | Plex Systems


    • Edited by TSCH Thursday, November 19, 2015 2:33 PM
    • Proposed as answer by TSCH Friday, November 20, 2015 1:59 PM
    • Marked as answer by NZSnark Saturday, November 21, 2015 1:03 AM
    Thursday, November 19, 2015 2:22 PM

All replies

  • Hello,

    We are researching on the query and would get back to you soon on this.

    I apologize for the inconvenience and appreciate your time and patience in this matter.

    Regards,
    Neelesh
    Friday, November 13, 2015 3:04 PM
  • This might work for you. It is the approach I am taking to validating B2C JWTs and it is working well form me: Azure AD B2C Preview: Token Reference

    Tom Schulte | Plex Systems


    • Edited by TSCH Tuesday, November 17, 2015 4:54 PM
    Tuesday, November 17, 2015 4:53 PM
  • Thanks Tom, that was one of the many articles I have reviewed with this challenge. Getting the token is not the problem and applying it to the SOAP service is all good. I am also happy getting the attached claims. The bit that I have not been able to crack is using the published public key to validate the third part of the JWT (ie. the signature) to ensure non tampering of the bearer token and mitigating a man-in-the-middle attack. Cheers anyhow.
    Wednesday, November 18, 2015 6:46 AM
  • You are welcome. For me, I can use the claims to get to the metadata address and thus populate SigningTokens and other properties of a TokenValidationParameters instance and then validate the token and its signature with JwtSecurityTokenHandler.

    Tom Schulte | Plex Systems

    Wednesday, November 18, 2015 2:47 PM
  • I would love to see how you did that, I wasn't having much luck with the JWTTokenHandler hence the approach I was adopting above. Could you drop a sample (I know how to pluck the acr out of the claims and get the public key), so its just the extraction and application of the key against the signature that I have not been able to nut out, so anything you can show from a code perspective there would be much appreciated.


    • Edited by NZSnark Thursday, November 19, 2015 4:47 AM
    Thursday, November 19, 2015 4:47 AM
  • Basically, I do this

          JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
          _jwtPayload = JsonConvert.DeserializeObject<dictionary<string, object="">>(Jose.JWT.Payload(jwt));
    ...
         TokenValidationParameters validationParams = new TokenValidationParameters() { ValidAudiences = [my validAudiences] };
    
            OpenIdConnectCachingSecurityTokenProvider issuerCredentialProvider = new OpenIdConnectCachingSecurityTokenProvider(_jwtPayload["iss"] + ".well-known/openid-configuration?p=" + _jwtPayload["acr"]);
            await issuerCredentialProvider.RetrieveMetadata();
            IEnumerable<SecurityToken> securityTokens = issuerCredentialProvider.SecurityTokens;
            validationParams.IssuerSigningTokens = securityTokens;
            validationParams.ValidIssuer = issuerCredentialProvider.Issuer;
    ...
    
    _claimsPrincipal = tokenHandler.ValidateToken(jwt, validationParams, out _validatedSecurityToken);</dictionary<string,>

    The important part is OpenIdConnectCachingSecurityTokenProvider.


    Tom Schulte | Plex Systems


    • Edited by TSCH Thursday, November 19, 2015 2:33 PM
    • Proposed as answer by TSCH Friday, November 20, 2015 1:59 PM
    • Marked as answer by NZSnark Saturday, November 21, 2015 1:03 AM
    Thursday, November 19, 2015 2:22 PM
  • Aha! that's the secret sauce I was missing. I was trying to do it manually (and rather unsuccessfully). Thanks a million, your a champion.

    Regards

    Friday, November 20, 2015 7:33 AM
  • You are very welcome! If you can, mark my response as the answer. I don't recall every actually answering something for some one! This would be my first!

    Tom Schulte | Plex Systems

    Friday, November 20, 2015 2:00 PM