【问题标题】:Signing and verifying data using pycrypto (RSA)使用 pycrypto (RSA) 签名和验证数据
【发布时间】:2010-11-20 11:07:35
【问题描述】:

我正在尝试熟悉 pycrypto 模块,但缺乏清晰的文档使事情变得困难。

首先,我想了解签名和验证数据。有人可以提供一个示例来说明如何编写吗?

【问题讨论】:

  • 警告不要使用 pycrypto! It's unmaintained 自 2013 年左右以来,它至少有两个 严重漏洞 仍未修复今天。请改用pycryptodome 或cryptography.io!

标签: python pycrypto


【解决方案1】:

这是example in the old PyCrypto documentation:的充实版本

确保您使用的是pycryptodome 而不是pycrypto(未维护!)

pycryptodome 可以用pip install pycryptodome安装

import Crypto.Hash.MD5 as MD5
import Crypto.PublicKey.RSA as RSA
import Crypto.PublicKey.DSA as DSA
import Crypto.PublicKey.ElGamal as ElGamal
import Crypto.Util.number as CUN
import os

plaintext = 'The rain in Spain falls mainly on the Plain'

# Here is a hash of the message
hash = MD5.new(plaintext).digest()
print(repr(hash))
# '\xb1./J\xa883\x974\xa4\xac\x1e\x1b!\xc8\x11'

for alg in (RSA, DSA, ElGamal):
    # Generates a fresh public/private key pair
    key = alg.generate(384, os.urandom)

    if alg == DSA:
        K = CUN.getRandomNumber(128, os.urandom)
    elif alg == ElGamal:
        K = CUN.getPrime(128, os.urandom)
        while CUN.GCD(K, key.p - 1) != 1:
            print('K not relatively prime with {n}'.format(n=key.p - 1))
            K = CUN.getPrime(128, os.urandom)
        # print('GCD({K},{n})=1'.format(K=K,n=key.p-1))
    else:
        K = ''

    # You sign the hash
    signature = key.sign(hash, K)
    print(len(signature), alg.__name__)
    # (1, 'Crypto.PublicKey.RSA')
    # (2, 'Crypto.PublicKey.DSA')
    # (2, 'Crypto.PublicKey.ElGamal')

    # You share pubkey with Friend
    pubkey = key.publickey()

    # You send message (plaintext) and signature to Friend.
    # Friend knows how to compute hash.
    # Friend verifies the message came from you this way:
    assert pubkey.verify(hash, signature)

    # A different hash should not pass the test.
    assert not pubkey.verify(hash[:-1], signature)

【讨论】:

  • @Noah McIlraith:对于 RSA,不使用第二个参数 K。对于 ElGamal 和 DSA,需要提供长字符串或随机数据 K。详细信息可以在标题为“ElGamal 和 DSA 算法”的部分下的 dlitz.net/software/pycrypto/doc/… 中找到。
  • @Noah McIlraith:对于签名的便携式存储,我认为纯文本字符串是最简单的。您可以使用 json.dumps(signature) 将其保存为 JSON 字符串,然后使用 json.loads 将其加载回(作为元组)。
  • 签名元组是否会包含多个项目?
  • @Noah McIlraith:对于 RSA,签名元组的长度为 1,但对于 DSA 和 ElGamal,它的长度为 2。我编辑了我的答案以显示如何使用 DSA 和 ElGamal。
  • 投反对票,因为帮助(有关 RSA 密钥类型,请参阅 help(key.sign))说“注意:此函数执行普通的原始 RSA 解密(教科书)。在实际应用中,您总是需要使用适当的加密填充,并且您不应该直接使用这种方法对数据进行签名。否则可能会导致安全漏洞。建议改用模块Crypto.Signature.PKCS1_PSS或Crypto.Signature.PKCS1_v1_5。使用加密技术很难做到正确,违背图书馆的建议似乎真的是个坏主意......
【解决方案2】:

下面是我创建的helper class,用于执行所有必要的 RSA 功能(加密、解密、签名、验证签名和生成新密钥)

rsa.py

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA512, SHA384, SHA256, SHA, MD5
from Crypto import Random
from base64 import b64encode, b64decode

