我正在尝试将我的代码从 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?
最佳答案
正如我在评论中提到的,encrypt
和 decrypt
PyCryptodome 只能用于使用公钥 加密并使用私钥 解密。 PyCryptodome 没有 M2Crypto 的 private_encrypt
或 public_decrypt
的 1:1 对应部分,它允许使用私钥加密和使用公钥解密。相反,PyCryptodome 使用 sign
和 verify
,但是它们在细节上的工作方式不同,因此 private_encrypt
和 sign
不会生成相同的签名(对于相同的 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
PS
为0xFF
填充字节,使填充后的消息长度为模数。
private_encrypt
的填充与 RSASSA-PKCS1-V1_5 的填充不同,ID
不会自动添加。为了使 sign
和 private_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/