How do I verify a JWT signature for an Azure B2C id token in Java?

狂风中的少年 提交于 2021-02-06 05:00:49

问题


How do I verify a JWT signature for an Azure B2C id token in Java? I have successfully verified signatures with google open-id connect, but I have not been successful verifying signatures for Microsoft Azure B2C jwt id tokens. I used the example B2C playground app here https://aadb2cplayground.azurewebsites.net/ . After signing up and editing my profile, and I captured this id token.

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSIsImtpZCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSJ9.eyJhdWQiOiJiYjJhMmUzYS1jNWU3LTRmMGEtODhlMC04ZTAxZmQzZmMxZjQiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83NzU1MjdmZi05YTM3LTQzMDctOGIzZC1jYzMxMWY1OGQ5MjUvIiwiaWF0IjoxNDU4NDMzMDIzLCJuYmYiOjE0NTg0MzMwMjMsImV4cCI6MTQ1ODQzNjkyMywiYW1yIjpbInB3ZCJdLCJpcGFkZHIiOiI3NC4xMzkuMjEzLjE3NSIsIm5hbWUiOiJib2I1Iiwibm9uY2UiOiJMNlNvekpjeVVEc2lYQ2t0NVQwN1NBPT0iLCJvaWQiOiJjNjc0NDA1Yy05ZGIxLTRmN2EtYTIwMy1jNzZkNDk1Zjk5ZDAiLCJwdWlkIjoiMTAwM0JGRkQ5NkQ0NThCMSIsInN1YiI6InozTGRJOWUtSFlWeXpCSl9sOE9RTndWYnRQM3BnbHBwREo3NjJ2TmEycFkiLCJ0aWQiOiI3NzU1MjdmZi05YTM3LTQzMDctOGIzZC1jYzMxMWY1OGQ5MjUiLCJ1bmlxdWVfbmFtZSI6InJib290aF9jYWxsaWJyaXR5LmNvbSNFWFQjQGZhYnJpa2FtYjJjLm9ubWljcm9zb2Z0LmNvbSIsInVwbiI6InJib290aF9jYWxsaWJyaXR5LmNvbSNFWFQjQGZhYnJpa2FtYjJjLm9ubWljcm9zb2Z0LmNvbSIsInZlciI6IjEuMCJ9.MPPkvUc0bHuVyf8hr4JZ0hG0mLE2pT7maDR-10e3XR8m6FtrsmQlkgvhnzfao94jPzDzX_CnG_Asfnqv04JeIpvQXBlViO63AlfZaZVllLByeJti5Uat1WepMPz5MRydk6b2o5w_xRfl7QOI-L9Yt8r7-rQX1FMuIPfvvsUity-M-H8s0XInvihxiKEHU_wvz6U017Tgjs4qcrpILM5Ziaxfb7oSxgECl3EDWAoITDy5B-rYCH_o-7mhxHQauUYgH5dUV2MrM8iuaMPoRc3r9Xk38SyfgS1-4taK_bi_AIutyOBX4O3cWbrvGDshQbHBW4BmjctTBT-xUPWboydpuA

I pointed my java code to use the following endpoint for token verification.

https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/discovery/v2.0/keys?p=b2c_1_sign_in

At the time this was written, this was the json at that end-point.

{
  "keys": [
    {"kid":"IdTokenSigningKeyContainer","use":"sig","kty":"RSA","e":"AQAB","n":"tLDZVZ2Eq_DFwNp24yeSq_Ha0MYbYOJs_WXIgVxQGabu5cZ9561OUtYWdB6xXXZLaZxFG02P5U2rC_CT1r0lPfC_KHYrviJ5Y_Ekif7iFV_1omLAiRksQziwA1i-hND32N5kxwEGNmZViVjWMBZ43wbIdWss4IMhrJy1WNQ07Fqp1Ee6o7QM1hTBve7bbkJkUAfjtC7mwIWqZdWoYIWBTZRXvhMgs_Aeb_pnDekosqDoWQ5aMklk3NvaaBBESqlRAJZUUf5WDFoJh7yRELOFF4lWJxtArTEiQPWVTX6PCs0klVPU6SRQqrtc4kKLCp1AC5EJqPYRGiEJpSz2nUhmAQ"}
  ]
}

Here is the java code that I used

package com.example

import org.jose4j.jwk.HttpsJwks
import org.jose4j.jwt.JwtClaims
import org.jose4j.jwt.consumer.InvalidJwtException
import org.jose4j.jwt.consumer.JwtConsumer
import org.jose4j.jwt.consumer.JwtConsumerBuilder
import org.jose4j.keys.resolvers.HttpsJwksVerificationKeyResolver

