【问题标题】:Decoding jwt error: Unable to decode the payload as Base64Url encoded string解码 jwt 错误:无法将有效负载解码为 Base64Url 编码字符串
【发布时间】:2017-08-21 20:13:03
【问题描述】:

我有一个 jwt 令牌 'eyJhbGciOiJIUzI1NiJ9.YUExIQ.srnc87a8Se8arhCopLBpgxEvILA2AZxOB8BIrFDHKL4 ' 那是在节点中编码,我正在尝试在 c# 中对其进行解码,但我收到了一个错误,表明它对有效负载感到不安。

    public void Consume(BasicDeliverEventArgs msg)
    {
        var message = Encoding.UTF8.GetString(msg.Body);
        var user = JsonConvert.DeserializeObject<User>(message);

        var handler = new JwtSecurityTokenHandler();

        try
        {    
            var x = handler.ReadToken(user.Password) as JwtSecurityToken;
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.Message);
        }



        _repo.UpdateUser(user);
    }

返回错误:无法将有效负载“YUExIQ”解码为 Base64Url 编码字符串

我不确定是否可以将用于在解码中编码的秘密放在某处,或者我是否需要以某种方式将有效负载转换为 base64,或者我是否只是在某处遗漏了一些步骤。提前致谢。

【问题讨论】:

    标签: c# node.js base64 jwt


    【解决方案1】:

    使用 JWT,您可以省略填充字节。 .NET 需要在末尾填充 = 字符,以使字符数成为 4 的倍数。YUExIQ== 应该是可解码的。

    【讨论】:

    • 您需要分别为每个点分隔部分添加填充。 = 字符在 JWT 中无效,但在解析时,您必须添加它们。
    • 我不确定你的意思,因为 ReadToken 接受一个字符串参数。你想让我 split(.) 并添加 = 填充然后将它们连接在一起吗?
    • 我认为错误在您调用的方法中,而不是在您发布的代码中。这是第三方库吗?
    【解决方案2】:

    既然您询问了使用 Base64 进行解码的问题,我将重点关注这个问题,而不是 .NET 中 JWT 的语义。这应该会稍微揭开 JWT 令牌的神秘面纱。

    请注意,只有上面的 JWT 字符串的第一段是有效的——它转换为 {"alg":"HS256"},而第二段转换为 aA1!——所以为了举例,我将使用来自 http://jwt.io 的默认示例值:

    var jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
    

    因此,根据 JWT.io's introduction,JWT 令牌中只有三个段:

    • 标题
    • 有效载荷
    • 签名

    假设令牌没有加密,前两个段可以使用普通的 Base64 解码(一旦你将长度填充为四的倍数,就像上面提到的 @Jacob 一样)。最后一段只是一个密码签名;它不包含任何要解码的有意义的编码信息。

    因此,显示解码后的 JWT 内容的简单代码 sn-p 可能如下所示:

    var decoded = jwt.Split('.')
      .Take(2)
      .Select(x => 
        Encoding.UTF8.GetString(
          Convert.FromBase64String(
            x.PadRight(x.Length + (x.Length % 4), '='))))
    .Aggregate((s1, s2) => s1 + Environment.NewLine + s2);
    
    Console.WriteLine(decoded);
    
    /* Prints:
       {
         "alg": "HS256",
         "typ": "JWT"
       }
       {
         "sub": "1234567890",
         "name": "John Doe",
         "admin": true
       }
    */
    

    分离所有 lambda 的更简单示例如下所示:

    /// <summary>
    /// A function to make the Base64 decoding a little less
    /// verbose below:
    /// </summary>
    string Base64Decode(string value)
    {
        return Encoding.Default.GetString(Convert.FromBase64String(value)); 
    }
    
    // Get the first two segments into an enumerable:
    var pieces = jwt.Split('.').Take(2);
    // Pad them with equals signs to a length that is a multiple of four:
    var paddedPieces = pieces.Select(x => x.PadRight(x.Length + (x.Length % 4), '='));
    // Base64 decode the pieces:
    var decodedPieces = paddedPieces.Select(x => Base64Decode(x));
    // Join it all back into one string with .Aggregate:
    Console.WriteLine(decodedPieces.Aggregate((s1, s2) => s1 + Environment.NewLine + s2));
    

    这里的结果和上面的一样。

    当然,如果你打算做有意义的 JWT 操作,比如签名和验证,你应该使用 System.IdentityModel.Tokens.Jwt 中的 JwtSecurityToken 类。但是如果你只需要找出一个未加密的令牌包含哪些信息,就和上面一样简单。

    【讨论】:

    • 我认为这个答案还不错,但没有提到base64url 用于JWT。因此,需要将“-”到“+”和“_”到“/”的字符串替换。
    【解决方案3】:

    我无法添加评论,只想添加到@paul-smith 答案。

    他的回答有效,但在将字符串填充为 4 的倍数的逻辑中存在一个小错误。

    而不是,

    var paddedPieces = pieces.Select(x => x.PadRight(x.Length + (x.Length % 4), '='));
    

    应该是……

    var paddedPieces = pieces.Select(x => x.PadRight(x.Length + ((x.Length % 4 != 0) ? (4 - x.Length % 4) : 0), '='));
    

    您需要检查“x.Length % 4 != 0”,否则在字符串已经是 4 的倍数的情况下,它可能会在末尾添加“====”,这会破坏 Base64 解码。但真正的问题是应该是 (4 - x.Length % 4) 而不是 (x.Length % 4)

    例如

    如果字符串长度为 7 个字符...

    7 + (7 % 4) = 10
    7 + (4 - (7 % 4) = 8

    【讨论】:

      猜你喜欢
      • 2017-08-17
      • 2020-09-18
      • 2018-08-06
      • 1970-01-01
      • 2017-03-31
      • 2011-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多