验证 JWT 令牌签名的最简单方法是什么?

What is the easiest way to validate the signature of a JWT token?

首先是一些代码...我有一个安全 class:

public static class Security
{
    public static RSACryptoServiceProvider RSA { get; } = new(4096);
    public static SigningCredentials Credentials()
    {
        return new SigningCredentials(new RsaSecurityKey(RSA), SecurityAlgorithms.RsaSha512)
        {
            CryptoProviderFactory = new CryptoProviderFactory { CacheSignatureProviders = false }
        };
    }

}

这是一个简单的静态 class,可生成 RSA 密钥和相关的签名凭据。它有更多代码,但这对我的问题并不重要...
然后我有使用 System.IdentityModel.Tokens.JwtSystem.Security.Claims:

生成 JWT 令牌的代码
    var claims = new[]
    {
        new Claim(JwtRegisteredClaimNames.Jti, account.Id.ToString()),
        new Claim(JwtRegisteredClaimNames.Sub, account.Name),
        new Claim(JwtRegisteredClaimNames.Name, account.FullName),
        new Claim(JwtRegisteredClaimNames.GivenName, account.FirstName),
        new Claim(JwtRegisteredClaimNames.FamilyName, account.LastName),
        new Claim(JwtRegisteredClaimNames.Birthdate, account.Birthdate.ToString()),
        new Claim(JwtRegisteredClaimNames.Email, account.Email.ToString()),
    };
    var token = new JwtSecurityToken
    (
        issuer: host,
        audience: audience,
        claims: claims,
        notBefore: DateTime.UtcNow,
        expires: DateTime.UtcNow.AddHours(4),
        Security.Credentials()
    );

同样,不是太具有挑战性。我代码中数据的一些声明,这是我自己的 class 和基本用户数据。这变成了已签名的令牌。结果是这个标记:

eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJhYmQ3NjUxMS1jNmNmLTQzZjUtOGE1Zi1iZjEzZTg0NGM1ZmEiLCJzdWIiOiJ3aW0iLCJuYW1lIjoiV2ltIFRlbiBCcmluayIsImdpdmVuX25hbWUiOiJXaW0iLCJmYW1pbHlfbmFtZSI6IlRlbiBCcmluayIsImJpcnRoZGF0ZSI6IjExLTctMTk2NiIsImVtYWlsIjoid2ltQGV4YW1wbGUuY29tIiwibmJmIjoxNjQwNjU1MDA1LCJleHAiOjE2NDA2Njk0MDUsImlzcyI6ImxvY2FsaG9zdDo3MTkwIiwiYXVkIjoiL1Rva2VuL0xvZ2luIn0.iFn6DKvHxW_Vd6bxlLs4quQwCyYWpL7bbyMJSTVMxS4RRQ9GxM9IvgoymEqyn9YNBc68TFULNPRVBFE8_mF-IpYt1NIGtp8p7u9laMRegEfXyvJFislDcPZoMbD5xjB18xwUwTpkNjdX4svzFIR8mQT5bLcb5BojFLhxUVT-oJ8G_f0lD0XWJkDo40OW9aHECjIOOu_KfeYZiPdiuo-q9yed6WN1-dgqz4ykWOYC7FoMA0ZYogG5pRvmVLOq8haiEDEJRszbhjj6CLt6h_hlwOS0mMZF6a7Ag5mN4lI2UtEot-jYyXM72eXgBPm_bWNsa6RG_m9vFf4EElK4ekfFd3V_qkAnZ8XEhfTcRD8ASEVjXg8hTI5SY4LFyrggso44HFN5oEoVWlVVcGZxEG2R6LspJSFD4xkrVUUeQvt7IbBL1ViflSfGYp6-cLMPfxo3qZvItC7DxP_8aAygNmHi0_T8BxGNLnOwqrPtDOjKzw4SqEEwBf5S5R6O_dufhauAVvvS_rKQnkEdq-MNNNW5U90ytauCcAgFEGa03MN3E4hhQKUE5y70wCStrIFcBtJezf32U8V7SHv1WrVMpnQXxQoLneZh3b7QsthKCh49ypUSLjw4yL9VVur2K0oV8NsLnwoqH5dCVFuq7YHmreiTa-ZUIFVrgWYMzMqR8hG7Ato

现在我想使用以下代码将其转换回令牌:

var tokenData = new JwtSecurityToken(tokenString);

这将再次从字符串中生成令牌,但当然不会对其进行验证。我需要验证此令牌的签名。
需要明确的是:这不适用于 ASP.NET Core 应用程序,但我确实将 .NET Core 用于此控制台应用程序。这是一个连接到数据库的简单工具,该数据库包含作为字符串的令牌,它只需要使用特定的 RSA 密钥验证其中的每个令牌。 (也存储在安全位置。)此数据库中的令牌来自 Web 应用程序,该应用程序使用此 table 进行日志记录,但显然某些令牌使用错误或无效密钥签名,我需要一种快速方法来找到哪个此日志中的令牌无效。

那么,验证这些签名的最简单方法是什么?


还有一个更新:我的代码正在使用 Microsoft.AspNetCore.Authentication.JwtBearer and for some reason it caused some unexpected behavior and errors during validations. When I kicked it out and replaced it with System.IdentityModel.Tokens.Jwt 一切都开始工作了。
这两个包在 classes 和其他部分非常相似,我不确定它们之间有何不同,但它们确实不同。一种是使用 OpenID,另一种通常用于 JWT。切换到最后一个解决了我的问题...

如果您不限于语言,您可以使用我们在 Curity 创建的库来验证 JWT:https://www.npmjs.com/package/@curity/jwt-validation. We've also created a list of libraries which you can use to validate JWTs: https://curity.io/resources/guides/libraries/api/ so if you want to stick to .NET you can find a library there. We also have a tutorial which shows how to use that .NET lib: https://curity.io/resources/learn/dotnet-api/

事实证明,它似乎是包 Microsoft.AspNetCore.Authentication.JwtBearer 中的一个错误,但包 System.IdentityModel.Tokens.Jwt 几乎相同,但完成了工作。以下代码有效:

        var param = new TokenValidationParameters
        {
            ClockSkew = TimeSpan.FromMinutes(5),
            ValidateAudience = true,
            ValidateIssuer = true,
            ValidateIssuerSigningKey = true,
            ValidateTokenReplay = false,
            ValidateLifetime = true,
            IssuerSigningKey = new RsaSecurityKey(Security.RSA),
            ValidAudience = Audience,
            ValidIssuer = Issuer,
        };
        var claim = new JwtSecurityTokenHandler().ValidateToken(tokenString, param, out _);
        if (claim == null) throw new InvalidSignature(tokenString);

(但是使用 Bearer 库 ir 失败了。不知道为什么。)