吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 457|回复: 5
收起左侧

[已解决] 这个python代码要如何修改,才能使得token结果与java代码一致?

[复制链接]
windindind 发表于 2024-8-29 22:29
本帖最后由 windindind 于 2024-8-30 10:29 编辑

我从一个jar包中查询到jwt token生成的过程,关键代码如下
[Java] 纯文本查看 复制代码
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret1.getBytes(StandardCharsets.UTF_8),"HmacSHA256"));
mac.update(secret2.getBytes(StandardCharsets.UTF_8));
jwtSecret = new String(mac.doFinal());

Algorithm algorithm = Algorithm.HMAC256(jwtSecret);
String token = JWT.create()
.withClaim("a", a)
……
.sign(algorithm);
System.out.println("Generated JWT Token: " + token);


ai告诉我等效的python代码如下
[Python] 纯文本查看 复制代码
mac = hmac.new(secret1.encode('utf-8'), digestmod=hashlib.sha256)
mac.update(secret2.encode('utf-8'))
jwt_secret = mac.digest()

token = jwt.encode({
"a": a,
……
}, jwt_secret, headers={"typ": "JWT", "alg": "HS256"}, sort_headers=False)
print(token)


经过反复调试,终于使得两段代码生成的JWT token中,headerpayload完全一致(其实就是增加“sort_headers=False”,python默认会对header排序)
问题是,我传入相同的secret1、secret2两段代码生成的token,signature部分完全不同
经过调试,我觉得关键在于jwt_secret的生成上,python的mac.digest()无法等同java的mac.doFinal(),但是我不知道要怎么处理

还请大佬指点一下,python下应该怎么写,才能使得生成的token相同

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

爱飞的猫 发表于 2024-8-30 05:34
代码不全,有部分代码省略了。
建议放出完整的可执行的部分代码。
 楼主| windindind 发表于 2024-8-30 08:04
爱飞的猫 发表于 2024-8-30 05:34
代码不全,有部分代码省略了。
建议放出完整的可执行的部分代码。

补充完整可执行的代码
[Java] 纯文本查看 复制代码
package com.example;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;

public class test {
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {

        String jwtSecret;
        String secret1 = "key1";
        String secret2 = "key2";
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(secret1.getBytes(StandardCharsets.UTF_8),
                "HmacSHA256"));
        mac.update(secret2.getBytes(StandardCharsets.UTF_8));
        jwtSecret = new String(mac.doFinal(), StandardCharsets.UTF_8);
        System.out.println("jwtSecret: " + jwtSecret);
        Algorithm algorithm = Algorithm.HMAC256(jwtSecret);
        String token = JWT.create()
                .withClaim("a", "b")
                .sign(algorithm);
        System.out.println("Generated JWT Token: " + token);
    }
}

以上代码运行结果应该输出的是
Generated JWT Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhIjoiYiJ9.53lahvsO7tuBgvpEM5iVtB8p7y6Lm3vx9cWa6aPZ_YE

[Python] 纯文本查看 复制代码
import hmac
import hashlib
import jwt

secret1 = "key1"
secret2 = "key2"
mac = hmac.new(secret1.encode('utf-8'), digestmod=hashlib.sha256)
mac.update(secret2.encode('utf-8'))
jwt_secret = mac.digest()

token = jwt.encode({"a": "b"}, jwt_secret, headers={"typ": "JWT", "alg": "HS256"}, sort_headers=False)
print(f"Generated JWT Token: {token}")

以上代码运行结果应该输出的是
Generated JWT Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhIjoiYiJ9.siAARkN0H1OXXdPg9N10ARjK9ga2-D_eC_O8SL3urHM

可以看到signature部分是不同的
爱飞的猫 发表于 2024-8-30 09:29
本帖最后由 爱飞的猫 于 2024-8-30 09:32 编辑

Java 处理文字转码的情况和 Python 不一样,遇到非法字符的时候会替换为 EF BF BD

Python 也让他这么处理就行。

此外,或许是我找的 Java JWT 库版本不同,我找的这个版本会对 header 进行排序,因此我在 py 版本也做了对应的更改。最终生成的 token 一致。
实际上不需要管它要不要进行排序,因为签名验证是对你提供的编码后的数据进行校验的(先校验,后反序列化)。


Java 可以观察到这一现象:

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;

