Использование шифрования Rijndael для больших файлов - PullRequest
7 голосов
/ 28 июля 2010

Я нахожусь в ситуации, когда мне нужно надежно зашифровать / расшифровать файл n длины, в идеале с использованием Rijndael, но определенно с 256-битным шифрованием.

Я играл с шифрованием раньше и довольно счастливо зашифровал / расшифровал строки и байтовые массивы. Однако, поскольку я не знаю размер файла (и вполне возможно, что рассматриваемые файлы могут быть довольно большими (~ 2,5 ГБ)), я не могу просто загрузить их в байтовый массив и зашифровать / расшифровать их в один, как у меня раньше.

Итак, после небольшого прочтения в Google, я понял, что ответом было зашифровать и расшифровать файл кусками, и поэтому я создал следующий код:

private static void Enc(string decryptedFileName, string encryptedFileName)
{            
   FileStream fsOutput = File.OpenWrite(encryptedFileName);
   FileStream fsInput = File.OpenRead(decryptedFileName);

   byte[] IVBytes = Encoding.ASCII.GetBytes("1234567890123456");

   fsOutput.Write(BitConverter.GetBytes(fsInput.Length), 0, 8);
   fsOutput.Write(IVBytes, 0, 16);

   RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC};
   ICryptoTransform encryptor = symmetricKey.CreateEncryptor(passwordDB.GetBytes(256 / 8), IVBytes);
   CryptoStream cryptoStream = new CryptoStream(fsOutput, encryptor, CryptoStreamMode.Write);

   for (long i = 0; i < fsInput.Length; i += chunkSize)
   {
      byte[] chunkData = new byte[chunkSize];
      fsInput.Read(chunkData, 0, chunkSize);
      cryptoStream.Write(chunkData, 0, chunkData.Length);
   }
   cryptoStream.Close();
   fsInput.Close();
   fsInput.Dispose();
   cryptoStream.Dispose();
}

private static void Dec(string encryptedFileName, string decryptedFileName)
{
    FileStream fsInput = File.OpenRead(encryptedFileName);
    FileStream fsOutput = File.OpenWrite(decryptedFileName);

    byte[] buffer = new byte[8];
    fsInput.Read(buffer, 0, 8);

    long fileLength = BitConverter.ToInt64(buffer, 0);

    byte[] IVBytes = new byte[16];
    fsInput.Read(IVBytes, 0, 16);

    RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC };
    ICryptoTransform decryptor = symmetricKey.CreateDecryptor(passwordDB.GetBytes(256 / 8), IVBytes);
    CryptoStream cryptoStream = new CryptoStream(fsOutput,decryptor,CryptoStreamMode.Write);

    for (long i = 0; i < fsInput.Length; i += chunkSize)
    {
        byte[] chunkData = new byte[chunkSize];
        fsInput.Read(chunkData, 0, chunkSize);
        cryptoStream.Write(chunkData, 0, chunkData.Length);
    }
    cryptoStream.Close();
    cryptoStream.Dispose();
    fsInput.Close();
    fsInput.Dispose();                      
} 

Все это "хорошо" выглядит для меня, но, к сожалению, выглядит обманчиво!

Шифрование работает без ошибок, но во время дешифрования метод cryptoStream.Close () выдает следующее исключение:

System.Security.Cryptography.CryptographicException Сообщение было обработано недействительный и не может быть удален. "
Source = "mscorlib" StackTrace: в System.Security.Cryptography.RijndaelManagedTransform.DecryptData (Byte [] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte [] & outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast) в System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock (Byte [] inputBuffer, Int32 inputOffset, Int32 inputCount) в System.Security.Cryptography.CryptoStream.FlushFinalBlock () at System.Security.Cryptography.CryptoStream.Dispose (Boolean утилизации) в System.IO.Stream.Close ()

Также представляется, что размер незашифрованного файла не соответствует ожидаемому размеру файла (в диапазоне от около 8 байт до около 60)

Я «исправил» исключение, изменив строки создания объекта RijndaelManaged для включения типа заполнения, как показано ниже:

RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC,Padding=PaddingMode.None };

Но размеры файлов по-прежнему не совпадают, и, как и ожидалось, только что незашифрованный файл - вздор!

Я признаю, что теперь я за пределами своей зоны комфорта с шифрованием / дешифрованием, и это, вероятно, ошибка новичка - но я не могу это обнаружить!

