C#如何验证JWT令牌上的签名

本文关键字:令牌 JWT 何验证 验证 | 更新日期: 2023-09-27 17:59:52

我正在尝试了解如何使用验证JWT令牌的签名。NET框架。我正在使用在中找到的令牌https://jwt.io/。

如果我了解这应该如何工作,我可以使用HMACSHA256哈希算法和前两个令牌以及一个秘密值来获得令牌的最后部分。如果匹配,则签名有效。

上的示例https://jwt.io/页面显示以以下方式计算哈希:

HMACSHA256(
        base64UrlEncode(header) + "." +
        base64UrlEncode(payload), secret
)

不幸的是,中的HMACSHA256对象。NET Framework没有这样的方法。必须传入字节[]或流。秘密也没有任何论据。然而,有一个构造函数将byte[]作为键。为了解决这个问题,我一直在将单词"secret"转换为一个字节[],用它来实例化HMACSHA256对象。

然后,我将base64编码的header.epayload字符串转换为字节[],并将其传递给HMACSHA256对象的ComputeHash方法。

这就是我遇到问题的地方。ComputeHash的输出是一个字节数组。无论我如何尝试将这个byte[]转换回字符串,它都不会与签名匹配。我不明白我哪里错了。令牌的签名部分是散列值还是base64编码的散列值?

这是我的代码:

string jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
string[] parts = jwt.Split(".".ToCharArray());
string headerDotPayload = string.Format("{0}.{1}", parts[0], parts[1]);
string signature = parts[2];
byte[] secret = System.Text.UTF8Encoding.UTF8.GetBytes("secret");
byte[] input = System.Text.UTF8Encoding.UTF8.GetBytes(headerDotPayload);
var alg = new HMACSHA256(secret);
byte[] hash = alg.ComputeHash(input);
//Attempting to verify
StringBuilder result = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
    result.Append(hash[i].ToString("x2"));
}
string verify1 = result.ToString(); //Does not match signature
string verify2 = System.Text.UTF8Encoding.UTF8.GetString(hash); //Does not match signature
byte[] verify3 = System.Text.UTF8Encoding.UTF8.GetBytes(signature); //Does not match value in the hash byte[] 

C#如何验证JWT令牌上的签名

令牌的签名部分是哈希值还是base64编码的散列值?

它是一个Base64 Url编码的哈希值。为了验证相等性,您需要对计算出的哈希值进行编码。

查看下面的单元测试,看看你的验证哪里出了问题。

[TestClass]
public class JwtUnitTest {
    [TestMethod]
    public void VerifySignature() {
        string jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
        string[] parts = jwt.Split(".".ToCharArray());
        var header = parts[0];
        var payload = parts[1];
        var signature = parts[2];//Base64UrlEncoded signature from the token
        byte[] bytesToSign = getBytes(string.Join(".", header, payload));
        byte[] secret = getBytes("secret");
        var alg = new HMACSHA256(secret);
        var hash = alg.ComputeHash(bytesToSign);
        var computedSignature = Base64UrlEncode(hash);
        Assert.AreEqual(signature, computedSignature);
    }
    private static byte[] getBytes(string value) {
        return Encoding.UTF8.GetBytes(value);
    }
    // from JWT spec
    private static string Base64UrlEncode(byte[] input) {
        var output = Convert.ToBase64String(input);
        output = output.Split('=')[0]; // Remove any trailing '='s
        output = output.Replace('+', '-'); // 62nd char of encoding
        output = output.Replace('/', '_'); // 63rd char of encoding
        return output;
    }
}