我正在尝试将代码从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>)
但是,对于相同的有效负载,签名是不同的。有人可以帮忙吗适当的解决方案?
在第二个代码段中,您正在使用PKCS#1 1.5算法(Crypto.Cipher.PKCS1_v1_5
模块)加密 SHA-1摘要。那不是签名。
相反,您应该使用Crypto.Signature.pkcs1_15
的pycryptodome
模块。例如,请参见取自here的示例:
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
message = 'To be signed'
key = RSA.import_key(open('private_key.der').read())
h = SHA256.new(message)
signature = pkcs1_15.new(key).sign(h)
正如我在评论中已经提到的,PyCryptodome的encrypt
和encrypt
仅可用于通过public密钥进行加密,并通过private密钥进行解密。 PyCryptodome与M2Crypto的decrypt
或decrypt
没有1:1对应关系,它允许使用私钥加密和使用公钥解密。取而代之的是,PyCryptodome使用private_encrypt
和public_decrypt
,但是详细信息的工作方式有所不同,因此sign
和sign
不会生成相同的签名(对于相同的密钥和消息) :
verify
实现verify
中描述的RSASSA-PKCS1-V1_5填充。消息的哈希值private_encrypt
如下填充:
sign
[sign
标识摘要并且用于SHA1(其他摘要请参见RFC 8017, chapter 8.2):
H
0x00 || 0x01 || PS || 0x00 || ID || H
是ID
个填充字节,因此填充的消息具有模数的长度。
here的填充与RSASSA-PKCS1-V1_5填充的不同之处在于,不会自动添加(0x)30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14
。为了使PS
和0xFF
生成相同的签名,必须在private_encrypt
的上下文中manually添加ID
,例如:
sign
作为站点注释,原始代码中有一个错误:private_encrypt
期望数据为二进制格式,而不是十六进制字符串。
相应的PyCryptodome代码可以例如:
ID
[使用以下测试密钥(为简单起见,为512位密钥):
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
结论:可以通过简单地添加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())
来修改M2Crypto代码,使结果与PyCryptodome代码相对应。然而,相反的方法似乎是不可能的,因为PyCryptodome实现会自动添加key = """-----BEGIN PRIVATE KEY-----
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEA2gdsVIRmg5IH0rG3
u3w+gHCZq5o4OMQIeomC1NTeHgxbkrfznv7TgWVzrHpr3HHK8IpLlG04/aBo6U5W
2umHQQIDAQABAkEAu7wulGvZFat1Xv+19BMcgl3yhCdsB70Mi+7CH98XTwjACk4T
+IYv4N53j16gce7U5fJxmGkdq83+xAyeyw8U0QIhAPIMhbtXlRS7XpkB66l5DvN1
XrKRWeB3RtvcUSf30RyFAiEA5ph7eWXbXWpIhdWMoe50yffF7pW+C5z07tzAIH6D
Ko0CIQCyveSTr917bdIxk2V/xNHxnx7LJuMEC5DcExorNanKMQIgUxHRQU1hNgjI
sXXZoKgfaHaa1jUZbmOPlNDvYYVRyS0CIB9ZZee2zubyRla4qN8PQxCJb7DiICmH
7nWP7CIvcQwB
-----END PRIVATE KEY-----"""
,这显然是无法避免的。