简体   繁体   English

在 C# 中手动验证 JWT 令牌

[英]Manually validating a JWT token in C#

I am having some trouble manually validating a JWT token issued by Identity Server 4. Using the我在手动验证Identity Server 4 颁发的 JWT 令牌时遇到了一些麻烦。

ClientId: "CLIENT1" ClientSecret: "123456" ClientId:“CLIENT1” ClientSecret:“123456”

The exception I keep getting is: IDX10501: Signature validation failed.我不断收到的例外是:IDX10501:签名验证失败。 Unable to match keys: '[PII is hidden by default.无法匹配键:'[默认情况下隐藏 PII。 Set the 'ShowPII' flag in IdentityModelEventSource.cs to true to reveal it.]'将 IdentityModelEventSource.cs 中的“ShowPII”标志设置为 true 以显示它。]'

Is anyone able to advise me where I am going wrong.有没有人能告诉我哪里出错了。

    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;
        }

    }

Check out ValidateJwt() in this sample:在此示例中查看ValidateJwt()

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

The bit you're missing is loading the public key from the discovery document.您缺少的一点是从发现文档中加载公钥。

Try changing the length of your private key.尝试更改私钥的长度。 Your private key is too small to be encoded I suppose.我想您的私钥太小而无法编码。

For manual verification, you could just use对于手动验证,您可以使用

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);
        }

This also answers @henk-holterman 's question这也回答了@henk-holterman 的问题

Encoding.UTF8.GetBytes( can't be the right way to do this. Encoding.UTF8.GetBytes( 不是正确的方法。

Although realistically a better way to do this is via the OIDC discovery-endpoint Auth0 has a good article about this using standard NuGet packages.虽然实际上更好的方法是通过 OIDC 发现端点 Auth0 有一篇关于使用标准 NuGet 包的好文章。 Basically, you load everything needed from the discovery end-point.基本上,您从发现端点加载所需的一切。

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);

You can read more about it here Or their GitHub sample page about this here你可以在这里阅读更多关于它的信息或者他们的 GitHub 示例页面在这里

In my case, I did not have a discovery endpoint.就我而言,我没有发现端点。 Just a JWKS endpoint.只是一个 JWKS 端点。 So I opted to do this.所以我选择这样做。

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;
}

You have specified:您已指定:

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

but the JwtSecurityTokenHandler could not match it with the key which can be part of jwt header itself .但是JwtSecurityTokenHandler无法将它与可以是 jwt 标头本身的一部分的密钥相匹配。 Basically it means that your configuration has mismatch[es] with configuration of the real issuer.基本上这意味着您的配置与实际发行人的配置不匹配[es]。 The error suggests that this relates to the signature keys.该错误表明这与签名密钥有关。

Please, check the configuration of that issuer (if you can), find out missed parts, and try again.请检查该发行者的配置(如果可以),找出遗漏的部分,然后重试。

You can use jwt.io to debug your jwt online.您可以使用jwt.io在线调试您的 jwt。

IdentityServer signs the JWT using RS256. IdentityServer 使用 RS256 对 JWT 进行签名。 This means you need to use a public key to verify the JWT (you can get this from the discovery document).这意味着您需要使用公钥来验证 JWT(您可以从发现文档中获取)。

The client id & client secret are client credentials used for requesting tokens.客户端 ID 和客户端密码是用于请求令牌的客户端凭据。 They have no part in validating them.他们没有参与验证它们。

You are trying to use SymmetricKey for JWT validation.您正在尝试使用 SymmetricKey 进行 JWT 验证。 Try looking your token in JWT.io and if algorithm is"RS256" then SymmetricKey won't work.尝试在JWT.io 中查看您的令牌,如果算法是“RS256”,则 SymmetricKey 将不起作用。

Please check when you create JWT token make sure that you added SigningCredentials.请在创建 JWT 令牌时检查确保您添加了 SigningCredentials。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM