2016-08-15 78 views
0

Rijndael的文件加密問題Rijndael的文件加密問題

我要加密使用Rijndael算法大型文件,但我走出內存異常錯誤的。任何想法? 這裏是我的代碼

 public void Rijndael_EncryptFile(string password, string filepath, int opt) 
    { 
     try 
     { 
      byte[] keyBytes; 
      keyBytes = Encoding.Unicode.GetBytes(password); 
      Rfc2898DeriveBytes derivedKey = new Rfc2898DeriveBytes(password, keyBytes); 
      RijndaelManaged rijndaelCSP = new RijndaelManaged(); 
      rijndaelCSP.BlockSize = opt; //128 256 
      rijndaelCSP.KeySize = opt; //128 256 
      rijndaelCSP.Key = derivedKey.GetBytes(rijndaelCSP.KeySize/8); 
      rijndaelCSP.IV = derivedKey.GetBytes(rijndaelCSP.BlockSize/8); 
      rijndaelCSP.Mode = CipherMode.CFB; 
      rijndaelCSP.Padding = PaddingMode.Zeros; 
      ICryptoTransform encryptor = rijndaelCSP.CreateEncryptor(); 
      FileStream inputFileStream = new FileStream(filepath, FileMode.Open, FileAccess.Read); 
      byte[] inputFileData = new byte[(int)inputFileStream.Length]; 
      inputFileStream.Read(inputFileData, 0, (int)inputFileStream.Length); 
      FileStream outputFileStream = new FileStream(filepath + ".enc", FileMode.Create, FileAccess.Write); 
      CryptoStream encryptStream = new CryptoStream(outputFileStream, encryptor, CryptoStreamMode.Write); 
      encryptStream.Write(inputFileData, 0, (int)inputFileStream.Length); 
      encryptStream.FlushFinalBlock(); 
      rijndaelCSP.Clear(); 
      encryptStream.Close(); 
      inputFileStream.Close(); 
      outputFileStream.Close(); 
     } 
    } 
+1

對於CFB模式,IV必須*唯一*。不要使用靜態IV,因爲這會使密碼具有確定性,因此在語義上不安全。觀察密文的攻擊者可以確定何時之前發送了相同的消息前綴。對於像CFB這樣的流式傳輸模式,甚至更糟糕的是,即使消息不同時,攻擊者甚至能夠推斷出消息IV是否被重用。 IV不是祕密的,所以你可以把它和密文一起發送。通常,它只是在密文前面加上,然後在解密之前切掉。 –

回答

2

好的,首先是您的實際問題。

您將整個文件讀入內存,對其進行加密,然後將其寫入磁盤。一個大的字節數組可能不適合內存(即使你有足夠的「RAM」)。

您應該一次讀取大塊數據。這個塊的大小可能是16個字節(AES塊的大小)的倍數,將它寫入使用CryptoStream,並且當您到達流的末尾時,關閉CryptoStream,以便應用任何填充。

好吧,其他的事情。首先,請不要使用RijndaelManaged。使用AesManaged。在AES被標準化之前,Rijndael就是AES的名字。 AES與Rijndael相同,只是AES不允許128以外的塊大小,所以不要更改塊大小。唯一需要使用Rijndael的是當你需要128位以外的塊大小以兼容現有系統時。

接下來,對於CFB模式,您的初始化向量應爲唯一的。 IV不是祕密,您可以將它與密文一起以純文本形式存儲。不要從密碼中派生出來。至關重要的是,IV決不能被重複使用。它也不需要是隨機的。它可能是一個簡單的遞增計數器。請注意,對於其他模式的AES,如CBC,IV應該是隨機的。

2

您不需要將整個文件讀入內存。最好的方法是每次從輸入中讀取一塊數據並將該塊寫入輸出。

類似下面的,雖然我沒有測試過以下:

byte[] keyBytes; 
keyBytes = Encoding.Unicode.GetBytes(password); 
Rfc2898DeriveBytes derivedKey = new Rfc2898DeriveBytes(password, keyBytes); 
RijndaelManaged rijndaelCSP = new RijndaelManaged(); 
// setup key parameters, i.e. IV, etc 
ICryptoTransform encryptor = rijndaelCSP.CreateEncryptor(); 
using (FileStream inputFileStream = new FileStream(filepath, FileMode.Open, FileAccess.Read)) 
using (FileStream outputFileStream = new FileStream(filepath + ".enc", FileMode.Create, FileAccess.Write)) 
using (CryptoStream encryptStream = new CryptoStream(outputFileStream, encryptor, CryptoStreamMode.Write)) 
{ 
    byte[] buffer = new byte[bufSize]; 
    int readSize = 0; 
    while ((readSize = inputFileStream.Read(buffer, 0, buffer.Length)) > 0) 
    { 
     encryptStream.Write(buffer, 0, readSize); 
    } 
    encryptStream.FlushFinalBlock(); 
    rijndaelCSP.Clear(); 
} 

UPDATE:刪除初始化代碼(從原來的問題複製),因爲它是不安全的。

+0

我猜初始化部分是爲了演示目的。這並不安全。您需要使用隨機鹽來獲得密鑰推導和隨機IV。兩者都不應該是祕密的,所以它們可以寫在密文前,並在解密期間使用,通過在實際啓動解密之前先讀取它們。 –

+0

是的,這並不是爲了正確使用加密,而是爲了解決如何避免將整個塊讀入內存的問題。我已經更新了答案,以刪除初始化代碼以清楚地說明。 –