You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java转Python:RSA/ECB/PKCS1PADDING签名逻辑实现结果不一致的问题求助

Java转Python:RSA/ECB/PKCS1PADDING签名逻辑实现结果不一致的问题求助

我仔细对比了你的Java和Python代码,发现问题出在RSA加密的输入内容以及加密方式的理解上,不是PKCS1 padding的差异,而是两个语言实现的逻辑步骤有细微但关键的不同:

核心差异分析

先拆解你的Java代码的实际执行流程:

  • 对payload生成SHA-1 digest字节数组
  • 将digest做Base16(Hex)编码,得到字节数组后转成字符串encodedDigest(默认用UTF-8编码)
  • 直接把encodedDigest的UTF-8字节数组用RSA/ECB/PKCS1Padding加密(注意这是私钥加密,不是标准RSA签名流程)
  • 最后把加密后的字节做Hex编码得到结果

而你的Python代码犯了两个关键错误:

  1. 你把Hex编码后的SHA1哈希又解码回了原始digest字节(sha1_hash_bytes = binascii.unhexlify(sha1_hash_hex)),这和Java的加密输入完全不同——Java加密的是Hex字符串的字节,你却转回了原始哈希值
  2. 你使用了private_key.sign()方法,这是标准RSA签名流程,它会自动对输入做哈希(你指定了hashes.SHA1()),还会添加PKCS1v1.5签名要求的ASN.1结构头,而Java里只是单纯对Hex字符串字节做RSA加密,没有这个额外结构

修正后的Python代码

下面是完全对齐Java逻辑的实现,手动模拟Java的私钥加密流程:

import hashlib
import binascii
import os
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization

private_key_pem = """-----BEGIN RSA PRIVATE KEY-----
MIIBOQIBAAJAXWRPQyGlEY+SXz8Uslhe+MLjTgWd8lf/nA0hgCm9JFKC1tq1S73c
Q9naClNXsMqY7pwPt1bSY8jYRqHHbdoUvwIDAQABAkAfJkz1pCwtfkig8iZSEf2j
VUWBiYgUA9vizdJlsAZBLceLrdk8RZF2YOYCWHrpUtZVea37dzZJe99Dr53K0UZx
AiEAtyHQBGoCVHfzPM//a+4tv2ba3tx9at+3uzGR86YNMzcCIQCCjWHcLW/+sQTW
OXeXRrtxqHPp28ir8AVYuNX0nT1+uQIgJm158PMtufvRlpkux78a6mby1oD98Ecx
jp5AOhhF/NECICyHsQN69CJ5mt6/R01wMOt5u9/eubn76rbyhPgk0h7xAiEAjn6m
EmLwkIYD9VnZfp9+2UoWSh0qZiTIHyNwFpJH78o=
-----END RSA PRIVATE KEY-----"""

def pkcs1_v1_5_encrypt_pad(data, key_size_bytes):
    """模拟Java RSA/ECB/PKCS1PADDING加密模式的填充逻辑"""
    padding_min_len = 8
    max_data_len = key_size_bytes - padding_min_len - 3
    if len(data) > max_data_len:
        raise ValueError("Data length exceeds max allowed for this key size")
    
    padding_len = key_size_bytes - len(data) - 3
    # 生成随机非零填充字节
    padding = b''
    while len(padding) < padding_len:
        byte = os.urandom(1)
        if byte != b'\x00':
            padding += byte
    # PKCS1 v1.5加密模式填充格式:0x00 + 0x02 + 随机非零字节 + 0x00 + 原始数据
    return b'\x00\x02' + padding + b'\x00' + data

def get_signature(payload):
    # 1. 加载私钥
    private_key = serialization.load_pem_private_key(
        private_key_pem.encode("utf-8"),
        password=None,
        backend=default_backend()
    )
    private_nums = private_key.private_numbers()
    key_size_bytes = private_key.key_size // 8  # 1024位密钥对应128字节

    # 2. Payload预处理(和Java保持一致)
    escaped_payload = payload.replace("\n", "").replace(" ", "").replace('<?xmlversion="1.0"encoding="UTF-8"?>', "")

    # 3. 生成SHA-1 digest
    sha1_hash = hashlib.sha1(escaped_payload.encode("utf-8")).digest()
    # 4. 做Hex编码(对应Java的Hex.encode(digest) -> new String(encodedBase16))
    sha1_hex_str = binascii.hexlify(sha1_hash).decode("utf-8")
    # 5. 把Hex字符串转成UTF-8字节(对应Java的encodedDigest.getBytes())
    sha1_hex_bytes = sha1_hex_str.encode("utf-8")

    # 6. 执行PKCS1 v1.5填充
    padded_data = pkcs1_v1_5_encrypt_pad(sha1_hex_bytes, key_size_bytes)
    # 7. 用私钥执行RSA加密(数学上等价于:padded_data^d mod n)
    padded_int = int.from_bytes(padded_data, byteorder='big')
    encrypted_int = pow(padded_int, private_nums.d, private_nums.n)
    encrypted_bytes = encrypted_int.to_bytes(key_size_bytes, byteorder='big')

    # 8. 把加密后的字节转成Hex编码
    hex_signature = binascii.hexlify(encrypted_bytes).decode("utf-8")
    return hex_signature

print(get_signature('helloworld'))

为什么这样修改?

  1. 对齐输入内容:我们保留了SHA1 digest的Hex编码字符串,并直接取其UTF-8字节作为加密输入,完全匹配Java的处理逻辑
  2. 模拟Java的加密填充:手动实现了PKCS1v1.5加密模式的填充规则,避免了标准签名带来的ASN.1结构差异
  3. 手动执行RSA私钥加密:通过RSA的数学原理直接完成私钥加密操作,和Java的Cipher逻辑完全一致

运行这段代码后,你应该能得到和Java完全相同的签名结果。

备注:内容来源于stack exchange,提问作者Vineeth George Abraham

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 20:09:28