Любая помощь в решении этой проблемы будет принята с благодарностью!

Ответы [ 3 ]

5 голосов
/ 28 июля 2010

Проблема в том, что я использовал:

passwordDB.GetBytes(256 / 8)

в конструкторе для объекта RijndaelManaged в методах Encryption и Decryption, и я не пытался повторно инициализировать объект passwordDB перед попыткой расшифровать.

Решение заключалось в том, чтобы просто включить конструкцию объекта passwordDB в первые строки обоих методов Enc и Dec следующим образом:

        private static void Enc(string decryptedFileName, string encryptedFileName)
        {
            PasswordDeriveBytes passwordDB = new PasswordDeriveBytes("ThisIsMyPassword", Encoding.ASCII.GetBytes("thisIsMysalt!"), "MD5", 2);
            byte[] passwordBytes = passwordDB.GetBytes(128 / 8);

            using (FileStream fsOutput = File.OpenWrite(encryptedFileName))
            {
                using(FileStream fsInput = File.OpenRead(decryptedFileName))
                {
                    byte[] IVBytes = Encoding.ASCII.GetBytes("1234567890123456");

                    fsOutput.Write(BitConverter.GetBytes(fsInput.Length), 0, 8);
                    fsOutput.Write(IVBytes, 0, 16);

                    RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC,Padding=PaddingMode.ANSIX923};
                    ICryptoTransform encryptor = symmetricKey.CreateEncryptor(passwordBytes, IVBytes);                   

                    using (CryptoStream cryptoStream = new CryptoStream(fsOutput, encryptor, CryptoStreamMode.Write))
                    {
                        for (long i = 0; i < fsInput.Length; i += chunkSize)
                        {
                            byte[] chunkData = new byte[chunkSize];
                            int bytesRead = 0;
                            while ((bytesRead = fsInput.Read(chunkData, 0, chunkSize)) > 0)
                            {
                                if (bytesRead != 16)
                                {
                                    for (int x = bytesRead - 1; x < chunkSize; x++)
                                    {
                                        chunkData[x] = 0;
                                    }
                                }
                                cryptoStream.Write(chunkData, 0, chunkSize);
                            }
                        }
                        cryptoStream.FlushFinalBlock();
                    }
                }
            }            
        }

        private static void Dec(string encryptedFileName, string decryptedFileName)
        {
            PasswordDeriveBytes passwordDB = new PasswordDeriveBytes("ThisIsMyPassword", Encoding.ASCII.GetBytes("thisIsMysalt!"), "MD5", 2);
            byte[] passwordBytes = passwordDB.GetBytes(128 / 8);

            using (FileStream fsInput = File.OpenRead(encryptedFileName))
            {
                using (FileStream fsOutput = File.OpenWrite(decryptedFileName))
                {
                    byte[] buffer = new byte[8];
                    fsInput.Read(buffer, 0, 8);

                    long fileLength = BitConverter.ToInt64(buffer, 0);

                    byte[] IVBytes = new byte[16];
                    fsInput.Read(IVBytes, 0, 16);


                    RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC,Padding=PaddingMode.ANSIX923};
                    ICryptoTransform decryptor = symmetricKey.CreateDecryptor(passwordBytes, IVBytes);

                    using (CryptoStream cryptoStream = new CryptoStream(fsOutput, decryptor, CryptoStreamMode.Write))
                    {
                        for (long i = 0; i < fsInput.Length; i += chunkSize)
                        {
                            byte[] chunkData = new byte[chunkSize];
                            int bytesRead = 0;
                            while ((bytesRead = fsInput.Read(chunkData, 0, chunkSize)) > 0)
                            {
                                cryptoStream.Write(chunkData, 0, bytesRead);
                            }
                        }
                    }
                }
            }
        }

Знал, что это должен быть школьникошибка: P

3 голосов
/ 28 июля 2010

Метод Stream.Read возвращает количество байтов, фактически прочитанных из потока.

Вы должны использовать это возвращаемое значение как последний параметр в методе Write на следующей строке.

Мой код будет выглядеть так:

byte[] chunkData = new byte[chunkSize];   
var bytesRead = 0;
while ((bytesRead = fsInput.Read(chunkData, 0, chunkSize)) > 0)
{
    cryptoStream.Write(chunkData, 0, bytesRead);
}
0 голосов
/ 09 августа 2016

Существует класс CryptoStream для шифрования / дешифрования потоков

...