2011-06-10 205 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)。