使用 SHA256withRSA 算法验证 WSO2 API 网关 JWT 签名的 C# .NET 代码

标签 c# rsa jwt sha256 wso2-api-manager

有人可以提供示例 C# .NET 代码来验证 WSO2 API 网关发布的 JWT,该网关使用 SHA256withRSA 算法签名。我很确定我需要设置 TokenValidationParameters.IssuerSigningToken,然后调用 JwtSecurityTokenHandler.ValidateToken 方法,但我无法让它工作,也无法找到任何示例代码。

这是我目前所拥有的:

 // Use JwtSecurityTokenHandler to validate the JWT token
 var tokenHandler = new JwtSecurityTokenHandler();
 var convertedSecret = EncodeSigningToken(ConfigurationManager.AppSettings["ClientSecret"]);

 // Read the JWT
 var parsedJwt = tokenHandler.ReadToken(token);


 // Set the expected properties of the JWT token in the TokenValidationParameters
 var validationParameters = new TokenValidationParameters()
 {
     NameClaimType = "http://wso2.org/claims/enduser",
     AuthenticationType = "http://wso2.org/claims/usertype",
     ValidAudience = ConfigurationManager.AppSettings["AllowedAudience"],
     ValidIssuer = ConfigurationManager.AppSettings["Issuer"],
     IssuerSigningToken = new BinarySecretSecurityToken(convertedSecret)
 };


 var claimsPrincipal = tokenHandler.ValidateToken(token, validationParameters, out parsedJwt);

最佳答案

来自 WSO2 API 网关的 JWT 不符合规范(https://www.rfc-editor.org/rfc/rfc7519)。

我见过的所有样本都是这样的形式:

<Base64lEncodedHeader>.<Base64EncodedPayload>.<OPTIONAL, Base64EncodedSignature>

但应该是:

<Base64UrlEncodedHeader>.<Base64UrlEncodedPayload>.<OPTIONAL, Base64UrlEncodedSignature>

问题是使用Base64而不是Base64Url编码。由于签名基于 <Base64EncodedHeader>.<Base64EncodedPayload> ,并且 MS JWT 框架正在根据预期的 <Base64UrlEncodedHeader>.<Base64UrlEncodedPayload> 验证签名,它将始终无法通过验证。我不得不编写自己的自定义签名验证代码来解决这个问题。然后,在使用 JwtSecurityTokenHandler 进行解析和解码之前,我从 token 中剥离签名。

这是最终代码:

try
{
    // Get data and signature from unaltered token
    var data = Encoding.UTF8.GetBytes(token.Split('.')[0] + '.' + token.Split('.')[1]);
    var signature = Convert.FromBase64String(token.Split('.')[2]);

    // Get certificate from file
    var x509 = new X509Certificate2(HttpContext.Current.Server.MapPath("~/App_Data/" + ConfigurationManager.AppSettings["CertFileName"]));

    // Verify the data with the signature
    var csp = (RSACryptoServiceProvider)x509.PublicKey.Key;
    if (!csp.VerifyData(data, "SHA256", signature))
    {
        // Signature verification failed; data was possibly altered
        throw new SecurityTokenValidationException("Data signature verification failed. Token cannot be trusted!");
    }

    // strip off signature from token
    token = token.Substring(0, token.LastIndexOf('.') + 1);

    // Convert Base64 encoded token to Base64Url encoding
    token = token.Replace('+', '-').Replace('/', '_').Replace("=", "");

    // Use JwtSecurityTokenHandler to validate the JWT token
    var tokenHandler = new JwtSecurityTokenHandler();

    // Read the JWT
    var parsedJwt = tokenHandler.ReadToken(token);

    // Set the expected properties of the JWT token in the TokenValidationParameters
    var validationParameters = new TokenValidationParameters()
    {
        NameClaimType = "http://wso2.org/claims/enduser",
        AuthenticationType = ((JwtSecurityToken)parsedJwt).Claims.Where(c => c.Type == "http://wso2.org/claims/usertype").First().Value,
        ValidateAudience = false,
        ValidateLifetime = true,
        ValidateIssuer = true,
        ValidateIssuerSigningKey = false,
        RequireExpirationTime = true,
        RequireSignedTokens = false,
        //ValidAudience = ConfigurationManager.AppSettings["AllowedAudience"],
        ValidIssuer = ConfigurationManager.AppSettings["Issuer"],
        //IssuerSigningToken = new X509SecurityToken(cert),
        CertificateValidator = X509CertificateValidator.None
    };

    // Set both HTTP Context and Thread principals, so they will be in sync
    HttpContext.Current.User = tokenHandler.ValidateToken(token, validationParameters, out parsedJwt);
    Thread.CurrentPrincipal = HttpContext.Current.User;

    // Treat as ClaimsPrincipal, extract JWT expiration and inject it into request headers
    var cp = (ClaimsPrincipal)Thread.CurrentPrincipal;
    context.Request.Headers.Add("JWT-Expiration", cp.FindFirst("exp").Value);
}
catch (SecurityTokenValidationException stvErr)
{
    // Log error
    if (context.Trace.IsEnabled)
        context.Trace.Write("JwtAuthorization", "Error validating token.", stvErr);
}
catch (System.Exception ex)
{
    // Log error
    if (context.Trace.IsEnabled)
        context.Trace.Write("JwtAuthorization", "Error parsing token.", ex);
}

关于使用 SHA256withRSA 算法验证 WSO2 API 网关 JWT 签名的 C# .NET 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33222980/

相关文章:

c# - C# WebApi 中的实时 FLV 流式传输

phpseclib/jsbn : encrypt with public key in PHP, 在jsbn中用私钥解密

cryptography - RSA 签名是唯一的吗?

reactjs - 重新发送使用过期 token 发出的请求会导致开发人员工具处于待定状态

http - 我可以在 Authorization header 中同时使用 Basic 和 Bearer 吗?

php - Google API OAuth 2.0 身份验证返回 "invalid_grant"- 可能是私钥签名格式问题?

c# - 在带有代码隐藏的 ASP.NET 网站中,.cs 文件在什么时候编译?

c# - 有关远程 IP 地址的信息

c# - 通过 Entity Framework 提高 SQL Server 中的搜索性能

c - 如何使用 libtomcrypt 导入 RSA 公钥?