Azure 公钥解释?

Posted

技术标签:

【中文标题】Azure 公钥解释?【英文标题】:Azure Public Keys explained? 【发布时间】:2017-04-08 07:38:44 【问题描述】:

我正在更新我的应用程序以使用 Azure Active Directory 作为 OAuth 2.0 身份验证服务器。目前我已成功使用授权码授权类型并接收 access_token 和 id_token 值。

现在,我正在尝试验证返回的 id_token。我按照doco 中概述的步骤进行操作,并且能够找到用于签署 JWT 的公钥。例如,这是 Azure REST 端点返回的记录

据我了解,这些是可用的公钥。我可以将其过滤为一个(使用返回的 JWT 标头中的孩子值)。但我很难理解的是每个字段的含义。

我是否使用 n/e 字段为 SHA256 公钥创建模数和指数值?我是否打算改用 x5c 值?抱歉,如果这是一个明显的问题,但是是否有任何文档说明,给定上述值,如何为其创建公钥?我是用 Java 做的,所以任何具体的东西都会非常感激。

【问题讨论】:

上面的记录好像是 JSON Web Key 格式。如果是这种情况,希望有一些库可以创建适当的 Key 对象。 考虑到包含返回上述数据结构的 REST 端点的属性称为“jwks_uri”,这似乎表明数据元素是 JSON Web Key (JWK) 实例! 这看起来像一个可以创建密钥的相关库:connect2id.com/products/nimbus-jose-jwt 支持,因为我搜索了几个小时,但未能找到这些字段的列表或解释。我正在检索令牌并插入 jwt.io 显示密钥 ID,但是当我将相应的 x5c 或 n 值放入签名字段时,它仍然具有无效签名。我应该使用什么值来使其工作?看到有人也在使用关键页眉和页脚? @RyanD 在 x5c 值之前连接“-----BEGIN CERTIFICATE-----”,在 x5c 值之后连接“-----END CERTIFICATE-----”。跨度> 【参考方案1】:

要验证 id_token 的签名,如果您使用 C# 开发,我们可以使用 JwtSecurityTokenHandler 类。您可以参考JsonWebTokenValidator.cs 的代码示例来使用此类。为了方便,我也复制了这个类:

 public class JsonWebTokenValidator

    public JwtSecurityToken Validate(string token)
    
        string stsDiscoveryEndpoint = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration";

        ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint);

        OpenIdConnectConfiguration config = configManager.GetConfigurationAsync().Result;

        TokenValidationParameters validationParameters = new TokenValidationParameters
        
            ValidateAudience = false,
            ValidateIssuer = false,
            IssuerSigningTokens = config.SigningTokens,
            ValidateLifetime = false
        ;

        JwtSecurityTokenHandler tokendHandler = new JwtSecurityTokenHandler();

        SecurityToken jwt;

        var result = tokendHandler.ValidateToken(token, validationParameters, out jwt);

        return jwt as JwtSecurityToken;
    

【讨论】:

【参考方案2】:

这是我发布的类似question 并获得了帮助,这可能会有所帮助。我认为您想使用完整的证书链 x5c,并从中获取公钥来验证 JWT 的签名。模数和指数(n 和 e)将仅生成证书链中第一个证书的公钥,但这不足以验证签名。

这是我用来从端点提取公钥的一些代码 sn-ps。 AzurePublicKey 类只是 json 的 POJO。

private PublicKey convertKey(AzurePublicKey azKey) 
    PublicKey publicKey = null;

    BigInteger modulus = new BigInteger(1, Base64.getUrlDecoder().decode(azKey.getN()));
    BigInteger exponent = new BigInteger(1, Base64.getUrlDecoder().decode(azKey.getE()));

    try 
        publicKey = KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(modulus, exponent));

        // load cert
        CertificateFactory factory;
        X509Certificate cert = null; 
        try 
            factory = CertificateFactory.getInstance("X.509");
            cert = (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(DatatypeConverter.parseBase64Binary(azKey.getX5c().iterator().next())));
         catch (CertificateException e) 
            e.printStackTrace();
        

        // grab public key
        publicKey = (RSAPublicKey)cert.getPublicKey();

        System.out.println("[");
        System.out.println("kid : " + azKey.getKeyIdentifier());
        System.out.println("e : " + azKey.getE());
        System.out.println("n : " + azKey.getN());

        System.out.println("Maybe this : " + DatatypeConverter.printBase64Binary(publicKey.getEncoded()) );
        System.out.println("]");

     catch (InvalidKeySpecException | NoSuchAlgorithmException e) 
        // TODO Auto-generated catch block
        e.printStackTrace();
    

    return publicKey;

以及使用该公钥使用 JJWT 库验证签名的代码。

Jws<Claims> claims = Jwts.parser()
                                .setSigningKeyResolver(signingKeyResolver)
                                .parseClaimsJws(token.replace(TOKEN_PREFIX, ""));


        String issuer   = claims.getBody().getIssuer();
        if(issuer == null || !VALID_ISSUERS.contains(issuer))
            throw new IncorrectClaimException(claims.getHeader(), claims.getBody(), "Invalid Issuer in Token.", new Throwable("Invalid Issuer in Token."));

SigningKeyResolverImpl 仅根据令牌标头的kid 提供正确的公钥。如果解析没有引发异常,您可以根据您期望的值验证令牌声明,因为我已经根据我期望的 VALID_ISSUERS 列表验证了颁发者。

已经有一段时间了,所以我很模糊,但我希望这会有所帮助。

【讨论】:

我刚刚检查了模数和指数产生的密钥与 X509 证书提取的密钥相同。但是,我无法验证 jwt.io 网站上 azure ad 生成的任何令牌的单个签名。您使用的是 2.0 版还是旧版? 原谅我,不确定什么版本?对于 jjwt 库,看起来 0.9.0 是最新的,但我使用的是 0.7.0。 mvnrepository.com/artifact/io.jsonwebtoken/jjwt 抱歉,我的意思是您用来获取密钥的 Microsoft api 版本。我的处理方式与您类似,但我提取的密钥都没有验证我的 jwt 令牌的签名。 啊,是的。 v2.0 来自这个 URL:login.microsoftonline.com/common/discovery/v2.0/keys

以上是关于Azure 公钥解释?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 microsoft azure vault 中配置 RSA HSM 私钥和公钥?

通过 R 使用 Linux Azure DSVM 进行 SSH 公钥身份验证

如何在 azure-pipelines 中使用公钥对某些内容进行 scp?

如何通过 SSH 在 azure devops 管道复制文件中使用公钥

如何将私钥证书 (.pfx)、公钥证书 (.cer) 上传到 Azure WebApp

Azure 点到站点 *** 中是不是需要私钥?