Редактировать: resolved - проблема была функцией дешифрования. Исправленную функцию дешифрования можно найти в ответе ниже.C# Rijndael Управляемое шифрование и дешифрование иногда не удается
Я провел последний день на работе, пытаясь выяснить, где я ошибся с этой реализацией AES. Мы должны иметь возможность шифровать/расшифровывать большие файлы, чтобы я использовал потоки файлов и записывал на диск каждый фрагмент. AES является требованием, как это должно работать с существующей системой, написанной в Java, которая использует AES/CBC/PKCS5Padding (который, насколько я знаю, что эквивалентно pkcs7/CBC)
Эта реализация работает наиболее файлов , Однако есть несколько файлов, в которых отсутствуют последние 5 байтов необработанных данных. Файл будет дешифровать без ошибок, однако хэш не соответствует, а недостающие байты представляют собой комбинацию реальных данных и завершающих нулей.
Следует отметить, что эти потоки gzip до и после шифрования (код внизу).
Шифрование
public static void AesEncrypt(byte[] keyBytes, byte[] ivBytes, Stream dataStream, FileStream outStream)
{
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
symmetricKey.BlockSize = 128;
symmetricKey.KeySize = 256;
const int chunkSize = 4096;//1024 * 1024 * 10;
using (ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, ivBytes))
{
using (CryptoStream cryptoStream = new CryptoStream(outStream, encryptor, CryptoStreamMode.Write))
{
while (dataStream.Position != dataStream.Length)
{
long remainingBytes = dataStream.Length - dataStream.Position;
var buffer = chunkSize > remainingBytes ? new byte[(int)remainingBytes] : new byte[chunkSize];
dataStream.Read(buffer, 0, buffer.Length);
cryptoStream.Write(buffer, 0, buffer.Length);
cryptoStream.Flush();
}
cryptoStream.FlushFinalBlock();
}
}
symmetricKey.Clear();
}
дешифрование
public static void AesDecrypt(byte[] keyBytes, byte[] ivBytes, Stream dataStream, FileStream outStream)
{
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
symmetricKey.BlockSize = 128;
symmetricKey.KeySize = keyBytes.Length == 32 ? 256 : 128;
const int chunkSize = 4096;
using (ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, ivBytes))
{
using (CryptoStream cryptoStream = new CryptoStream(dataStream, decryptor, CryptoStreamMode.Read))
{
while (dataStream.Position != dataStream.Length)
{
long remainingBytes = dataStream.Length - dataStream.Position;
var buffer = chunkSize > remainingBytes
? new byte[(int) remainingBytes]
: new byte[chunkSize];
cryptoStream.Read(buffer, 0, buffer.Length);
outStream.Write(buffer, 0, buffer.Length);
outStream.Flush();
}
//cryptoStream.FlushFinalBlock(); // Was throwing an exception
}
}
symmetricKey.Clear();
}
Compression (До шифрования)
public static void StreamCompress(Stream dataStream, FileStream outStream)
{
dataStream.Position = 0;
outStream.Position = 0;
const int chunkSize = 4096;
using (GZipStream gzs = new GZipStream(outStream, CompressionMode.Compress))
{
while (dataStream.Position != dataStream.Length)
{
long remainingBytes = dataStream.Length - dataStream.Position;
var buffer = chunkSize > remainingBytes ? new byte[(int)remainingBytes] : new byte[chunkSize];
dataStream.Read(buffer, 0, buffer.Length);
gzs.Write(buffer, 0, buffer.Length);
gzs.Flush();
}
}
}
декомпрессия (После расшифровки)
public static void StreamDecompress(Stream dataStream, FileStream outStream)
{
byte[] buffer = new byte[4096];
dataStream.Position = 0;
using (GZipStream gzs = new GZipStream(dataStream, CompressionMode.Decompress))
{
for (int r = -1; r != 0; r = gzs.Read(buffer, 0, buffer.Length))
if (r > 0) outStream.Write(buffer, 0, r);
}
}
Я прошел через некоторые другие вопросы, но не могу понять, почему это происходит только на некоторых файлах. Недопустимый размер файла - 46 854 144 байта. Это, кажется, отлично работает с большими и меньшими файлами.
Любая помощь была бы принята с благодарностью.
'Stream.Read()' возвращает 'int ', указывающий количество прочитанных байтов. Хотя это может не решить вашу проблему, я настоятельно рекомендую использовать это возвращаемое значение, чтобы определить, сколько байтов было * на самом деле * прочитано и поэтому должно быть записано на ваш вывод, а не предполагать, что буфер всегда был полностью заполнен. Например: «var bytesRead = inStream.Read (buffer, 0, buffer.Length); outStream.Write (буфер, 0, bytesRead); '. – Iridium
Или просто используйте 'Stream.CopyTo' – CodesInChaos
Спасибо за ответ! Я добавил переменную 'bytesRead' и использую ее во всех, кроме метода декомпрессии, который уже делал это. К сожалению, это не решило проблему - я все еще смотрю на это :( – newuser