【问题标题】:Validation of PS256 encoded JWT using C#使用 C# 验证 PS256 编码的 JWT
【发布时间】:2019-07-20 06:45:47
【问题描述】:

我的任务是验证使用 PS256 算法编码的 JWT 令牌,过去两天我一直遇到问题。我缺乏这方面的知识,我一直在尝试不同的解决方案来慢慢解决这个问题。

// Encoded
eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZDEyMzQifQ.eyJpc3MiOiJmb28uYmFyLnRlc3Rpc3N1ZXIiLCJleHAiOjE1NTEyMDEwNjgsImF0X2hhc2giOiJqaFl3c1pyTnZ0dFNYQnR6QVMtWlNnIn0.yJePyxdJWyydG4HM97oQag6ulGKa5Afw-LHYYEXz7lVy8v0IJD0mSO9WtowlWJIeD2Vvthuj71XUfHsgz0LD9rK0VBucJbd_OiIXpbwPUqBcdj82DNLFXDJfCJnUC-Rv8QP7OUVBvLjvBQ6WYMrx1Qnq8xP6qeL_ohKwRmo6EDhZRkYBz9gFhfha1ZlKcnyR73nXdShwy7OmmyiRvVWPBf_GgSsfz8FNNoKySW1MA4tRa7cl3zPlyCnWyLaZ3kcQsmTqarHG--YXSDF5ozZ_Sx6TkunCxrOYzOFNcPyeIWqI84cemM6TgMBw9jhzMCk7Y4Fzxe5KEYJH4GlGA4s4zg

// Header
{
    "alg": "PS256",
    "typ": "JWT",
    "kid": "kid1234"
}

// Payload
{
    "iss": "foo.bar.testissuer",
    "exp": 1551201068,
    "at_hash": "jhYwsZrNvttSXBtzAS-ZSg"
}

我有一个 RS256 编码 JWT 的工作实现,它使用 Microsoft.IdentityModel.TokensSystem.IdentityModel.Tokens.JwtJWTSecurityTokenHandler /强>。对于 RS256 实现,我有一个 IssuerSigningKeyResolver 正在为孩子进行自定义检查并提供公钥

var tokenValidationParameters = new TokenValidationParameters
{
    ValidIssuer = issuer,
    ValidateLifetime = true,
    RequireSignedTokens = true,
    RequireExpirationTime = true,
    ValidateAudience = false,
    ValidateIssuer = true,
    IssuerSigningKeyResolver = (string token, SecurityToken securityToken, string kid, TokenValidationParameters validationParameters) =>
    {
        // Custom kid checks

        var rsa = RSA.Create();
        rsa.ImportParameters(new RSAParameters
        {
            Exponent = Base64UrlEncoder.DecodeBytes(matchingKid.E),
            Modulus = Base64UrlEncoder.DecodeBytes(matchingKid.N),
        });
        latestSecurityKeys.Add(matchingKid.Kid, new RsaSecurityKey(rsa));

        var securityKeys = new SecurityKey[1]
        {
            new RsaSecurityKey(rsa)
        };

        return securityKeys;
    }
};

var tokenHandler = new JwtSecurityTokenHandler();
try
{
    var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out SecurityToken validatedToken);
    return true;
}
catch (SecurityTokenException ex)
{
    // Do something with ex
    return false;
}

此实现不适用于 PS256 编码的 JWT。我在 System.IdentityModel.Tokens.Jwt 中调试了 JwtSecurityTokenHandler,但似乎即使 PS256 在支持的算法列表中,验证也会失败。

我必须再次声明,我对这个主题的了解有限。据我了解,RSA256 和 PS256 属于同一算法家族吗?使用 jose-jwt 等其他库创建 PS256 JWT 的自定义验证会更好吗?

【问题讨论】:

  • 你试过JWT.io吗?先在那里尝试并验证。 jwt.io/…
  • 显然该令牌已过期,因此您需要生成一个新令牌并将您的公钥和私钥粘贴到相关框中。
  • 我提供的令牌只是一个例子。我可以使用公钥验证 jwt.io 上的令牌,所以我知道它的编码正确。我的问题是如何使用 .NET 进行相同的验证。
  • 您使用的是 .NET Framework 4.5+ 吗?你在用欧文吗?您可能会使用IdentityServer3.AccessTokenValidation nuget 包来调用一种方法来设置它:UseIdentityServerBearerTokenAuthentication

标签: c# .net jwt rsa jose


【解决方案1】:

在向 Microsoft 提出问题后,现在似乎 Microsoft.IdentityModel.Tokens 不支持此类验证System.IdentityModel.Tokens.Jwt 。详细信息可以在这里找到 - https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/1117

最后,我使用 jose-jwt 和一些自定义检查验证了我的令牌。

private bool IsValid(string token, string issuer, string configId)
{
    var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
    var jwtSecurityToken = jwtSecurityTokenHandler.ReadToken(token) as JwtSecurityToken;

    // Extract the kid from token header
    var kidHeader = jwtSecurityToken.Header.Where(k => k.Key.ToLower() == "kid")?.FirstOrDefault();
    if (kidHeader?.Value == null) ThrowInvalidOperation($"Failed to find matching kid for Issuer: {issuer.ToLower() }");

    var kid = kidHeader?.Value as string;

    // Extract the expiration time from token payload
    var expirationTime = jwtSecurityToken.Payload?.Exp;
    if (expirationTime == null) ThrowInvalidOperation($"Failed to find matching expiration time for Issuer: {issuer.ToLower() }");

    // Decode to verify signature
    var verifiedToken = JWT.Decode(token, GetPublicKey(kid, issuer, providerId));

    if (verifiedToken != null)
    {
        var json = JsonConvert.DeserializeObject<dynamic>(verifiedToken);
        return IsValidIssuer(json, issuer) && IsValidExpirationTime(json, expirationTime);
    }
    else
    {
        return false;
    }

    void ThrowInvalidOperation(string msg) => throw new InvalidOperationException(msg);
}

private bool IsValidIssuer(dynamic json, string issuer)
{
    if (json != null && issuer != null)
    {
        if (json["iss"] == issuer)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    return false;
}

private bool IsValidExpirationTime(dynamic json, int? expTime)
{
    if (json != null && expTime != null)
    {
        if (json["exp"] == expTime)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    return false;
}

private RSA GetPublicKey(string kid, string validIssuer, string configId)
{
    var openIdConfig = openIdConfigurationProvider.GetOpenIdConfiguration(configId);
    var matchingKid = openIdConfig?.JsonWebKeySet?.Keys?.FirstOrDefault(x => x.Kid == kid);
    if (matchingKid == null)
    {
        throw new InvalidOperationException($"kid is null");
    }

    var rsa = RSA.Create();
    rsa.ImportParameters(new RSAParameters
    {
        Exponent = Base64UrlEncoder.DecodeBytes(matchingKid.E),
        Modulus = Base64UrlEncoder.DecodeBytes(matchingKid.N),
    });

    return rsa;
}

【讨论】:

  • 好的,我现在明白了。但奇怪的是,在 JWT 网站上,有相当多的库支持 PS256 算法。
  • 是的,这是一开始我最困惑的地方。事实证明, jose-jwt 和可能的 BouncyCastle 是唯一支持一切的。尽管如此,还是感谢您的帮助和建议。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-21
  • 2017-06-01
  • 2018-09-16
  • 2013-09-11
  • 2019-02-15
  • 2021-05-10
  • 1970-01-01
相关资源
最近更新 更多