【问题标题】:JSON Web Tokens Not Decodable on jwt.io: "JWT payload is not a valid JSON object"JSON Web 令牌在 jwt.io 上不可解码:“JWT 有效负载不是有效的 JSON 对象”
【发布时间】:2021-06-01 16:31:39
【问题描述】:

使用下面的代码,我可以构建一个解码的 JSON Web Token (decodedToken)。当我将它粘贴到https://jwt.io 的调试器中时,标题和有效负载正确显示在右侧。但是,当我将编码后的令牌 (encodedToken) 粘贴到调试器中时,我会收到以下消息:

看起来您的 JWT 负载不是有效的 JSON 对象。根据https://www.rfc-editor.org/rfc/rfc7519#section-7.2,JWT 有效负载必须是顶级 JSON 对象

它还在底部用大红色字母写着“无效签名”。

另外,当使用编码令牌时,头部区域显示我解码的头部,而有效负载区域显示我的整个解码令牌。所以不知何故 jwt.io 成功地解码了 JWT。但它不应该尝试将整个解码令牌的 JSON 解析为一个单元。它应该尝试分别解析标头和有效负载部分的 JSON。

我想知道为什么 jwt.io 不能完全读取我的编码令牌的有效负载。 decodedTokenencodedToken 的值在下面的源代码中显示为 cmets。谢谢。

private static string GetEncodedToken(string privateKey)
{
    JObject header = JObject.FromObject(new { alg = "ES384", typ = "JWT" });
    JObject payload = JObject.FromObject(new { name = "value" });//simple test JSON data
    string decodedToken = Base64URLEncode(header) + "." + Base64URLEncode(payload);
    //eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidmFsdWUifQ
    byte[] privateKeyBytes = Convert.FromBase64String(privateKey);
    using (ECDsa ecdsa = ECDsa.Create())
    {
        ecdsa.ImportECPrivateKey(privateKeyBytes, out _);
        string encodedToken = JWT.Encode(decodedToken, ecdsa, JwsAlgorithm.ES384);
        //eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.ZXlKaGJHY2lPaUpGVXpNNE5DSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnVZVzFsSWpvaWRtRnNkV1VpZlE.kYj0M2GNC5w9oRrIVcHJ9dSQLbOEjNjxd61zSXD5iz0nBRRWMWyxj2l1DAhKPa_hpaFUscuubCzYe_W1SKpo3s06on-hYnICgQH4fVoAwZxiM_N4W761jecOExfivztn
        return encodedToken;
    }
}

private static string Base64URLEncode(JObject jObject)
{
    string jsonString = jObject.ToString(Formatting.None);
    byte[] jsonBytes = Encoding.UTF8.GetBytes(jsonString);
    string escapedBase64 = Convert.ToBase64String(jsonBytes).TrimEnd('=').Replace('+', '-').Replace('/', '_');
    return escapedBase64;
}

更新

感谢您到目前为止的回答。我想我已经接近了,但 jwt.io 仍然说,“无效签名”,即使我已经粘贴了公钥。使用以下信息,我希望有人能告诉我为什么说我的签名无效。

我生成了一个临时私钥,只是为了在 stackoverflow.com 上调试这种情况: MIGkAgEBBDBywQ7LVcyOGzxJ0Tpjpww2zUZbbtb3WVm4A3uv7ho31jJzQRYTpSqR7+ORAdoxmamgBwYFK4EEACKhZANiAASG50vW1r/O1XmUbTBb6yx1YSABh1USA6MJ8HJnYJ58tjGGVPL88a6Z1gOUlAsHtNhL44PhnTNTNNFdaH2Z41yn7oZmBhuon0vuUNFic2HDpfa/uFwRUAmhSBQz8hu+980=

我用我的私钥初始化了一个ecdsa 的实例,并用Convert.ToBase64String(ecdsa.ExportSubjectPublicKeyInfo()) 生成了一个公钥: MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEhudL1ta/ztV5lG0wW+ssdWEgAYdVEgOjCfByZ2CefLYxhlTy/PGumdYDlJQLB7TYS+OD4Z0zUzTRXWh9meNcp+6GZgYbqJ9L7lDRYnNhw6X2v7hcEVAJoUgUM/IbvvfN

这是更新后的代码:

