在Python中使用RSA加密文件

Cri*_*tin 13 python encryption public-key-encryption

我正在使用PyCrypto使用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)
Run Code Online (Sandbox Code Playgroud)

所以我的问题是:有没有更好的方法在文件上使用私钥/公钥加密?

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

Gil*_*il' 31

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

  • 要加密文件,请随机生成块或密码密码(例如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()))
Run Code Online (Sandbox Code Playgroud)

iv是一个初始化矢量用于CBC 操作模式.每条消息的每个密钥都必须是唯一的.通常情况下,它会以明文形式与数据一起发送.在这里,由于密钥只使用一次,因此您可以使用已知的IV.

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

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

在这里,似乎上述问题不适用于这种情况.但是,使用RSA不受保护可能会产生其他弱点.特别是,如果公共指数非常小(这里不是PyCrypto使用65537的情况)或者你为许多不同的收件人加密相同的材料(再次,可能不是这里的情况,因为每条消息都有自己的密钥),那么简单的数学计算将允许攻击者恢复RSA明文.为了避免这种攻击,使用RSA加密的值需要与RSA模数"足够接近",以便加密操作实际执行模幂运算.我建议的填充确保通过使最高位的字节适合0xff; 这被认为是安全的,虽然在现实世界中你应该使用批准的填充模式(OAEP).