Обновление индикатора выполнения для шифрования / дешифрования AES, запущенного на BackgroundWorker - PullRequest
1 голос
/ 29 апреля 2020

Я пытаюсь реализовать шифрование для настольного приложения. Так как это может потребовать обработки некоторых больших файлов (~ 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); неправильным поиском места, где начинаются зашифрованные данные, но я не смог найти решение.

Как я могу исправить эти проблемы, и есть ли лучший / более чистый способ реализации этого кода?

...