JWT 无法从密钥中破译

Posted

技术标签:

【中文标题】JWT 无法从密钥中破译【英文标题】:JWT can not be decipher from keys 【发布时间】:2021-12-20 02:38:57 【问题描述】:

创建了以下 JWT 密钥。 但是,我们想将 io.jsonwebtoken 升级到 0.11.2 并看到 signWithMethod 已被弃用。 将签名更改为密钥,但是,旧密钥与密钥解密不兼容...为什么,如何? 如果它破坏了已发布的密钥,则无法进入 prod。

       private final String secretKeyEncoded = Base64.getEncoder().encodeToString(secretKey.getBytes());


       Key key = new SecretKeySpec(secretKeyEncoded.getBytes(), SignatureAlgorithm.HS256.getJcaName());

        final String originalJWT = Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(now)
                .setExpiration(validity)
                .signWith(SignatureAlgorithm.HS256, secretKeyEncoded)
                .compact();

        Jwts.parser().setSigningKey(secretKeyEncoded).parseClaimsJws(originalJWT);//old way
        Jwts.parserBuilder().setSigningKey(secretKeyEncoded).build().parseClaimsJws(originalJWT);
        Jwts.parserBuilder().setSigningKey(new String(key.getEncoded())).build().parseClaimsJws(originalJWT);
        Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(originalJWT);//!!!!!! THIS IS NOT WORKING
        final String newJWT = Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(now)
                .setExpiration(validity)
                .signWith(key, SignatureAlgorithm.HS256)
                .compact();

        Jwts.parser().setSigningKey(key).parseClaimsJws(newJWT);//old way depricated
        Jwts.parserBuilder().setSigningKey(secretKeyEncoded.getBytes()).build().parseClaimsJws(newJWT);
        Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(newJWT); // THIS ONE WORKS !!!!

【问题讨论】:

【参考方案1】:

signWith overload that takes a String 要求字符串包含实际密钥的 base64 编码,但其他重载(包括 Keybyte[])采用实际密钥而不是 base64 编码。这一点说得很清楚;你没读过javadoc吗? 此更正的代码使用 base64 用于 String 版本而不是 Key AND byte[] 版本适用于所有组合。

static void SO69863437Jjwt (String[] args) throws Exception 
    final String secretKey = "thisisanicelongsecretkey12345678", // BAD IDEA: shouldn't be String
            secretKeyEncoded = Base64.getEncoder().encodeToString(secretKey.getBytes());
    final Date now = new Date(), validity = new Date(now.getTime()+86400000);

    Key key = new SecretKeySpec( //WRONG secretKeyEncoded.getBytes(), SignatureAlgorithm.HS256.getJcaName());
            secretKey.getBytes(), SignatureAlgorithm.HS256.getJcaName()); // NOT BASE64!

    @SuppressWarnings("deprecation")
    final String originalJWT = Jwts.builder() //.setClaims(claims)
            .setIssuedAt(now) .setExpiration(validity)
            .signWith(SignatureAlgorithm.HS256, secretKeyEncoded)
            .compact();
    //Jwts.parser().setSigningKey(secretKeyEncoded).parseClaimsJws(originalJWT);//old way
    Jwts.parserBuilder().setSigningKey(secretKeyEncoded).build().parseClaimsJws(originalJWT);
    //ONLY WORKED WITH WRONG Key Jwts.parserBuilder().setSigningKey(new String(key.getEncoded())).build().parseClaimsJws(originalJWT);
    Jwts.parserBuilder().setSigningKey(secretKey.getBytes()).build().parseClaimsJws(originalJWT); //BETTER
    Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(originalJWT);// NOW WORKS

    final String newJWT = Jwts.builder() //.setClaims(claims)
            .setIssuedAt(now) .setExpiration(validity)
            .signWith(key, SignatureAlgorithm.HS256)
            .compact();
    //Jwts.parser().setSigningKey(key).parseClaimsJws(newJWT);//old way depricated
    //ONLY WORKED WITH WRONG Key Jwts.parserBuilder().setSigningKey(secretKeyEncoded.getBytes()).build().parseClaimsJws(newJWT);
    Jwts.parserBuilder().setSigningKey(secretKey.getBytes()).build().parseClaimsJws(newJWT); //BETTER
    Jwts.parserBuilder().setSigningKey(secretKeyEncoded).build().parseClaimsJws(newJWT); // FIXED
    Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(newJWT); // THIS ONE WORKS !!!!
    
    System.out.println ("done");

但是请注意,使用任意 Java String 的字节作为加密密钥是一个坏主意(因此我的评论)。它限制了熵,使您的系统不那么安全,特别是如果与根本不安全的具有人类意义的内容(例如我的示例!)一起使用,而且getBytes() 是依赖于语言环境的,因此如果跨区域使用它可能会导致一切失败多个系统或环境——尽管后一个问题对于 HMAC 签名可能不太重要,因为它们通常只能在单个系统上安全使用。

这里根本没有解密(或加密),只有签名生成和验证。

【讨论】:

以上是关于JWT 无法从密钥中破译的主要内容,如果未能解决你的问题,请参考以下文章

错误,Lcobucci\JWT\Signer\InvalidKeyProvided:无法解析您的密钥

各种微服务中 JWT 的共享签名密钥

KEIL5 密钥过期 怎么办 还能破译吗

如何使用 sha512 为 JWT 生成 RSA 密钥?

如何使用 JWT 从前端(角度 4)将密钥传递到后端(节点 js)

使用 Rails RPUSH gem 为 APNS 编码 JWT 密钥