【问题标题】:C# .NET Code to validate WSO2 API Gateway JWT signature, using SHA256withRSA algorithm使用 SHA256withRSA 算法验证 WSO2 API Gateway JWT 签名的 C# .NET 代码
【发布时间】:2016-01-18 07:20:06
【问题描述】:

有人可以提供示例 C# .NET 代码来验证由 WSO2 API 网关发布的 JWT,该 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);

【问题讨论】:

    标签: c# rsa jwt sha256 wso2-am


    【解决方案1】:

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

    我看到的所有样本都是这样的:

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

    但应该是:

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

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

    这是最终代码:

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

    【讨论】:

      【解决方案2】:

      WSO2 提供了将 JWT 的格式更改为 URL 编码的选项,之后不再需要自定义代码。

      文档@https://docs.wso2.com/display/AM260/Passing+Enduser+Attributes+to+the+Backend+Using+JWT 提及:

      “但是,对于某些应用程序,您可能需要使用 Base64URL 编码。要使用 Base64URL 编码对 JWT 进行编码,请在 /repository/conf/api-manager.xml 的元素中添加 URLSafeJWTGenerator 类”

      【讨论】:

        猜你喜欢
        • 2012-08-02
        • 2021-08-10
        • 2018-05-15
        • 1970-01-01
        • 1970-01-01
        • 2021-11-20
        • 2021-10-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多