我该如何在Python中使用RSA私钥进行加密?

14

在Python中,是否可以使用pycryptodome或其他库使用私钥加密消息?我知道不应该使用私钥加密并使用公钥解密,但我的目的是使用私钥加密,以便接收者可以确信该消息是由真正的作者发送的。除了安全加密之外,我还希望进行某种混淆。我想做一个应用程序,在这个应用程序中,消息是公开的,但只有持有公钥的人才能看到它。

我已经尝试过这样做:

from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64


def generate_keys():
    modulus_lenght = 256 * 4
    private_key = RSA.generate(modulus_lenght, Random.new().read)
    public_key = private_key.publickey()
    return private_key, public_key

def encrypt_private_key(a_message, private_key):
    encryptor = PKCS1_OAEP.new(private_key)
    encrypted_msg = encryptor.encrypt(a_message)
    encoded_encrypted_msg = base64.b64encode(encrypted_msg)
   return encoded_encrypted_msg

def decrypt_public_key(encoded_encrypted_msg, public_key):
    encryptor = PKCS1_OAEP.new(public_key)
    decoded_encrypted_msg = base64.b64decode(encoded_encrypted_msg)
    decoded_decrypted_msg = encryptor.decrypt(decoded_encrypted_msg)
    return decoded_decrypted_msg

private_key, public_key = generate_keys()

message = "Hello world"

encoded = encrypt_private_key(message, private_key)
decoded = decrypt_public_key(encoded, public_key)

print decoded

但它引发了下一个错误:TypeError: This is not a private key(这不是私钥)。


可能是如何在Python中使用RSA私钥加密数据?的重复问题。 - John Luscombe
请看这里:https://dev59.com/lJjga4cB1Zd3GeqPJWO6 - John Luscombe
3个回答

23

简短回答

  • 由于安全原因,您正在使用的代码不允许您这样做
  • 以下是备选代码

详细回答

我对您的问题感到好奇,然后开始尝试编码

过了一会儿,我意识到如果您运行此片段,您将看到它能够正确工作:

#!/usr/bin/env python

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64

def generate_keys():
    modulus_length = 1024

    key = RSA.generate(modulus_length)
    #print (key.exportKey())

    pub_key = key.publickey()
    #print (pub_key.exportKey())

    return key, pub_key

def encrypt_private_key(a_message, private_key):
    encryptor = PKCS1_OAEP.new(private_key)
    encrypted_msg = encryptor.encrypt(a_message)
    print(encrypted_msg)
    encoded_encrypted_msg = base64.b64encode(encrypted_msg)
    print(encoded_encrypted_msg)
    return encoded_encrypted_msg

def decrypt_public_key(encoded_encrypted_msg, public_key):
    encryptor = PKCS1_OAEP.new(public_key)
    decoded_encrypted_msg = base64.b64decode(encoded_encrypted_msg)
    print(decoded_encrypted_msg)
    decoded_decrypted_msg = encryptor.decrypt(decoded_encrypted_msg)
    print(decoded_decrypted_msg)
    #return decoded_decrypted_msg

def main():
  private, public = generate_keys()
  print (private)
  message = b'Hello world'
  encoded = encrypt_private_key(message, public)
  decrypt_public_key(encoded, private)

if __name__== "__main__":
  main()

但如果您现在将最后两行代码(即密钥的作用)更改为:

    encoded = encrypt_private_key(message, private)
    decrypt_public_key(encoded, public)

如果您重新运行程序,您将收到TypeError: No private key错误。引用自这个问题的答案

"正如事实证明的那样,PyCrypto只是试图防止您在此处将一个错误地误解为另一个,OpenSSL或Ruby OpenSSL例如允许您执行公共加密/公共解密和私有加密/私有解密。

[...]

为使结果在实践中可用,需要额外注意一些事项。这就是为什么在PyCrypto中有一个专门的签名包 - 这实际上做了您所描述的事情,但还另外处理了我提到的事情"

根据这个链接进行调整,我得出了以下代码,应该可以解决您的问题:

# RSA helper class for pycrypto
# Copyright (c) Dennis Lee
# Date 21 Mar 2017

# Description:
# Python helper class to perform RSA encryption, decryption, 
# signing, verifying signatures & keys generation

# Dependencies Packages:
# pycrypto 

# Documentation:
# https://www.dlitz.net/software/pycrypto/api/2.6/

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
import rsa

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)

def main():
    msg1 = b"Hello Tony, I am Jarvis!"
    msg2 = b"Hello Toni, I am Jarvis!"

    keysize = 2048

    (public, private) = rsa.newkeys(keysize)

    # https://docs.python.org/3/library/base64.html
    # encodes the bytes-like object s
    # returns bytes
    encrypted = b64encode(rsa.encrypt(msg1, private))
    # decodes the Base64 encoded bytes-like object or ASCII string s
    # returns the decoded bytes
    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.decode('ascii'))
    print("Decrypted: '%s'" % (decrypted))
    print("Signature: " + signature.decode('ascii'))
    print("Verify: %s" % verify)
    rsa.verify(msg2, b64decode(signature), public)

if __name__== "__main__":
    main()

最终备注:

  • 最后两个print的结果是ascii,因为如此处所述,“在base64的情况下,所有字符都是有效的ASCII字符”。
  • 在这种情况下,我们使用相同的密钥 - 私钥 - 既用于加密又用于解密,所以是对称的,但是......
  • 但是 - 如此处所述 - “公钥是公开的 - 它是你愿意共享的东西,因此很容易被传播。在这种情况下,与使用对称密码和共享密钥相比,没有增加价值”,再加上“从概念上讲,使用私钥进行“加密”更有用于签署消息,而使用公钥进行“解密”用于验证消息”。
  • 此答案中表达了完全相同的原则 - “通常[...]我们说用私钥签名,用公钥验证”

非常感谢你,Antonino,这非常有帮助!只有一个问题:在代码中,我需要同时使用私钥进行加密和解密,这是否意味着消息的创建者需要将私钥发送给接收者以便他/她可以解密它?那不会使加密对称吗? - Cesar Cabrera
你好 @CesarCabrera,我刚刚更新了我的回答的最后部分,并添加了新的细节。顺便说一下,当我阅读GitHub的解决方案时,我也有和你一样的疑问,并且已经搜索了原因。希望现在更清楚了。祝你有愉快的一天。 - Antonino
PyCrypto 强制你分享私钥,以使你的程序更加安全! - recolic
你让我的一天变得美好 ;) - Hadi

3

看起来 pycrypto 自2014年以来就没有再进行积极开发,并且在 Python 3.3 上已停止支持。cryptography 现在似乎是标准。

使用 cryptography

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend

password = b'thepassword'

key = rsa.generate_private_key(
    backend=default_backend(),
    public_exponent=65537,
    key_size=2048
)

private_key = key.private_bytes(
    serialization.Encoding.PEM,
    serialization.PrivateFormat.PKCS8,
    serialization.BestAvailableEncryption(password)
)

public_key = key.public_key().public_bytes(
    serialization.Encoding.OpenSSH,
    serialization.PublicFormat.OpenSSH
)

3
然后,您如何使用私钥对消息进行签名,以及如何使用公钥验证该消息? - Albert Hendriks

0

1
加密库在现代计算机上不受支持。 - Jacob Glik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接