无法验证 AAD 访问令牌 - IDX10511:签名验证失败

ano*_*er1 9 azure access-token azure-active-directory

我正在尝试构建一种验证我的令牌的方法。我正在使用 Open Id Connect 授权代码流从 Azure Active Directory 检索我的令牌。我得到的令牌是access_tokenid_token。我正在使用 .NET Core。

我的验证代码如下:

string stsDiscoveryEndpoint = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration";
var handler = new JwtSecurityTokenHandler();
ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
OpenIdConnectConfiguration config = configManager.GetConfigurationAsync().Result;

try
{
  TokenValidationParameters validationParameters = new TokenValidationParameters
  {
     ValidIssuers = new [] { "https://login.microsoftonline.com/tenantid/v2.0" },
     ValidAudiences = new [] { "client-Id" },
     ValidateAudience = true,
     ValidateIssuer = true,
     IssuerSigningKeys = config.SigningKeys,
     ValidateLifetime = true
  };
  var tokenHandler = new JwtSecurityTokenHandler();
  SecurityToken validatedToken = null;
  tokenHandler.ValidateToken(token.AccessToken, validationParameters, out validatedToken);
  return validatedToken != null;
 }
 catch (SecurityTokenInvalidSignatureException ex)
 {
   return false;
 }
 catch(SecurityTokenValidationException)
 {
   return false;
 }
Run Code Online (Sandbox Code Playgroud)

下面的代码适用id_token不适用于access_token

为 access_token 执行此方法时收到的错误消息是:

IDX10511:签名验证失败。尝试过的密钥:“Microsoft.IdentityModel.Tokens.X509SecurityKey,KeyId:CtAAALb-8NsDe333734859crfOc”。孩子:“CtAAALb-8NsDe333734859crfOc”。捕获异常:''

小智 7

在签名验证之前,nonce 标头必须经过 SHA2 哈希处理

这是一个代码示例,您可以在其中看到

jsonToken.Header.Add("nonce", hashedNonce);
Run Code Online (Sandbox Code Playgroud)
        private static bool _hashNonceBeforeValidateToken = true;
        private const string MicrosoftGraphApplicationId = "00000003-0000-0000-c000-000000000000";
        private const string MicrosoftIssuer = "https://sts.windows.net";


    public static bool ValidateTokenSignature(string accessToken, ApplicationConfiguration applicationConfiguration) {
        var tokenHandler = new JwtSecurityTokenHandler();
        var jsonToken = tokenHandler.ReadJwtToken(accessToken);
        string[] parts = accessToken.Split('.');
        string header = parts[0];
        string payload = parts[1];
        string signature = parts[2];

        //hash nonce and update header with the hash before validating
        if (_hashNonceBeforeValidateToken &&
            jsonToken.Header.TryGetValue("nonce", out object nonceAsObject))
        {
            string plainNonce = nonceAsObject.ToString();
            using (SHA256 sha256 = SHA256.Create())
            {
                byte[] hashedNonceAsBytes = sha256.ComputeHash(
                    System.Text.Encoding.UTF8.GetBytes(plainNonce));
                string hashedNonce = Base64Url.Encode(hashedNonceAsBytes);
                jsonToken.Header.Remove("nonce");
                jsonToken.Header.Add("nonce", hashedNonce);
                header = tokenHandler.WriteToken(jsonToken).Split('.')[0];

                accessToken = $"{header}.{payload}.{signature}";
            }
        }

        //get the Microsoft JWT signature public key 
        string stsDiscoveryEndpoint = $"https://login.microsoftonline.com/{applicationConfiguration.TenantId}/v2.0/.well-known/openid-configuration";
        if (jsonToken.Header.TryGetValue("ver", out object version) && version.ToString() == "1.0")
        {
            stsDiscoveryEndpoint = $"https://login.microsoftonline.com/{applicationConfiguration.TenantId}/.well-known/openid-configuration";
        }
        var openidConfigManaged = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint,
            new OpenIdConnectConfigurationRetriever(),
            new HttpDocumentRetriever());
        var configTask = openidConfigManaged.GetConfigurationAsync();
        configTask.Wait();
        var config = configTask.Result;

        var parameteres = new TokenValidationParameters()
        {
            RequireAudience = true,
            ValidateAudience = true,
            ValidAudiences = new[] { applicationConfiguration.ApplicationId, MicrosoftGraphApplicationId },

            ValidateIssuer = true,
            ValidIssuers = new string[] { $"{MicrosoftIssuer}/{applicationConfiguration.TenantId}/", config.Issuer },

            IssuerSigningKeys = config.SigningKeys,
            ValidateIssuerSigningKey = true,

            RequireExpirationTime = true,
            ValidateLifetime = true,
        };

        var claimPrincipal = tokenHandler.ValidateToken(
            accessToken, parameteres, out SecurityToken validatedToken);

        return claimPrincipal.Identity.IsAuthenticated;
    }

Run Code Online (Sandbox Code Playgroud)

  • 您能详细说明一下您是如何/在哪里想到这一点的吗?这似乎是我负责维护的大量代码,所以我很好奇它来自哪些文档或库源代码......或者一些权威机构(例如 Microsoft)解释了为什么我需要它。 (2认同)

evi*_*obu 1

受众是access_token您的 API 或 Microsoft Graph/其他第三方服务吗?只有验证您(您的服务)使用的令牌才有意义,其他受众将自行处理此问题。最重要的是,该 JWT 的签名对您来说可能是不透明的。

有关更多信息,请参阅此 - https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/812#issuecomment-456700813