2016-08-15 41 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

是的,这并不是为了正确使用加密,而是为了解决如何避免将整个块读入内存的问题。我已经更新了答案,以删除初始化代码以清楚地说明。 –