【问题标题】:How can I validate an Azure AD JWT Token in Java?如何在 Java 中验证 Azure AD JWT 令牌?
【发布时间】:2020-07-08 02:56:45
【问题描述】:

我有一个使用 Msal 库获得的 Azure AD JWT 令牌,但是当我尝试验证此令牌时出现问题:

客户端:Sharepoint Web 部件

 const config = {
     auth: {
         clientId: "xxxxx",
         authority: "https://login.microsoftonline.com/yyyyyy"
     }
 };

 const myMSALObj = new UserAgentApplication(config);

 let accessTokenRequest = {
     scopes: ["user.read"],
     loginHint: this.context.pageContext.user.loginName,
     extraQueryParameters: {domain_hint: 'organizations'}
 }

 myMSALObj.acquireTokenSilent(accessTokenRequest).then(
  function(accessTokenResponse) { 
  // Acquire token silent success 
  let accessToken = accessTokenResponse.accessToken;

我正在尝试从以下位置检索公钥: https://login.microsoftonline.com/tenant-id/.well-known/openid-configuration 但有了这个公钥,我永远无法重定向到:https://login.microsoftonline.com/common/discovery/keys

在这种情况下,有没有其他方法可以获取公钥?

服务器:验证

public PublicKey getPublicKeyFromParams(String e, String n){

    byte[] modulusBytes = Base64.getUrlDecoder().decode(n);

    BigInteger modulusInt = new BigInteger(1, modulusBytes);

    byte[] exponentBytes = Base64.getUrlDecoder().decode(e);

    BigInteger exponentInt = new BigInteger(1, exponentBytes);

    KeyFactory keyFactory;

    RSAPublicKeySpec publicSpec = new RSAPublicKeySpec(modulusInt, exponentInt);

        try {

            keyFactory = KeyFactory.getInstance("RSA");

            return keyFactory.generatePublic(publicSpec);

        } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) {

            ex.printStackTrace();

        }

    return null;

}

@Test

public void name() throws Exception {

    String jwt = "xxxxxxx";

    KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256);

    String e = Base64.getUrlEncoder().encodeToString((((RSAPublicKeyImpl) keyPair.getPublic()).getPublicExponent()).toByteArray());

    String n = Base64.getUrlEncoder().encodeToString((((RSAPublicKeyImpl) keyPair.getPublic()).getModulus()).toByteArray());

    System.out.println("Public Key: " + Base64.getUrlEncoder().encodeToString(keyPair.getPublic().getEncoded()));

    System.out.println("Public Key Exponent: " + e);

    System.out.println("Public Key Modulus: " + n);

    String jws = Jwts.builder().setSubject("pepe").signWith(keyPair.getPrivate()).compact();

    System.out.println("Generated JWS:" + jws);

    PublicKey publicKey = getPublicKeyFromParams(e, n);

    Jwt parsedJWs = Jwts.parserBuilder().setSigningKey(publicKey).build().parse(jws);

    System.out.println("Parsed JWS: " + parsedJWs);

    publicKey = getPublicKeyFromParams(eValue, nValue);

    System.out.println("Azure PublicKey fron n-e: " + publicKey);

    CertificateFactory factory = CertificateFactory.getInstance("X.509");

    Certificate cert = factory.generateCertificate(new ByteArrayInputStream(

    DatatypeConverter.parseBase64Binary("cccccccccccccccccc")));

    publicKey = cert.getPublicKey();

    System.out.println("Azure PublicKey from x5c: " + publicKey);

    Jwt jwtParsed = Jwts.parserBuilder().setSigningKey(publicKey).build().parse(jwt);

    System.out.println(jwtParsed);

}

public static PublicKey getPublicKey(String key){

    try{

        byte[] byteKey = Base64.getDecoder().decode(key);

        X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(byteKey);

        KeyFactory kf = KeyFactory.getInstance("RSA");

        return kf.generatePublic(X509publicKey);

    }

    catch(Exception e){

        e.printStackTrace();

    }

    return null;

}

【问题讨论】:

  • 能否提供您的代码?
  • 嗨@JimXu。感谢您的回复。我已经添加了我的代码。

标签: azure azure-active-directory


【解决方案1】:

通过此更改,验证工作正常。

let accessTokenRequest = {
    scopes:["clientId/.default"],
    loginHint: this.context.pageContext.user.loginName,
    extraQueryParameters: {domain_hint: 'organizations'}
}

【讨论】:

  • 感谢 vcima,您在无数小时后才救了我。看来msal生成的access_token在用scopes:['user.read']生成时实际上是invalid,用['your client id/.default']有效,可通过jwt.io验证。另见here
