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

问题描述 投票:0回答:2

有人可以提供示例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);
c# rsa jwt sha256 wso2-am
2个回答
2
投票

来自WSO2 API网关的JWT不遵循规范(https://tools.ietf.org/html/rfc7519)。

我见过的所有样品都是以下形式:

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

但应该是:

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

问题是使用Base64而不是Base64Url编码。由于签名基于<Base64EncodedHeader>.<Base64EncodedPayload>,并且MS JWT框架正在验证针对预期的<Base64UrlEncodedHeader>.<Base64UrlEncodedPayload>的签名,因此它将始终无法通过验证。我必须编写自己的自定义签名验证码来解决此问题。然后,在使用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);
}

0
投票

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类”

© www.soinside.com 2019 - 2024. All rights reserved.