2011-06-10 183 views
14

我正在使用PyCrypto实现使用RSA进行文件加密。在Python中使用RSA加密文件

我知道这有点不对,首先因为RSA非常慢,第二,因为PyCrypto RSA只能加密128个字符,所以你必须以128个字符的块来分解文件。

这是到目前为止的代码:

from Crypto.PublicKey import RSA 

file_to_encrypt = open('my_file.ext', 'rb').read() 
pub_key = open('my_pub_key.pem', 'rb').read() 
o = RSA.importKey(pub_key) 

to_join = [] 
step = 0 

while 1: 
    # Read 128 characters at a time. 
    s = file_to_encrypt[step*128:(step+1)*128] 
    if not s: break 
    # Encrypt with RSA and append the result to list. 
    # RSA encryption returns a tuple containing 1 string, so i fetch the string. 
    to_join.append(o.encrypt(s, 0)[0]) 
    step += 1 

# Join the results. 
# I hope the \r\r\r sequence won't appear in the encrypted result, 
# when i explode the string back for decryption. 
encrypted = '\r\r\r'.join(to_join) 
# Write the encrypted file. 
open('encrypted_file.ext', 'wb').write(encrypted) 

所以我的问题是:是否有使用私有/公共密钥加密的文件任何更好的方法?

我听说过Mcrypt和OpenSSL,但我不知道他们是否可以加密文件。

+34

您应该使用像AES这样的对称密码来加密文件,然后使用RSA来加密AES密钥。 – 2011-06-10 17:28:05

+6

@Petey B:我希望我能喜欢那个评论100次。 – vcsjones 2011-06-10 17:28:58

+0

@Petey:谢谢,我知道这件事,但我想要实现像gpg4win这样的东西。 – 2011-06-10 17:31:32

回答

30

公钥密码通常只用于少量数据。它很慢,并且很难正确使用。通常的做法是使用其他方法将不对称问题减少到由共享密钥提供安全性的问题,然后使用公钥加密来保护该共享密钥。例如:

  • 要加密文件,随机生成一个块或流密码(例如AES)的密钥。存储使用此密码加密的数据,并将用公钥加密的密钥与加密的有效载荷一起存储。
  • 要签名文件,请计算加密摘要(例如SHA-256)。用私钥签署文件的摘要并将其存储在文件旁边。

所以这里的加密怎么可以像(警告,未经测试的代码,直接在浏览器中键入)草图:

import os 
from Crypto.Cipher import AES 
from Crypto.PublicKey import RSA 
import Crypto.Util.number 
def encrypt_file(rsa, input, output): 
    # Generate secret key 
    secret_key = os.urandom(16) 
    # Padding (see explanations below) 
    plaintext_length = (Crypto.Util.number.size(rsa.n) - 2)/8 
    padding = '\xff' + os.urandom(16) 
    padding += '\0' * (plaintext_length - len(padding) - len(secret_key)) 
    # Encrypt the secret key with RSA 
    encrypted_secret_key = rsa.encrypt(padding + secret_key, None) 
    # Write out the encrypted secret key, preceded by a length indication 
    output.write(str(len(encrypted_secret_key)) + '\n') 
    output.write(encrypted_secret_key) 
    # Encrypt the file (see below regarding iv) 
    iv = '\x00' * 16 
    aes_engine = AES.new(secret_key, AES.MODE_CBC, iv) 
    output.write(aes_engine.encrypt(input.read())) 

ivinitialization vectorCBCmode of operation。它需要每个消息的每个密钥都是唯一的。通常,它以明文形式与数据一起发送。在这里,由于钥匙只用过一次,所以可以使用已知的IV。

分组密码的API在PEP 272中描述。不幸的是,它只支持一次加密。对于大文件,最好是按块加密块;您可以一次只加密一个块(AES为16个字节),但您需要一个更好的加密库。

请注意,一般情况下,您不应该直接使用RSA加密数据。最明显的问题是攻击者知道公钥并因此可能试图猜测明文(如果攻击者认为明文可能是swordfish,那么攻击者可以用RSA公钥加密swordfish,并将结果与​​输出进行比较的RSA加密)。如果要将文件发送给多个收件人,另一个问题是,如果RSA加密步骤是确定性的,那么攻击者可以知道明文是相同的,因为密文相同。针对这些问题的正常防范是使用padding scheme,其中包括向明文添加一些随机秘密数据;这个数据被称为填充。攻击者则无法猜测随机数据,并且每次加密都会看到不同的结果,因为相同的明文永远不会被加密两次;就合法收件人而言,填充只是可以扔掉的数据。

这里,看起来上述问题不适用于这种情况。但是,使用RSA不受保护可能会产生其他缺陷。特别是,如果公开指数非常小(在PyCrypto使用65537时不是这种情况),或者您为许多不同的收件人加密相同的资料(再次,可能不是这种情况,因为每封邮件都有它自己的密钥),那么simple mathematical calculation would allow the attacker to recover the RSA plaintext。为了避免这种攻击,使用RSA加密的值需要与RSA模数“足够接近”,以便加密操作实际执行模幂运算。我建议的填充可以确保通过生成符合0xff的最高位字节; this is believed to be safe,尽管在现实世界中,您应该使用批准的填充模式(OAEP)。