【解决方案2】:

您需要使用 microsoft 提供的新密钥才能使上述代码正常工作。变化:

provider = new UrlJwkProvider(new URL("https://login.microsoftonline.com/common/discovery/keys"));

provider = new UrlJwkProvider(new URL("https://login.microsoftonline.com/common/discovery/v2.0/keys"));

对于上面的示例,缺少一些依赖项,正确的示例应该是:

<dependency>
        <groupId>com.microsoft.azure</groupId>
        <artifactId>azure-storage</artifactId>
        <version>8.6.2</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.16.0</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.auth0/jwks-rsa -->
    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>jwks-rsa</artifactId>
        <version>0.18.0</version>
    </dependency>

和代码:

String token="YOUR JWT";
        DecodedJWT jwt = JWT.decode(token);
        System.out.println(jwt.getKeyId());

        JwkProvider provider = null;
        Jwk jwk =null;
        Algorithm algorithm=null;

        try {
            provider = new UrlJwkProvider(new URL("https://login.microsoftonline.com/common/discovery/v2.0/keys"));
            jwk = provider.get(jwt.getKeyId());
            algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);
            algorithm.verify(jwt);// if the token signature is invalid, the method will throw SignatureVerificationException
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (JwkException e) {
            e.printStackTrace();
        }catch(SignatureVerificationException e){

            System.out.println(e.getMessage());

        }

【讨论】:

  • 当我尝试这段代码时,我在下面的行中遇到了 sslhand 抖动异常。 jwk = provider.get(jwt.getKeyId());我们需要在 JKS 中添加任何公共证书吗?
  • 这个验证甚至过期的令牌。有没有办法自定义验证选项以包括到期时间、发行人等?
【解决方案3】:

如果您想验证 Azure AD 访问令牌,我们可以尝试使用 sdk java-jwtjwks-rsa 来实现它。

例如

  1. 通过 maven 安装 SDK
 <dependency>
      <groupId>com.microsoft.azure</groupId>
      <artifactId>azure-storage</artifactId>
      <version>8.6.2</version>
    </dependency>

    <dependency>
      <groupId>com.auth0</groupId>
      <artifactId>jwks-rsa</artifactId>
      <version>0.11.0</version>
    </dependency>
  1. 代码

    一个。验证签名

     String token="<your AD token>";
      DecodedJWT jwt = JWT.decode(token);
      System.out.println(jwt.getKeyId());
    
      JwkProvider provider = null;
      Jwk jwk =null;
      Algorithm algorithm=null;
    
      try {
          provider = new UrlJwkProvider(new URL("https://login.microsoftonline.com/common/discovery/keys"));
          jwk = provider.get(jwt.getKeyId());
          algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);
          algorithm.verify(jwt);// if the token signature is invalid, the method will throw SignatureVerificationException
      } catch (MalformedURLException e) {
          e.printStackTrace();
      } catch (JwkException e) {
          e.printStackTrace();
      }catch(SignatureVerificationException e){
    
         System.out.println(e.getMessage());
    
      }
    

【讨论】:

  • 嗨,吉姆!感谢您的答复。使用您的代码我有同样的问题:我已经获得了令牌: myMSALObj.acquireTokenSilent(accessTokenRequest).then(function(accessTokenResponse) { // 获取令牌静默成功 // 使用令牌调用 API let accessToken = accessTokenResponse.accessToken;
  • 您的示例的验证错误是:使用算法验证时,令牌的签名导致无效:SHA256withRSA 您有什么想法吗?
  • @vcima 是你用sdk msal.js 要求token吗?
  • 嗨@Jim XU,是的,我正在使用Sharepoint WebPart 中的Msal 库进行此导入:从“msal”导入{UserAgentApplication};。这有问题吗?。
  • const config = { auth: { clientId: "xxxxx", authority: "login.microsoftonline.com/yyyyyy" } }; const myMSALObj = new UserAgentApplication(config);让 accessTokenRequest = { 范围:[“user.read”],loginHint:this.context.pageContext.user.loginName,extraQueryParameters:{domain_hint:'organizations'} } myMSALObj.acquireTokenSilent(accessTokenRequest).then(function(accessTokenResponse) { // 获取令牌静默成功 let accessToken = accessTokenResponse.accessToken;
猜你喜欢
  • 2016-12-02
  • 1970-01-01
  • 2021-08-21
  • 2018-05-10
  • 2019-05-13
  • 2020-07-12
  • 2017-03-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多