private static string GetSignedEncodedToken(string privateKey)
{
    JObject payload = JObject.FromObject(new { name = "value" });//simple test JSON data
    string payloadString = payload.ToString(Formatting.None);
    byte[] privateKeyBytes = Convert.FromBase64String(privateKey);
    using (ECDsa ecdsa = ECDsa.Create())
    {
        ecdsa.ImportECPrivateKey(privateKeyBytes, out _);
        string signedEncodedToken = JWT.Encode(payloadString, ecdsa, JwsAlgorithm.ES384);
        return signedEncodedToken;
    }
}

这是签名的编码令牌:eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.ew0KICAibmFtZSI6ICJ2YWx1ZSINCn0.KpJAgc3-yaoGmHGAXHOeH3BPgpxdBRm461yWia60dgjuQHG5iLnwLQtQgdZtsHnI-bEK_wdmvu85ZrF7n-TdWiFb4FQxGeLBeeRfnMLJhKfInu_7MYEWPS2Ohm4yBAqg

更新解决方案

正如 jps 所提到的,我需要用“-----BEGIN PUBLIC KEY-----”和“-----END PUBLIC KEY-----”来包装我的公钥。然后 jwt.io 显示“签名已验证”。

【问题讨论】:

  • 还有一件事,您的有效负载中似乎有换行符编码。我不知道你是如何产生payloadString,但它应该是紧凑的形式(去掉所有空格)
  • @jps,我已经更新了我的代码。它现在格式化 JSON 而不进行格式化。非常感谢。

标签: c# json jwt ecdsa jwt.io


【解决方案1】:

你在这里调用的decodedToken 基本上已经是一个没有签名的编码令牌。函数JWT.Encode 采用第一个参数并重复您已经手动执行的操作,即它为您创建一个base64url 编码的标头和有效负载,然后对令牌进行签名。您不需要创建自己的标头,也不需要关心 base64url 编码。

因此,您只需要在那里传递您的 payload(不是 base64 编码)。结果是一个签名的令牌。

并且 jwt.io 显示“无效签名”,因为您可能没有将公钥传递到右侧列中的“公钥”字段。在不知道公钥的情况下,无法验证签名。

当您将公钥粘贴到那里时,请确保它具有页眉和页脚行 -----BEGIN PUBLIC KEY----- -----END PUBLIC KEY-----,然后验证应该可以正常工作。

【讨论】:

  • 您的回答很有帮助。我投了赞成票。我已经更新了我的问题,希望您能就“无效签名”消息提供意见。谢谢。
  • 验证工作正常,粘贴密钥时必须保留页眉和页脚行-----BEGIN PUBLIC KEY----- -----END PUBLIC KEY-----
  • Precisely Public and Private 包括所有细节,包括标题,使用多行字符串
【解决方案2】:

您提供的令牌,我在jwt.io 调试器中使用了相同的令牌,可以看到几个问题:

eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.ZXlKaGJHY2lPaUpGVXpNNE5DSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnVZVzFsSWpvaWRtRnNkV1VpZlE.kYj0M2GNC5w9oRrIVcHJ9dSQLbOEjNjxd61zSXD5iz0nBRRWMWyxj2l1DAhKPa_hpaFUscuubCzYe_W1SKpo3s06on-hYnICgQH4fVoAwZxiM_N4W761jecOExfivztn

以下是解码细节:

标题(正确):

{
  "alg": "ES384",
  "typ": "JWT"
}

有效载荷(不正确):

"eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidmFsdWUifQ"

因为 Payload 应该是 Json,而不是 Base64Encoded 字符串。您应该能够对其进行修改以生成新的 Token。

现在主要错误:

解码调试器肯定需要Public Key,它可以与Private Key匹配,如果你使用非对称加密,如果你使用Symmetric encryption,那么两个密钥将相同。

您还可以选择添加私钥以使用修改后的算法或有效负载生成新令牌。

您可以通过编程方式查看详细信息:

  1. 您正在提供正确的公钥以匹配私钥
  2. 您以编程方式将 Base64String 转换为 Json。这对于 JWT 调试器是不可行的

【讨论】:

  • 感谢您的回答。这很有启发性。我投了赞成票。
  • 欢迎@user1325179,你觉得它有用
猜你喜欢
  • 2018-12-04
  • 2022-10-25
  • 2018-11-18
  • 1970-01-01
  • 2019-10-29
  • 2017-01-06
  • 2017-07-10
  • 2018-09-15
  • 1970-01-01
相关资源
最近更新 更多