class AzureB2CPOC7 {

public static talk(){

    String jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSIsImtpZCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSJ9.eyJhdWQiOiJiYjJhMmUzYS1jNWU3LTRmMGEtODhlMC04ZTAxZmQzZmMxZjQiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83NzU1MjdmZi05YTM3LTQzMDctOGIzZC1jYzMxMWY1OGQ5MjUvIiwiaWF0IjoxNDU4NDMzMDIzLCJuYmYiOjE0NTg0MzMwMjMsImV4cCI6MTQ1ODQzNjkyMywiYW1yIjpbInB3ZCJdLCJpcGFkZHIiOiI3NC4xMzkuMjEzLjE3NSIsIm5hbWUiOiJib2I1Iiwibm9uY2UiOiJMNlNvekpjeVVEc2lYQ2t0NVQwN1NBPT0iLCJvaWQiOiJjNjc0NDA1Yy05ZGIxLTRmN2EtYTIwMy1jNzZkNDk1Zjk5ZDAiLCJwdWlkIjoiMTAwM0JGRkQ5NkQ0NThCMSIsInN1YiI6InozTGRJOWUtSFlWeXpCSl9sOE9RTndWYnRQM3BnbHBwREo3NjJ2TmEycFkiLCJ0aWQiOiI3NzU1MjdmZi05YTM3LTQzMDctOGIzZC1jYzMxMWY1OGQ5MjUiLCJ1bmlxdWVfbmFtZSI6InJib290aF9jYWxsaWJyaXR5LmNvbSNFWFQjQGZhYnJpa2FtYjJjLm9ubWljcm9zb2Z0LmNvbSIsInVwbiI6InJib290aF9jYWxsaWJyaXR5LmNvbSNFWFQjQGZhYnJpa2FtYjJjLm9ubWljcm9zb2Z0LmNvbSIsInZlciI6IjEuMCJ9.MPPkvUc0bHuVyf8hr4JZ0hG0mLE2pT7maDR-10e3XR8m6FtrsmQlkgvhnzfao94jPzDzX_CnG_Asfnqv04JeIpvQXBlViO63AlfZaZVllLByeJti5Uat1WepMPz5MRydk6b2o5w_xRfl7QOI-L9Yt8r7-rQX1FMuIPfvvsUity-M-H8s0XInvihxiKEHU_wvz6U017Tgjs4qcrpILM5Ziaxfb7oSxgECl3EDWAoITDy5B-rYCH_o-7mhxHQauUYgH5dUV2MrM8iuaMPoRc3r9Xk38SyfgS1-4taK_bi_AIutyOBX4O3cWbrvGDshQbHBW4BmjctTBT-xUPWboydpuA";
    HttpsJwks httpsJkws = new HttpsJwks("https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/discovery/v2.0/keys?p=b2c_1_sign_in");
    HttpsJwksVerificationKeyResolver httpsJwksKeyResolver = new HttpsJwksVerificationKeyResolver(httpsJkws);
    JwtConsumer jwtConsumer = new JwtConsumerBuilder()
            .setRequireExpirationTime() // the JWT must have an expiration time
            .setAllowedClockSkewInSeconds(3600) // allow some leeway in validating time based claims to account for clock skew
            .setRequireSubject() // the JWT must have a subject claim
            .setExpectedIssuer("https://sts.windows.net/775527ff-9a37-4307-8b3d-cc311f58d925/") // whom the JWT needs to have been issued by
            .setExpectedAudience("bb2a2e3a-c5e7-4f0a-88e0-8e01fd3fc1f4") // to whom the JWT is intended for
            .setVerificationKeyResolver(httpsJwksKeyResolver)
            .build();

    try
    {
        //  Validate the JWT and process it to the Claims
        JwtClaims jwtClaims = jwtConsumer.processToClaims(jwt);
        System.out.println("JWT validation succeeded! " + jwtClaims);
    }
    catch (InvalidJwtException e)
    {
        // InvalidJwtException will be thrown, if the JWT failed processing or validation in anyway.
        // Hopefully with meaningful explanations(s) about what went wrong.
        System.out.println("Invalid JWT! " + e);
    }
}
}

At first, I received this error message "Unable to find a suitable verification key for JWS w/ header". So, I created a local web server and copied the json from the microsoft json endpoint, but I replaced "kid":"IdTokenSigningKeyContainer" with "kid":"MnC_VZcATfM5pOYiJHMba9goEKY".

That change fixed the "Unable to find a suitable verification key for JWS w/ header" error message, but I received the following error instead "JWS signature is invalid".

I am searching for a java solution to validate the signature of the jwt id token listed above. Thanks in advance.


回答1:


Try using https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/discovery/v2.0/keys for the HTTPS JWKS location. There's a different key there and it has a kid and x5t that match up to what's in the JWT.




回答2:


That url:

https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/discovery/v2.0/keys?p=b2c_1_sign_in

is just example of generic endpoint provided for every b2c tenant

https://login.microsoftonline.com/{your tenant name}/discovery/v2.0/keys?p={your sign in sign up policy}

, Azure b2c tokens doesn't provide key itself but only key id "kid", however that json object provided at custom endpoint contains all necessary components to encode verification key, you just need "e" and "n" values

public class KeyUtilHandler {

// values "n" and "e" from json object at
// https://login.microsoftonline.com/{your tenant name}/discovery/v2.0/keys?p={your sign in sign up policy}

private String eValue;
private String nValue;

public String stringPublicKey(){

    byte[] modulusBytes = Base64.getUrlDecoder().decode(nValue);
    BigInteger modulusInt = new BigInteger(1, modulusBytes);

    byte[] exponentBytes = Base64.getUrlDecoder().decode(eValue);
    BigInteger exponentInt = new BigInteger(1, exponentBytes);

    KeyFactory keyFactory;

    RSAPublicKeySpec publicSpec = new RSAPublicKeySpec(modulusInt, exponentInt);

    String encodedStringKey = null;

    {
        try {
            keyFactory = KeyFactory.getInstance("RSA");

            RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(publicSpec);

            byte [] encodedKey = publicKey.getEncoded();

            encodedStringKey = Base64.getEncoder().encodeToString(encodedKey);

        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            e.printStackTrace();
        }
    }

    return String.format("-----BEGIN PUBLIC KEY-----%s-----END PUBLIC KEY-----"
            , encodedStringKey);
}


来源:https://stackoverflow.com/questions/36110265/how-do-i-verify-a-jwt-signature-for-an-azure-b2c-id-token-in-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!