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

IV должен быть * уникальным * для режима CFB. Не используйте статический IV, потому что это делает шифр детерминированным и, следовательно, не семантически безопасным. Злоумышленник, который наблюдает за зашифрованным текстом, может определить, когда тот же префикс сообщения был отправлен раньше. И что еще хуже с потоковым режимом, таким как CFB, злоумышленник может даже вывести сообщение, если IV повторно используется, даже когда сообщения разные. IV не секрет, поэтому вы можете отправить его вместе с зашифрованным текстом. Обычно это просто добавляется к зашифрованному тексту и разрезается перед расшифровкой. –

ответ

2

OK, первая ваша актуальная проблема.

Ваше чтение всего файла в память, шифрование его, а затем запись его на диск. Большой байтовый массив может не вписываться в память (даже если у вас достаточно «ОЗУ»).

Вы должны вместо этого читать кусок данных за раз. Размер этого фрагмента, вероятно, будет кратным 16 байтам (размер блока AES), напишите его на использование CryptoStream, и когда вы достигнете конца потока, закройте CryptoStream, чтобы он применял любые дополнения.

ОК, другие вещи.

Во-первых, не используйте RijndaelManaged. Используйте AesManaged. Rijndael был именем AES, прежде чем он был стандартизован как 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

Да, это не означает, что нужно правильно использовать шифрование, чтобы решить вопрос о том, как избежать чтения всего блока в память. Я обновил ответ, чтобы удалить код инициализации, чтобы сделать это понятным. –

Смежные вопросы