c#如何验证签名JWT?

Posted

技术标签:

【中文标题】c#如何验证签名JWT?【英文标题】:c# How to verify signature JWT? 【发布时间】:2016-07-10 08:37:33 【问题描述】:

我有一个令牌,一个包含公钥的文件,我想验证签名。 我尝试根据this 验证签名。

但是,decodedCrypto 和 decodedSignature 不匹配。

这是我的代码:

public static string Decode(string token, string key, bool verify)
    
        var parts = token.Split('.');
        var header = parts[0];
        var payload = parts[1];
        byte[] crypto = Base64UrlDecode(parts[2]);

        var headerJson = Encoding.UTF8.GetString(Base64UrlDecode(header));
        var headerData = JObject.Parse(headerJson);
        var payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(payload));
        var payloadData = JObject.Parse(payloadJson);

        if (verify)
        
            var bytesToSign = Encoding.UTF8.GetBytes(string.Concat(header, ".", payload));
            var keyBytes = Encoding.UTF8.GetBytes(key);
            var algorithm = (string)headerData["alg"];
            var signature = HashAlgorithms[GetHashAlgorithm(algorithm)](keyBytes, bytesToSign);
            var decodedCrypto = Convert.ToBase64String(crypto);
            var decodedSignature = Convert.ToBase64String(signature);

            if (decodedCrypto != decodedSignature)
            
                throw new ApplicationException(string.Format("Invalid signature. Expected 0 got 1", decodedCrypto, decodedSignature));
            
        

        return payloadData.ToString();
    

我确定令牌的签名是有效的。我尝试在https://jwt.io/ 上进行验证,它显示签名已验证。 所以问题是编码、解码的算法。

有没有人能解决这个问题?算法是RS256

【问题讨论】:

@Thomas 在另一个线程***.com/questions/10055158/… 中报告了最简单的答案。 @KarthickJayaraman 我提到了上面的参考,但它不起作用。你不仔细看问题吗? 【参考方案1】:

使用JwtSecurityTokenHandler怎么样? 它可能看起来像这样:

public bool ValidateToken(string token, byte[] secret)

    var tokenHandler = new JwtSecurityTokenHandler();

    var validationParameters = new TokenValidationParameters
    
        ValidateIssuerSigningKey = true,
        IssuerSigningToken = new BinarySecretSecurityToken(secret)
    ;

    SecurityToken validatedToken;
    try
    
        tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
    
    catch (Exception)
    
       return false;
    

    return validatedToken != null;

请注意,我尚未对其进行测试,但我们在其中一个项目中使用了类似的实现

【讨论】:

这里的“BinarySecretSecurityToken”是什么?这里没有参考 @ANguyen: msdn.microsoft.com/en-us/library/… 它是IdentityModel 库的一部分。 System.IdentityModel 是 .NET Framework 的一部分,您可以通过 Nuget 获得 System.IdentityModel.Tokens.Jwt 如果您可以将密钥转换为或已经拥有证书,则可以将 IssuerSgningKey 与 X509SecurityKey 一起使用。【参考方案2】:

byte[] crypto = Base64UrlDecode(parts[2]);

在这一行中,您是JWT 令牌的base64 解码签名部分,但据我所知,该部分不是base64 编码的。请尝试此代码。 (我已经注释掉了不必要的行)

public static string Decode(string token, string key, bool verify)

    var parts = token.Split('.');
    var header = parts[0];
    var payload = parts[1];
    // byte[] crypto = Base64UrlDecode(parts[2]);
    var jwtSignature = parts[2];

    var headerJson = Encoding.UTF8.GetString(Base64UrlDecode(header));
    var headerData = JObject.Parse(headerJson);
    var payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(payload));
    var payloadData = JObject.Parse(payloadJson);

    if (verify)
    
        var bytesToSign = Encoding.UTF8.GetBytes(string.Concat(header, ".", payload));
        var keyBytes = Encoding.UTF8.GetBytes(key);
        var algorithm = (string)headerData["alg"];
        var computedJwtSignature = Encoding.UTF8.GetString(HashAlgorithms[GetHashAlgorithm(algorithm)](keyBytes, bytesToSign));
        // var decodedCrypto = Convert.ToBase64String(crypto);
        // var decodedSignature = Convert.ToBase64String(signature);

        if (jwtSignature != computedJwtSignature)
        
            throw new ApplicationException(string.Format("Invalid signature. Expected 0 got 1", decodedCrypto, decodedSignature));
        
    

    return payloadData.ToString();

