【问题标题】:Manually validating a JWT token in C#在 C# 中手动验证 JWT 令牌
【发布时间】:2018-07-27 03:09:54
【问题描述】:

我在手动验证 Identity Server 4 颁发的 JWT 令牌时遇到了一些问题。使用

ClientId:“CLIENT1” ClientSecret:“123456”

我不断收到的异常是:IDX10501:签名验证失败。无法匹配键:'[PII 默认隐藏。将 IdentityModelEventSource.cs 中的“ShowPII”标志设置为 true 以显示它。]'

有谁能告诉我哪里出错了。

    private static void ValidateJwt(string jwt, DiscoveryResponse disco)
    {        

        var parameters = new TokenValidationParameters
        {

            ValidateIssuerSigningKey = true,
            ValidIssuer = disco.Issuer,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("123456")),               
            ValidAudience = "CLIENT1",
            //IssuerSigningKeys = keys,
            // ValidateAudience = true,
            // ValidateLifetime = true,
        };

        SecurityToken validatedToken;
        var handler = new JwtSecurityTokenHandler();
        handler.InboundClaimTypeMap.Clear();

        try
        {
            var user = handler.ValidateToken(jwt, parameters, out validatedToken);
        }
        catch(Exception ex)
        {
            var error = ex.Message;
        }

    }

【问题讨论】:

  • Encoding.UTF8.GetBytes( 不可能是这样做的正确方法。

标签: c# jwt identityserver4


【解决方案1】:

您已指定:

IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("secret"))

JwtSecurityTokenHandler 无法与can be part of jwt header itself 的密钥匹配。基本上这意味着您的配置与实际发行者的配置不匹配。该错误表明这与签名密钥有关。

请检查该发行者的配置(如果可以的话),找出遗漏的部分,然后重试。

您可以使用jwt.io在线调试您的jwt。

