简体   繁体   English

Python RSA 使用 NONEWithRSA 对字符串进行签名

[英]Python RSA sign a string with NONEWithRSA

I am working on a project in which client side code is in Java and they are using NONEWithRSA algorithm to sign and verify a string.我正在开发一个客户端代码在 Java 中的项目,他们使用NONEWithRSA算法来签名和验证字符串。

I am using PyCryptodome to generate sign for a given string.我正在使用PyCryptodome为给定的字符串生成符号。 https://pycryptodome.readthedocs.io/en/latest/src/signature/pkcs1_v1_5.html https://pycryptodome.readthedocs.io/en/latest/src/signature/pkcs1_v1_5.html

But I have not found it uses NONEWithRSA algorithm so its giving different signature than what my clients expects on the server.但我还没有发现它使用NONEWithRSA算法,因此它给出的签名与我的客户在服务器上期望的签名不同。

Do any library on python supports this algorithm? python 上的任何库都支持这个算法吗?

I recently ran into this same issue trying to support signature generation for an Android app that expects the equivalent of "NONEwithRSA."我最近遇到了同样的问题,试图支持 Android 应用程序的签名生成,该应用程序期望等效于“NONEwithRSA”。 In my case, it was actually a situation where the application is using a hash, but it is obviously missing the ASN.1 code prefix.就我而言,实际上是应用程序使用哈希的情况,但显然缺少 ASN.1 代码前缀。

It turns out that the Java RSA cipher works like magic behind the scenes and automatically uses signature block type PKCS#1 padding (so that each message/key combination produces the same signature).事实证明,Java RSA 密码在幕后就像魔术一样工作,并自动使用签名块类型 PKCS#1 填充(以便每个消息/密钥组合产生相同的签名)。

This caused me problems because I initially thought I could just encrypt the raw hash using the private key in Python, and then it would be compatible with Java.这给我带来了问题,因为我最初认为我可以使用 Python 中的私钥对原始哈希进行加密,然后它将与 Java 兼容。 However, encrypting with the private key in PyCryptodome uses encryption block type PKCS#1 which causes a v15 unpad exception when verifying with Java.但是,在 PyCryptodome 中使用私钥加密使用加密块类型 PKCS#1,这会在使用 Java 验证时导致 v15 unpad 异常。

The simplest solution I found was to use the rsa package ( https://pypi.org/project/rsa/ ) and directly access some of its internal methods.我找到的最简单的解决方案是使用 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()

Essentially, this code originates in the rsa/pkcs1.py module's "sign_hash" method.本质上,此代码源自 rsa/pkcs1.py 模块的“sign_hash”方法。 We just do not add the ASN.1 code.我们只是不添加 ASN.1 代码。

This solution is admittedly fragile, but it should give you a good working reference to make something more robust.这个解决方案无可否认是脆弱的,但它应该给你一个很好的工作参考,使一些更健壮的东西。

Validation验证

This Python signature generation procedure produces an identical signature to the Java "NONEwithRSA" algorithm for the same key/input.此 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="

Decode the message from base 64 to a byte array in each language.将消息从 base 64 解码为每种语言的字节数组。 The previously supplied Python signing method will produce the following signature:之前提供的 Python 签名方法将生成以下签名:

signature_result = "WhM77ROa7Dl0HmO6QdMQjkQ336N2gfi4c2QuLKkIHtxWZQDm7xmLbzbcVxzN3A6hrSdZIS/v5HIpuLe5MovEno6t5QhTyONL4J0evYeO3Ou784qQZORXRlgoOCbkZu25Ty9hBy043HaeUprkOC5f7hcJjRmrel1+izD25NFEDreCnC3paPJqzMXQyUI6gioqdaxth3H0lRW9rAu4pgH/Hgt0/ljd0ch8FJPhfEUNKAV6wltp4h53D9gSlJVBCW2/VFDpzfTHkgkNK+egA2GTc/glWKQ0jm2tEVWniR9nia2bJfNJ/LWrr19ktejex+7g93569HX8w+jp6dAlSIMk9Q=="

The following Java code produces the same result:以下 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);

Yes, you can use the library cryptography with Prehashed .是的,您可以将库cryptographyPrehashed一起使用

This will assume your data is already the output of a hash (so it will not add extra hashing):这将假设您的数据已经是散列的输出(因此它不会添加额外的散列):

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())
)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM