JJWT的解密bug

问题描述

使用io.jsonwebtoken.Jwts构造了一个token,在解析这个token时,发现解析秘钥和构建秘钥不完全相同也可以成功解析,代码如下

签发token

    /** 测试生成token */
    @Test
    public void testJwt() {
        JwtBuilder jwtBuilder = Jwts.builder()
                // 唯一ID {"":""}
                .setId("888")
                // 接受的用户 {"sub":"Rose"}
                .setSubject("Rose")
                // 签发时间 {"iat":"。。。"}
                .setIssuedAt(new Date())
                // 签名算法 及秘钥
                .signWith(SignatureAlgorithm.HS256, "node");
        // 签发token
        String token = jwtBuilder.compact();
        System.out.println(token);

        String[] split = token.split("\\.");
        // 头部
        System.out.println(Base64Codec.BASE64.decodeToString(split[0]));
        // 载荷
        System.out.println(Base64Codec.BASE64.decodeToString(split[1]));
        // 算法及秘钥 这个会乱码
        System.out.println(Base64Codec.BASE64.decodeToString(split[2]));
    }
}

解析token

/** 解析token */
    @Test
    public void testParseToken() {
        String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiJSb3NlIiwiaWF0IjoxNjMyNzMxMzYyfQ.9GZJUoTNwJuMh5tFMd3giJh36YNuCnFuWsDKONOg2C8";
        // 得到的是荷载
        Claims claims = (Claims) Jwts.parser()
                // 解析时的秘钥一定要和签发时秘钥相同,但是发现这了的秘钥为4-7个x都是可以成功解析的
                .setSigningKey("nodeabc")
                // .parse(token)
                .parseClaimsJws(token)
                .getBody();
        System.out.println(claims);
    }

 解析成功。只要近似的key就能解析,这样很不安全。

问题分析

在查看设置秘钥的地方,io.jsonwebtoken.JwtBuilder有一个默认的实现类io.jsonwebtoken.impl.DefaultJwtBuilder,我们看看他的signWith方法,signWith有三个重载形式,我们只看我们调用的那个就行了

关键的地方我标出来了,signWith拿到秘钥之后是先将秘钥用base64解码之后再进行操作的。

然后再看解析token时的地方,io.jsonwebtoken.JwtParser也有一个默认的实现类io.jsonwebtoken.impl.DefaultJwtParser,设置秘钥的方法setSigningKey也有三个重载形式,我们使用的是下面这个

可以看到,setSigningKey方法也将拿到的秘钥用base64解码了,咋一看好像没什么问题,解码就解码呗,反正两边都解了,同一个字符串,解码过肯定是一样的结果,但问题恰巧就出现在这里。

虽然相同的字符串经过base64解码过后也一定是相同的,但是不同的字符串经过base64解码过后也可能是相同的!!

测试

可以看到,我测试了三种不同地方的工具类,在将xxxx和xxxxx按照base64解码时,都解码成相同的byte数组了。在jwt设置秘钥和解析秘钥时,使用的是第一种,所以就不难理解,为啥秘钥即便不完全相同,只要近似也能解析。

解决问题 

虽然搞清楚了原理,那怎么解决这个问题呢?或者这是jjwt的一个或base64的bug?是不是base64的bug我不知道,但是一定不是jjwt的bug,只是我们调用api是没有按照人家要求调用。再来看看构建token时设置秘钥的方法

人家方法的变量名叫:base64EncodedSecretKey,就是想让你传一个经过base64编码过后的字符串。接口上也有明确的注释: 

在解析token时设置秘钥的方法也有同样的注释。如果按照api要求来调用,就没有问题了,因为base64是对称加密的,解码被加密过后一样的字符串,得到的肯定是两个一样原始内容。

联想拓展
我对base64了解没那么深,只知道是一个对称加密的算法,但是也会感到奇怪,为什么对不同的字符串进行解码时,能得到相同的结果呢?

我只能尝试这么理解:Base64在加密时,是通过一步一步加密的,相同的东西,在加密时经过的步骤都是相同的,所以最终加密出来也是相同的。但是解密则有点不同,什么不同呢?如果你是经过我Base64加密过后,你再来经过我解密,我能按照加密过程的逆过程,一步一步把你还原成原来的样子。但是如果你压根就没有经过我Base64加密,你却要来走我Base64的还原过程,那我在还原的过程中,有的步骤都没法走,直接跳过了,最后把你“还原”。所以那些近似的、不是经过Base64加密的字符串,在经过Base64解密时,都有一些相同步骤跳过了,所以最终解密过后得到了相同的东西。当然,这都是我自己的理解。

另外,在测试过程中,我发现,有的地方的Base64工具类,测试的结果不一样,上面测试了三个base64解码,分别是:

io.jsonwebtoken.impl.TextCodec.BASE64
io.jsonwebtoken.impl.Base64Codec
cn.hutool.core.codec.Base64
三者在解密时,结果都是一样的。但是在解密多个不同数的时候时,就有所不同了
 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值