【问题标题】:Python RSA sign a string with NONEWithRSAPython RSA 使用 NONEWithRSA 对字符串进行签名
【发布时间】:2020-01-03 04:16:53
【问题描述】:

我正在开发一个客户端代码在 Java 中的项目,他们使用NONEWithRSA 算法对字符串进行签名和验证。

我正在使用PyCryptodome 为给定字符串生成符号。 https://pycryptodome.readthedocs.io/en/latest/src/signature/pkcs1_v1_5.html

但我没有发现它使用NONEWithRSA 算法,因此它给出的签名与我的客户在服务器上的期望不同。

python上有没有库支持这个算法?

【问题讨论】:

  • 签名前的散列是签名安全的必要部分。 Java,出于某种原因,我不清楚,它提供了 RSA 签名和其他没有散列的签名。这并不意味着它是安全的。停止使用NONEWithRSA 并更喜欢 SHA 系列。 PyCryptodome 在这个问题上做得很好,因为不要让人们走错路。
  • @kelalaka 它的存在主要是为了让人们实现自己的哈希方式,例如如果我没记错的话,有一个 OpenSSL“哈希”在 PKCS#1 v1.5 填充中结合了 SHA-1 和 MD-5,但没有 DER 编码的哈希指示。
  • 最后这只是明文 RSA。来自standard names document:“RSA 签名算法不使用任何摘要算法,仅使用 PKCS #1 v2.2 中定义的 RSASP1/RSAVP1 原语。”。顺便说一句,这不是完全正确的,显然它还执行 I2OSP 和 OS2IP 功能来编码大整数从和到字节。
  • 所有工具包似乎都遵循 PSS 签名的 PKCS#1 v1.5。我什至在 GitHub 上发现了使用“原始”RSA 签名的功能被故意接受的请求。我猜您需要使用专门的库,例如 ChillKat 或 do the mod exp yourself。 @kelalaka 现代用途当然是 RSA-KEM,尽管那个是用于加密的。较旧的方案,例如 ISO 979 - uh 6 或 7 - 签名将是另一种方案。

标签: python cryptography rsa pycrypto pycryptodome


【解决方案1】:

我最近遇到了同样的问题,试图支持一个 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);

【讨论】:

    【解决方案2】:

    是的,您可以将库 cryptographyPrehashed 一起使用。

    这将假设您的数据已经是哈希的输出(因此它不会添加额外的哈希):

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

    【讨论】:

    • 这不是明确要求的模式 NoneWithRSA。恐怕只是一个 RSA 示例是行不通的。
    猜你喜欢
    • 2023-04-02
    • 1970-01-01
    • 2011-03-11
    • 2011-01-22
    • 1970-01-01
    • 2013-06-06
    • 2019-03-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多