Я нахожусь в ситуации, где я должен зашифровать / дешифруют файл n длины надежно, идеально с помощью Rijndael, но определенно при шифровании на 256 битов.
Я играл вокруг с шифрованием прежде и шифровал/дешифровал строки и массивы байтов вполне счастливо. Однако, потому что я не знаю размера файла (и очень выполнимо, что рассматриваемые файлы могли быть довольно большими (~2.5gb), я не могу только загрузить их в массив байтов и enc/decrypt их одним прыжком, как я имею прежде.
Так, после небольшого количества чтения вокруг на 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. Близко ()" метод выдает следующее исключение:
Система. Безопасность. Криптография. CryptographicException был необработанным сообщением = "Дополнение, недопустимо и не может быть удален".
Источник = "mscorlib" StackTrace: в System. Безопасность. Криптография. RijndaelManagedTransform. DecryptData (Байт [] inputBuffer, Int32 inputOffset, Int32 inputCount, Байт [] и outputBuffer, Int32 outputOffset, PaddingMode paddingMode, булевы квартиры) в System. Безопасность. Криптография. RijndaelManagedTransform. TransformFinalBlock (Байт [] inputBuffer, Int32 inputOffset, Int32 inputCount) в System. Безопасность. Криптография. CryptoStream. FlushFinalBlock () в Системе. Безопасность. Криптография. CryptoStream. Расположите (расположение булевской переменной) в System. IO.Stream. Близко ()
Также кажется, что незашифрованный размер файла не соответствует ожидаемому размеру файла (в пределах от приблизительно 8 байтов приблизительно к 60)
Я "зафиксировал" исключение путем изменения строк создания объекта RijndaelManaged для включения дополнительного типа, как указано ниже:
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC,Padding=PaddingMode.None };
Но размеры файла все еще не совпадают и, очевидно, недавно незашифрованный файл является вздором!
Я признаю, что я теперь за пределами моей зоны комфорта с шифрованием/дешифрованием, и это - вероятно, ошибка новичка - но я не могу определить его!
Любая справка при разрешении этого значительно ценилась бы!
Проблема в том, что я использовал:
passwordDB.GetBytes(256 / 8)
внутри конструктора для объекта RijndaelManaged в обоих методах шифрования и расшифровки, и я не инициализировал заново объект 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
Метод Stream.Read возвращает количество байтов, фактически считываемых из потока.
Вы должны использовать это возвращаемое значение в качестве последнего параметра в методе Write в следующей строке.
Мой код будет выглядеть так:
byte[] chunkData = new byte[chunkSize];
var bytesRead = 0;
while ((bytesRead = fsInput.Read(chunkData, 0, chunkSize)) > 0)
{
cryptoStream.Write(chunkData, 0, bytesRead);
}