python-3.x - 在 Python 3.X 中实现 RSA/pkcs1_padding

标签 python-3.x python-2.7 ssl rsa

我正在尝试将我的代码从 Python 2.7 迁移到 Python 3.5

下面是 Python 2.7 中使用 M2Crypto 的当前实现

import M2Crypto
import hashlib
from binascii import hexlify

# Generates the signature of payload
def getSign(payload_xml):
    # SHA-1 digest of the payload
    dig = myDigest(payload_xml)
    # Loading the privateKey PEM file
    private_key = M2Crypto.RSA.load_key('privatekey')
    # Generating base 16 and encoding
    signature = hexlify(private_key.private_encrypt(dig, M2Crypto.RSA.pkcs1_padding))
    return signature

# To generate sha-1 digest of payload
def myDigest(payload):

    # This will give base 16 of SHA-1 digest
    digest_1 = hashlib.sha1(payload).hexdigest()
    return digest_1

sign = getSign(<mypayload_xml>)

这是 Python 3.5 中使用 pycryptodome 的新实现

from Crypto.PublicKey import RSA
import hashlib
from Crypto.Cipher import PKCS1_v1_5
from binascii import hexlify

def myDigest(payload):
    # This will give base 16 of SHA-1 digest
    digest_1 = hashlib.sha1(payload.encode('utf-8')).hexdigest()
    return digest_1

def getSign(payload_xml):
    # SHA-1 digest of the payload
    dig = myDigest(payload_xml)
    with open('privatekey', 'r') as pvt_key:
        miPvt = pvt_key.read()
    rsa_key_obj = RSA.importKey(miPvt)
    cipher = PKCS1_v1_5.new(rsa_key_obj)
    cipher_text = cipher.encrypt(dig.encode())
    base_16_new = hexlify(cipher_text)
    return base_16_new

new_sign = getSign(<mypayload_xml>)

However, for same payload, signatures are different. Can someone help with the proper solution?

最佳答案

正如我在评论中提到的,encryptdecrypt PyCryptodome 只能用于使用公钥 加密并使用私钥 解密。 PyCryptodome 没有 M2Crypto 的 private_encryptpublic_decrypt 的 1:1 对应部分,它允许使用私钥加密和使用公钥解密。相反,PyCryptodome 使用 signverify ,但是它们在细节上的工作方式不同,因此 private_encryptsign 不会生成相同的签名(对于相同的 key 和消息):


sign 实现了 RFC 8017, chapter 8.2 中描述的 RSASSA-PKCS1-V1_5 填充.消息的哈希值H填充如下:

0x00 || 0x01 || PS || 0x00 || ID || H

ID 标识摘要并用于 SHA1(有关其他摘要,请参阅 here):

(0x)30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14 

PS0xFF填充字节,使填充后的消息长度为模数。


private_encrypt 的填充与 RSASSA-PKCS1-V1_5 的填充不同,ID 不会自动添加。为了使 signprivate_encrypt 生成相同的签名,必须在 的上下文中手动添加ID private_encrypt,例如:

import M2Crypto
import hashlib
from binascii import hexlify, unhexlify

key = """-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----"""

def getSign(payload_xml):
    dig = myDigest(payload_xml)
    private_key = M2Crypto.RSA.load_key_string(key)
    signature = hexlify(private_key.private_encrypt(unhexlify('3021300906052b0e03021a05000414' + dig.hexdigest()), M2Crypto.RSA.pkcs1_padding))
    return signature

def myDigest(payload_xml):
    digest_1 = hashlib.sha1(payload_xml)
    return digest_1 

sign = getSign(b"Hello world")
print("M2Crypto: " + sign)

作为站点说明,原始代码中存在错误:private_encrypt 需要二进制格式的数据而不是十六进制字符串。


相应的 PyCryptodome 代码可以是例如:

from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA1
from Crypto.PublicKey import RSA

key = """-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----"""

def getSign(payload_xml):
    dig = myDigest(payload_xml)
    private_key = RSA.import_key(key)
    signature = pkcs1_15.new(private_key).sign(dig)
    return signature

def myDigest(payload_xml):
    digest_1 = SHA1.new(payload_xml)
    return digest_1 

sign = getSign(b'Hello world')
print("PyCryptodome: " + sign.hex())

使用以下测试 key (为简单起见,使用 512 位 key ):

key = """-----BEGIN PRIVATE KEY-----
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEA2gdsVIRmg5IH0rG3
u3w+gHCZq5o4OMQIeomC1NTeHgxbkrfznv7TgWVzrHpr3HHK8IpLlG04/aBo6U5W
2umHQQIDAQABAkEAu7wulGvZFat1Xv+19BMcgl3yhCdsB70Mi+7CH98XTwjACk4T
+IYv4N53j16gce7U5fJxmGkdq83+xAyeyw8U0QIhAPIMhbtXlRS7XpkB66l5DvN1
XrKRWeB3RtvcUSf30RyFAiEA5ph7eWXbXWpIhdWMoe50yffF7pW+C5z07tzAIH6D
Ko0CIQCyveSTr917bdIxk2V/xNHxnx7LJuMEC5DcExorNanKMQIgUxHRQU1hNgjI
sXXZoKgfaHaa1jUZbmOPlNDvYYVRyS0CIB9ZZee2zubyRla4qN8PQxCJb7DiICmH
7nWP7CIvcQwB
-----END PRIVATE KEY-----"""

这两个代码都提供以下消息:

payload_xml = b'The quick brown fox jumps over the lazy dog'

以下签名:

8324a560e6934fa1d1421b9ae37641c3b50a5c3872beecea808fbfed94151747aad69d5e083a23aa0b134d9e8c65e3a9201bb22ec28f459e605692e53965ad3b

结论:可以通过简单地添加ID修改M2Crypto代码,使结果与PyCryptodome代码对应。然而,反过来看,这似乎是不可能的,因为 PyCryptodome 实现会自动添加 ID,这显然无法阻止。

关于python-3.x - 在 Python 3.X 中实现 RSA/pkcs1_padding,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59063930/

相关文章:

python-3.x - GroupBy 但仍保留所有行

python-3.x - 使用Geopandas,如何通过采样方法在每个多边形中随机选择5个点

python - 如何运行具有绝对导入的子目录内的 python 脚本

python - 如何使用 python 在 Swift 存储系统中上传文件?

python - 在字典值上运行函数而不丢失与键的关联的更简单的方法?

python-2.7 - 强制 matplotlibrc 使用 Arial(字体路径)

java - javax.xml.soap.SOAPConnection 和 java.mail 之间的冲突

json - Python Json 引用和验证

ssl - 在线信用卡处理

django - 当不使用 TLS/SSL 时,如何在 Django 中删除 HttpRequest 连接?