hash = "SHA-256"

def newkeys(keysize):
    random_generator = Random.new().read
    key = RSA.generate(keysize, random_generator)
    private, public = key, key.publickey()
    return public, private

def importKey(externKey):
    return RSA.importKey(externKey)

def getpublickey(priv_key):
    return priv_key.publickey()

def encrypt(message, pub_key):
    #RSA encryption protocol according to PKCS#1 OAEP
    cipher = PKCS1_OAEP.new(pub_key)
    return cipher.encrypt(message)

def decrypt(ciphertext, priv_key):
    #RSA encryption protocol according to PKCS#1 OAEP
    cipher = PKCS1_OAEP.new(priv_key)
    return cipher.decrypt(ciphertext)

def sign(message, priv_key, hashAlg="SHA-256"):
    global hash
    hash = hashAlg
    signer = PKCS1_v1_5.new(priv_key)
    if (hash == "SHA-512"):
        digest = SHA512.new()
    elif (hash == "SHA-384"):
        digest = SHA384.new()
    elif (hash == "SHA-256"):
        digest = SHA256.new()
    elif (hash == "SHA-1"):
        digest = SHA.new()
    else:
        digest = MD5.new()
    digest.update(message)
    return signer.sign(digest)

def verify(message, signature, pub_key):
    signer = PKCS1_v1_5.new(pub_key)
    if (hash == "SHA-512"):
        digest = SHA512.new()
    elif (hash == "SHA-384"):
        digest = SHA384.new()
    elif (hash == "SHA-256"):
        digest = SHA256.new()
    elif (hash == "SHA-1"):
        digest = SHA.new()
    else:
        digest = MD5.new()
    digest.update(message)
    return signer.verify(digest, signature)

示例用法

import rsa
from base64 import b64encode, b64decode

msg1 = "Hello Tony, I am Jarvis!"
msg2 = "Hello Toni, I am Jarvis!"
keysize = 2048
(public, private) = rsa.newkeys(keysize)
encrypted = b64encode(rsa.encrypt(msg1, public))
decrypted = rsa.decrypt(b64decode(encrypted), private)
signature = b64encode(rsa.sign(msg1, private, "SHA-512"))
verify = rsa.verify(msg1, b64decode(signature), public)

print(private.exportKey('PEM'))
print(public.exportKey('PEM'))
print("Encrypted: " + encrypted)
print("Decrypted: '%s'" % decrypted)
print("Signature: " + signature)
print("Verify: %s" % verify)
rsa.verify(msg2, b64decode(signature), public)

【讨论】:

  • 我觉得这很混乱。 rsa.encrypt 的签名是(message, pub_key),但示例用法中的调用是rsa.encrypt(msg1, private),这使它看起来想要一个公钥,但实际上得到了一个私钥。此外,rsa.newkeys() 返回两个值,其中一个是从另一个派生的(特别是 (x, x.public_key())),这似乎与 (public, private) 的“普通英语”解释完全不同
  • 感谢您指出示例用法中的错误(现已更新)。为了进行加密,您需要致电rsa.encrypt(msg1, public)。对于 RSA,您需要公钥用于加密和验证,私钥需要用于解密和签名。此外,您始终可以从private key 获得public key,但反之则不可能
【解决方案3】:

根据以下文档:

https://www.dlitz.net/software/pycrypto/api/current/Crypto.PublicKey.RSA._RSAobj-class.html

您不应在实际代码中使用 PyCrypto 中的 Crypto.PublicKey.RSA.sign 函数:

注意:此函数执行普通的原始 RSA 解密(教科书)。在实际应用中,您总是需要使用适当的加密填充,并且您不应该直接使用这种方法对数据进行签名。不这样做可能会导致安全漏洞。建议改用模块 Crypto.Signature.PKCS1_PSS 或 Crypto.Signature.PKCS1_v1_5。

我最终使用了实现 PKCS1_v1_5 的 RSA module。 documentation for signing 非常直接。其他人have recommended use M2Crypto.

【讨论】:

    猜你喜欢
    • 2023-03-22
    • 2012-01-16
    • 2022-01-01
    • 1970-01-01
    • 2014-03-10
    • 2019-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多