【讨论】:

    【解决方案2】:

    IdentityServer 使用 RS256 对 JWT 进行签名。这意味着您需要使用公钥来验证 JWT(您可以从发现文档中获取)。

    客户端 ID 和客户端密码是用于请求令牌的客户端凭据。他们不参与验证。

    【讨论】:

    • 什么是发现文件?
    【解决方案3】:

    查看此示例中的ValidateJwt()

    https://github.com/IdentityServer/IdentityServer4/blob/master/samples/Clients/old/MvcManual/Controllers/HomeController.cs

    您缺少的一点是从发现文档中加载公钥。

    【讨论】:

    • 我发现我间歇性地收到此错误,有什么原因吗? Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException:'IDX10501:签名验证失败。无法匹配键:'[PII 默认隐藏。将 IdentityModelEventSource.cs 中的 'ShowPII' 标志设置为 true 以显示它。]', token: '[PII 默认隐藏。将 IdentityModelEventSource.cs 中的 'ShowPII' 标志设置为 true 以显示它。]'.'
    • 原来是负载均衡器。
    • "404: 这不是您要查找的页面。" GitHub Obi-Wan Kenobe 说。
    • @PhillipScottGivens 链接已更新,他们一定已经删除了分支。
    • @MarzSocks 我遇到了类似的问题,我正在尝试在负载均衡器后面的服务中验证 JWT。您能否分享有关负载均衡器问题的更多详细信息?
    【解决方案4】:

    您正在尝试使用 SymmetricKey 进行 JWT 验证。尝试在JWT.io 中查找您的令牌,如果算法是“RS256”,则 SymmetricKey 将不起作用。

    【讨论】:

      【解决方案5】:

      尝试更改您的私钥的长度。我想您的私钥太小而无法编码。

      【讨论】:

        【解决方案6】:

        对于手动验证,您可以使用

        static byte[] FromBase64Url(string base64Url)
                {
                    string padded = base64Url.Length % 4 == 0
                        ? base64Url : base64Url + "====".Substring(base64Url.Length % 4);
                    string base64 = padded.Replace("_", "/")
                                          .Replace("-", "+");
                    return Convert.FromBase64String(base64);
                }
        

        这也回答了@henk-holterman 的问题

        Encoding.UTF8.GetBytes( 不可能是正确的方法。

        虽然实际上更好的方法是通过 OIDC 发现端点 Auth0 使用标准 NuGet 包对此有一篇很好的文章。基本上,您从发现端点加载所需的一切。

        IConfigurationManager<OpenIdConnectConfiguration> configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>($"{auth0Domain}.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());
        OpenIdConnectConfiguration openIdConfig = await configurationManager.GetConfigurationAsync(CancellationToken.None);
        TokenValidationParameters validationParameters =
            new TokenValidationParameters
            {
                ValidIssuer = auth0Domain,
                ValidAudiences = new[] { auth0Audience },
                IssuerSigningKeys = openIdConfig.SigningKeys
            };
        SecurityToken validatedToken;
        JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
        var user = handler.ValidateToken("eyJhbGciOi.....", validationParameters, out validatedToken);
        

        您可以阅读更多关于它的信息here 或者他们的 GitHub 示例页面关于这个here

        就我而言,我没有发现端点。只是一个 JWKS 端点。 所以我选择这样做。

        using Microsoft.IdentityModel.Tokens;
        using Newtonsoft.Json;
        using RestSharp;
        using System;
        using System.Collections.Generic;
        using System.IdentityModel.Tokens.Jwt;
        using System.Linq;
        using System.Threading.Tasks;
        
        public class ExpectedJwksResponse
            {
                [JsonProperty(PropertyName = "keys")]
                public List<JsonWebKey> Keys { get; set; }
            }
        
        private static async Task<List<SecurityKey>> GetSecurityKeysAsync()
                {
                    // Feel free to use HttpClient or whatever you want to call the endpoint.
                    var client = new RestClient("<https://sample-jwks-endpoint.url>");
                    var request = new RestRequest(Method.GET);
                    var result = await client.ExecuteTaskAsync<ExpectedJwksResponse>(request);
                    if (result.StatusCode != System.Net.HttpStatusCode.OK)
                    {
                        throw new Exception("Wasnt 200 status code");
                    }
        
                    if (result.Data == null || result.Data.Keys == null || result.Data.Keys.Count == 0 )
                    {
                        throw new Exception("Couldnt parse any keys");
                    }
        
                    var keys = new List<SecurityKey>();
                    foreach ( var key in result.Data.Keys )
                    {
                        keys.Add(key);
                    }
                    return keys;
                }
        
        private async Task<bool> ValidateToken(token){
            TokenValidationParameters validationParameters = new TokenValidationParameters
                    {
                        RequireExpirationTime = true,
                        RequireSignedTokens = true,
                        ValidateLifetime = true,
                        ValidIssuer = "https://sample-issuer.com",
                        ValidAudiences = new[] { "https://sample-audience/resource" },
                        IssuerSigningKeys = await GetSecurityKeysAsync()
                    };
            var user = null as System.Security.Claims.ClaimsPrincipal;
            SecurityToken validatedToken;
            try
            {
                user = handler.ValidateToken(token, validationParameters, out validatedToken);
            }
            catch ( Exception e )
            {
                Console.Write($"ErrorMessage: {e.Message}");
                return false;
            }
            var readToken = handler.ReadJwtToken(token);
            var claims = readToken.Claims;
            return true;
        }
        

        【讨论】:

          【解决方案7】:

          请在创建 JWT 令牌时检查确保您添加了 SigningCredentials。

          var token = new JwtSecurityToken(
            Constants.Audiance,Constants.Issuer,claims
             notBefore:DateTime.Now,
             expires:DateTime.Now.AddHours(1),
             **signinCredential**
             );
          

          【讨论】:

            猜你喜欢
            • 2018-06-12
            • 2019-01-26
            • 2019-06-23
            • 1970-01-01
            • 2019-01-30
            • 1970-01-01
            • 1970-01-01
            • 2022-11-21
            • 2017-07-27
            相关资源
            最近更新 更多