我正在做一个项目,其中客户端代码是Java的,他们正在使用NONEWithRSA
算法对字符串进行签名和验证。
我正在使用PyCryptodome
为给定字符串生成符号。 https://pycryptodome.readthedocs.io/en/latest/src/signature/pkcs1_v1_5.html
但我还没有发现它使用NONEWithRSA
算法,因此它提供的签名与我的客户在服务器上的期望不同。
python上是否有任何库支持此算法?
我最近在尝试支持Android应用程序的签名生成时遇到了同样的问题,该应用程序期望等同于"NONEwithRSA"。 就我而言,这实际上是应用程序使用哈希的情况,但它显然缺少 ASN.1 代码前缀。
事实证明,Java RSA密码在幕后就像魔术一样工作,并自动使用签名块类型PKCS#1填充(以便每个消息/密钥组合产生相同的签名(。
这给我带来了问题,因为我最初认为我可以使用 Python 中的私钥加密原始哈希,然后它与 Java 兼容。但是,在 PyCryptodome 中使用私钥进行加密使用加密块类型 PKCS#1,这在使用 Java 验证时会导致 v15 unpad 异常。
我发现最简单的解决方案是使用 rsa 包 (https://pypi.org/project/rsa/( 并直接访问它的一些内部方法。
key = rsa.PrivateKey.load_pkcs1(
RSA.import_key(base64.b64decode(private_key)).export_key()
)
keylength = rsa.pkcs1.common.byte_size(key.n)
padded = rsa.pkcs1._pad_for_signing(message, keylength)
payload = rsa.pkcs1.transform.bytes2int(padded)
encrypted = key.blinded_encrypt(payload)
block = rsa.pkcs1.transform.int2bytes(encrypted, keylength)
signature = base64.b64encode(block).decode()
从本质上讲,此代码源自 rsa/pkcs1.py模块的"sign_hash"方法。我们只是不添加 ASN.1 代码。
这个解决方案诚然是脆弱的,但它应该给你一个很好的工作参考,使一些东西更强大。
验证
对于相同的密钥/输入,此 Python 签名生成过程生成与 Java "NONEwithRSA" 算法相同的签名。
private_key = "MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDhqFoTCc5FEgL3OiM4yEmN/rLkS+bSBigRLW+2N+TdbR/n1O48fUqUoPPa4sEETz7KRHI8EIl+FISyJCk07sks47ucbdfmy0d9omb/GmDnwiRwKe0vFtoTZnrO+sW+l45VEmOymfPYeo6E5pdi9jp7oVZufGiFGryp9DX6mzmpw6unjeZpSfqWaQfba4+EjxO8uiN2GKNlFFxyL6ohrKafGsS9rPaMZv9U4dGpY2ls1855kt8WG4RCHxv8Dh5e0Uj2R3VVnuM/ZvwrW/KLvAf6aUWOZxZhqLknBLUrN+Wh+1KcwZ3PsUoZWAWtY/IIisy/EWatNZYQ0nbk9vFYeEYvAgMBAAECggEBANCJuXxAopwYMh9xXd160ubji0UJk+1h/SMqv0NBUkMDNuoTUHscOpEyUUlmJoQqC9f2+y7hBX87zQmn+2DMj6PpbZb5t/20vIOxUsfMA+QJ+YyiYmwTDUPCgBnJjHc/Li0WSoZiWgQj9pOBWkP9Uzyvc+W1GQ5xEAtsAXpwexCTZB/4Ce6Kmlps4K7QyAJhZnefDNOdtvDzqDlYP0hCfolNK9Y7w0+eGYRBMw7eoxtPlWhAwW+/49/Kljf6YOGYPDY9fP+X+U06YNyuP4kbWLRSaW+uYfLzol2lN1U9y9O83FbvIYI/m4fJVq0xveTkpTj6nwAm6FHcumsoIXEEQykCgYEA937skZ01b6/ea04erQA4kCZZIdAVXrZBSuFHsK/XozBLwVxE3rIIiGAVP5F27GFYsQZzqavx70IIJ/jjYYTc2HRxkT+yU4eXJKTwZTsyuGgR9YAqQrTgfA38NIJ3fYMcwzLOd5gR32GRWJELZI+DT4h+y2PjNPckXfuDQXurMQUCgYEA6WlUgic4yuhnjsT6lZVHCS+RFcp4rgSMBufn24zVkFLS5ajiczth2qMKQquewawoHnGWPQrD1JC1uPtZVL1Xu8oySx13BFlY2Mr6uTJG2TIMY2sh1Nk85QAUXye2nfS1LcuZ3hr6De/B2OCtf+F23dHZ9domxMzhyVQ6hklU0KMCgYEA2+izW09Mw8iI4N+w8hdYKv1oBVtXIajfqyExChW0jRPEvDK4Hwgh/MsjFjCycqasn98TtpdJiUYq6RmFMIlVkh8lKeJnGUFJUw4bbFeFYcVW78TJyCvfq1rh1eZ3NdbiH69tNWaxusJ+2ytF7Dx4eJrXzvEaWV2fusJYhj7UheECgYEAimIHV273jd1B4HRKeLrbNcf8xaWX7NwHROTjItiZmYpxPPXm5Bi1yFFXePWVFxABbSM8xo4GUpVFGBn0+XeUd2LGdKBQesSaF1YoVfpH5Od2Ts8qbK4rGz3z1gKOk5GMhU0ruXHXBMI79GAonSPIPHT5O3iN+anax2lnlUwOu+8CgYEAj/I5LNVX+ocHvM/VaaDAXPPEvcv00Wl84PWDJnKe2kOz7ouKU2fqvmv1LUHBDiPaTL3vK5REKFA0NQSQ4SzbHX1BAdNuZAOMP3Fmtd5CigOJx4lr508rutyCDNBlGjvXLuV7IzjelT4C6o0QoHLPIKN7r57jxP+uOcbMgmuVNmI="
message = "PLWReC8yuVEViE6kpS9tV2ueldyruoCMBLthuPgKDnw="
将消息从 base 64 解码为每种语言的字节数组。之前提供的 Python 签名方法将生成以下签名:
signature_result = "WhM77ROa7Dl0HmO6QdMQjkQ336N2gfi4c2QuLKkIHtxWZQDm7xmLbzbcVxzN3A6hrSdZIS/v5HIpuLe5MovEno6t5QhTyONL4J0evYeO3Ou784qQZORXRlgoOCbkZu25Ty9hBy043HaeUprkOC5f7hcJjRmrel1+izD25NFEDreCnC3paPJqzMXQyUI6gioqdaxth3H0lRW9rAu4pgH/Hgt0/ljd0ch8FJPhfEUNKAV6wltp4h53D9gSlJVBCW2/VFDpzfTHkgkNK+egA2GTc/glWKQ0jm2tEVWniR9nia2bJfNJ/LWrr19ktejex+7g93569HX8w+jp6dAlSIMk9Q=="
以下 Java 代码生成相同的结果:
Signature instance = Signature.getInstance("NONEwithRSA");
instance.initSign(privateKey);
instance.update(Base64.decode(data, Base64.NO_WRAP));
byte[] signature = instance.sign();
String b64Signature = Base64.encodeToString(signature, Base64.NO_WRAP);
是的,您可以将库cryptography
与预哈希一起使用。
这将假设您的数据已经是哈希的输出(因此它不会添加额外的哈希(:
import hashlib
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding, rsa, utils
private_key = rsa.generate_private_key(
public_exponent = 65537,
key_size = 2048,
backend = default_backend()
)
public_key = private_key.public_key()
# Compute the hash here
prehashed_msg = hashlib.sha256(b"A message I want to sign").digest()
# Sign the hash (do not perform any hashing)
signature = private_key.sign(
prehashed_msg,
padding.PSS(
mgf = padding.MGF1(hashes.SHA256()),
salt_length = padding.PSS.MAX_LENGTH),
utils.Prehashed(hashes.SHA256())
)
# Verify
public_key.verify(
signature,
prehashed_msg,
padding.PSS(
mgf = padding.MGF1(hashes.SHA256()),
salt_length = padding.PSS.MAX_LENGTH),
utils.Prehashed(hashes.SHA256())
)