Я пытаюсь реализовать шифрование для настольного приложения. Так как это может потребовать обработки некоторых больших файлов (~ 2 ГБ), я также хотел бы добавить индикатор выполнения, чтобы показать прогресс при шифровании / дешифровании файла. Функция запускается в фоновом режиме, чтобы избежать блокировки пользовательского интерфейса.
Сначала я установил некоторые переменные для буфера и счетчик для прочитанных байтов, а также сколько читать дальше:
int ToRead;
long BRead = 0;
var buffer = new byte[1024];
Функция шифрования работает в следующем порядке: READ -> COMPRESS -> ENCRYPT -> WRITE, но сначала нужно записать байтовый массив EncryptionVars
, содержащий все необходимое для дешифрования (IV, salt, et. c). Код выглядит следующим образом:
using (var aes = new RijndaelManaged())
{
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
using (var transform = aes.CreateEncryptor(key, iv))
using (var dest = new FileStream(destpath, FileMode.Create, FileAccess.Write, FileShare.None))
using (var encrypt = new CryptoStream(dest, transform, CryptoStreamMode.Write))
using (var cmp = new DeflateStream(encrypt, CompressionLevel.Optimal))
using (var source = new FileStream(srcpath, FileMode.Open, FileAccess.Read, FileShare.None))
{
dest.Write(encryptionVars, 0, encryptionVars.Length);
while ((ToRead = source.Read(buffer, 0, buffer.Length)) > 0)
{
if (encryptionBGWorker.CancellationPending)
{
encryptionBGWorker.CancelAsync();
break;
}
else
{
source.CopyTo(cmp, ToRead);
// Update the Progress bar
BRead += ToRead;
int progress = 100* (BRead/FileSize)
encryptionBGWorker.ReportProgress(progress);
}
}
}
}
Расшифровка аналогична, но работает в обратном порядке, READ -> DECRYPT -> DECOMPRESS -> WRITE, но также необходимо пропустить первые X байтов, поскольку они содержат EncryptionVars
, Код выглядит следующим образом:
using (var aes = new RijndaelManaged())
{
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
using (var transform = aes.CreateDecryptor(key, iv))
using (FileStream source = new FileStream(srcpath, FileMode.Open, FileAccess.Read, FileShare.Read))
using (CryptoStream decrypt = new CryptoStream(source, transform, CryptoStreamMode.Read))
using (DeflateStream dcmp = new DeflateStream(decrypt, CompressionMode.Decompress))
using (FileStream dest = new FileStream(destpath, FileMode.Create, FileAccess.Write, FileShare.None))
{
source.Seek(EncryptionVars.Length, SeekOrigin.Current);
while ((ToRead = source.Read(buffer, 0, buffer.Length)) > 0)
{
if (encryptionBGWorker.CancellationPending)
{
encryptionBGWorker.CancelAsync();
break;
}
else
{
dcmp.CopyTo(dest, ToRead);
// Update the Progress bar
BRead += ToRead;
int progress = 100* (BRead/FileSize)
encryptionBGWorker.ReportProgress(progress);
}
}
}
}
Фоновый рабочий код для ProgressChanged просто обновляет значение индикатора выполнения:
private void encryptionBGWorker_DoWork(object sender, DoWorkEventArgs e)
{
if (encryptionBGWorker.CancellationPending == true)
{
e.Cancel = true;
return;
}
else
{
string buttonName = (string)e.Argument;
switch (buttonName)
{
case "Encryption":
CreateEncryptedFile();
break;
case "Decryption":
CreateDecryptedFile();
break;
case null:
return;
}
}
}
private void encryptionBGWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
encryptionPBar.Value = e.ProgressPercentage;
}
private void encryptionBGWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == true)
{
MessageBox.Show("Operation stopped");
encryptButton.Enabled = true;
decryptButton.Enabled = true;
}
else if (e.Error != null)
{
MessageBox.Show("Error: " + e.Error.Message);
encryptButton.Enabled = true;
decryptButton.Enabled = true;
}
else
{
MessageBox.Show("File processing is complete");
encryptButton.Enabled = true;
decryptButton.Enabled = true;
}
}
Фоновый рабочий запускается при нажатии кнопки для Encrypt ( Расшифровка - то же самое с соответствующими изменениями для шифрования / дешифрования), со следующим кодом:
private void encryptBtn_Click(object sender, EventArgs e)
{
encryptBtn.Enabled = false;
if (!bgWorker.IsBusy)
{
bgWorker.RunWorkerAsync("Encryption");
}
}
Проблемы: 1: индикатор выполнения не обновляется ни для одной из функций, 2: расшифровка не выполняется, исключение System.Security.Cryptography.CryptographicException
что, скорее всего, связано с source.Seek(EncryptionVars.Length, SeekOrigin.Current);
неправильным поиском места, где начинаются зашифрованные данные, но я не смог найти решение.
Как я могу исправить эти проблемы, и есть ли лучший / более чистый способ реализации этого кода?