【讨论】:

computedJwtSignature 返回“�3�R4p/�����g�o����1�G��#�i”并且它们不匹配。我的算法是 RS256 这行返回什么? (请输入值和类型)HashAlgorithms[GetHashAlgorithm(algorithm)](keyBytes, bytesToSign) 据我所知,RS256 应该同时获得用于计算哈希的公钥和私钥,但您只提供一个。 我只需要一个公钥来验证。我试过jwt.io,它返回签名验证。 你只在 jwt.io 网站上放了公钥?正如我所见,那里需要公钥/私钥。【参考方案3】:

我终于从同事那里得到了解决方案。

有同样问题的朋友可以试试我的代码:

public static string Decode(string token, string key, bool verify = true)

    string[] parts = token.Split('.');
    string header = parts[0];
    string payload = parts[1];
    byte[] crypto = Base64UrlDecode(parts[2]);

    string headerJson = Encoding.UTF8.GetString(Base64UrlDecode(header));
    JObject headerData = JObject.Parse(headerJson);

    string payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(payload));
    JObject payloadData = JObject.Parse(payloadJson);

    if (verify)
    
        var keyBytes = Convert.FromBase64String(key); // your key here

        AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(keyBytes);
        RsaKeyParameters rsaKeyParameters = (RsaKeyParameters)asymmetricKeyParameter;
        RSAParameters rsaParameters = new RSAParameters();
        rsaParameters.Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned();
        rsaParameters.Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned();
        RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
        rsa.ImportParameters(rsaParameters);

        SHA256 sha256 = SHA256.Create();
        byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(parts[0] + '.' + parts[1]));

        RSAPKCS1SignatureDeformatter rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa);
        rsaDeformatter.SetHashAlgorithm("SHA256");
        if (!rsaDeformatter.VerifySignature(hash, FromBase64Url(parts[2])))
            throw new ApplicationException(string.Format("Invalid signature"));
    

    return payloadData.ToString();

它对我有用。算法是RS256。

【讨论】:

这对我很有用,非常感谢。注意:RSACryptoServiceProvider 和 SHA256 是一次性的 是否有类似于此 RS256 的 PS256 实现?还是我必须更改哈希算法才能解决这个问题?我不想传递私钥,只传递上面的公钥 Man.. Java 和 Python 的库允许您通过传入算法和公钥来动态地执行此操作。 .NET 领域缺乏远程接近的等价物,我快疯了…… 什么是“Base64UrlDecode”?我收到此错误:当前上下文中不存在名称“Base64UrlDecode” @NagarajAlagusudaram 你添加了引用docs.microsoft.com/en-us/dotnet/api/… 吗?【参考方案4】:

我知道这是一个旧线程,但我可以让 recommended 你使用 this library 而不是自己写。它有一些很好的文档可以开始。我使用它没有任何问题。

【讨论】:

这个库 RS256 实现仍然不能正常工作,因为它需要一个私钥来验证令牌签名

以上是关于c#如何验证签名JWT?的主要内容,如果未能解决你的问题,请参考以下文章

如何验证或验证 JWT 签名?

JWT 令牌签名验证 javascript

如何使用在线工具手动验证 JWT 签名

使用 JWT 在 c# 中生成令牌的无效签名

如何在 dotnet core 中验证非对称签名的 JWT?

Microsoft 帐户 JWT 身份验证令牌如何签名?