public class Main {
    public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeyException {
        String jwtSecret;
        String secret1 = "key1";
        String secret2 = "key2";
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(secret1.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
        mac.update(secret2.getBytes(StandardCharsets.UTF_8));
        byte[] hmac_digest = mac.doFinal();
        System.out.print("hmac_digest: ");
        for (byte b : hmac_digest) {
            System.out.printf("%02X ", b);
        }
        System.out.println();

        jwtSecret = new String(hmac_digest, StandardCharsets.UTF_8);
        System.out.println("jwtSecret: " + jwtSecret);
        byte[] secretBytes = jwtSecret.getBytes(StandardCharsets.UTF_8);
        for (byte b : secretBytes) {
            System.out.printf("%02X ", b);
        }
        System.out.println();

        {
            Algorithm algorithm = Algorithm.HMAC256(jwtSecret);
            String token = JWT.create()
                    .withClaim("a", "b")
                    .sign(algorithm);
            System.out.println("Generated JWT Token (jwtSecret): " + token);
        }
        {
            Algorithm algorithm = Algorithm.HMAC256(secretBytes);
            String token = JWT.create()
                    .withClaim("a", "b")
                    .sign(algorithm);
            System.out.println("Generated JWT Token (secretBytes): " + token);
        }
    }
}

输出(注意打印 jwtSecret 数组时,多次出现的 EF BF BD):

hmac_digest: BA 20 B4 F6 37 E6 2A 43 D8 36 81 FA 4C 22 24 EA AA 06 DA 9D CE 9C 23 EA 63 E3 07 2D A0 B2 B8 04 
jwtSecret: ? ??7?*C?6??L"$???#?c?-???
EF BF BD 20 EF BF BD EF BF BD 37 EF BF BD 2A 43 EF BF BD 36 EF BF BD EF BF BD 4C 22 24 EF BF BD 06 DA 9D CE 9C 23 EF BF BD 63 EF BF BD 07 2D EF BF BD EF BF BD EF BF BD 04 
Generated JWT Token (jwtSecret): eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.5bL_MzQ0Kv6RcnfeG9e5vw5YC-dHJTwFTUAv6roVdBA
Generated JWT Token (secretBytes): eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.5bL_MzQ0Kv6RcnfeG9e5vw5YC-dHJTwFTUAv6roVdBA

Python:

import hmac
import hashlib
import jwt

secret1 = "key1"
secret2 = "key2"
mac = hmac.new(secret1.encode('utf-8'), digestmod=hashlib.sha256)
mac.update(secret2.encode('utf-8'))
jwt_secret = mac.digest()

jwt_secret_bytes = jwt_secret.decode('utf-8', errors='replace')

print(f'JWT Secret (bytes): {jwt_secret_bytes.encode('utf-8')}')
print(f'JWT Secret (bytes/old): {jwt_secret}')

token = jwt.encode({"a": "b"}, jwt_secret_bytes, headers={"typ": "JWT", "alg": "HS256"}, sort_headers=True)
print(f"Generated JWT Token: {token}")

跑 py 版本可以看到也加入了对应的 ef bf bd 字符:

JWT Secret (bytes): b'\xef\xbf\xbd \xef\xbf\xbd\xef\xbf\xbd7\xef\xbf\xbd*C\xef\xbf\xbd6\xef\xbf\xbd\xef\xbf\xbdL"$\xef\xbf\xbd\x06\xda\x9d\xce\x9c#\xef\xbf\xbdc\xef\xbf\xbd\x07-\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd\x04'
JWT Secret (bytes/old): b'\xba \xb4\xf67\xe6*C\xd86\x81\xfaL"$\xea\xaa\x06\xda\x9d\xce\x9c#\xeac\xe3\x07-\xa0\xb2\xb8\x04'
Generated JWT Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.5bL_MzQ0Kv6RcnfeG9e5vw5YC-dHJTwFTUAv6roVdBA

免费评分

参与人数 2吾爱币 +2 热心值 +1 收起 理由
windindind + 1 + 1 谢谢@Thanks!
yinming.work + 1 用心讨论,共获提升!

查看全部评分

 楼主| windindind 发表于 2024-8-30 10:13
爱飞的猫 发表于 2024-8-30 09:29
[md]Java 处理文字转码的情况和 Python 不一样,遇到非法字符的时候会替换为 `EF BF BD`

Python 也让他 ...

感谢指导

关于header 进行排序,我知道签名的过程是先编码后加密:HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret)
一开始我发现生成的token签名不一致的时候,header部分也不一致,我以为是这个原因造成的,所以死磕header排序
当然,签名验证的时候,只要secret和token正确,与header排序无关了

“Java 处理文字转码的情况和 Python 不一样,遇到非法字符的时候会替换为EF BF BD”
这点我在逐字节对比的时候有发现,当数值为正数的时候,两者是一致的,当出现负数的时候,就对应EF BF BD,但是我不知道原因,也就无从下手了

点评

负数通常是遇到非法字节啦。utf8 编码规则可以有负数,但是需要遵守它的编码规则。不过我不清楚它会不会验证是否能映射到具体字符。 如果你去搜索EF BF BD,可以发现它的名字是“REPLACEMENT CHARACTER”,即无法  详情 回复 发表于 2024-8-30 16:42
爱飞的猫 发表于 2024-8-30 16:42
windindind 发表于 2024-8-30 10:13
感谢指导

关于header 进行排序,我知道签名的过程是先编码后加密:HMACSHA256(base64UrlEncode(header ...

负数通常是遇到非法字节啦。utf8 编码规则可以有负数,但是需要遵守它的编码规则。不过我不清楚它会不会验证是否能映射到具体字符。

如果你去搜索EF BF BD,可以发现它的名字是“REPLACEMENT CHARACTER”,即无法正确编码时,替换为这个字符表示错误。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-12 10:06

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表