3

So I've made a method to encrypt a file. However, large files take quite a long time and I know that the time can be greatly reduced by using more threads. I have very little experience on multithreading. I'm using AESManaged and .NET 4.7.2 with a Windows Forms Application. Any help would be greatly appreciated, thanks.

EDIT: I forgot to add the method, I apologize.

private void AES_Encrypt(string inputFile, string password)
{
    try
    {
        byte[] salt = GenerateRandomSalt();

        if (!deleteAfter.Checked)
        {
            FileStream fsCrypt = new FileStream(inputFile + ".encrypted", FileMode.Create);

            byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(password);

            AesManaged AES = new AesManaged();
            AES.KeySize = 256;
            AES.BlockSize = 128;


            var key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000);
            AES.Key = key.GetBytes(AES.KeySize / 8);
            AES.IV = key.GetBytes(AES.BlockSize / 8);



            fsCrypt.Write(salt, 0, salt.Length);

            CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write);

            FileStream fsIn = new FileStream(inputFile, FileMode.Open);

            byte[] buffer = new byte[1048576];
            int read;
            long bufferLength = 0;
            long fileLength = new FileInfo(inputFile).Length;
            float bufferFloat = 0;
            float fileFloat = 0;
            float finalFloat = 0;

            try
            {
                while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0)
                {
                    Application.DoEvents();
                    cs.Write(buffer, 0, read);
                    bufferLength += read;
                    bufferFloat = (float)bufferLength;
                    fileFloat = (float)fileLength;
                    finalFloat = (bufferFloat / fileFloat) * 100;
                    int finalInt = (int)finalFloat;
                    percentage.Value = finalInt;
                    percentageText.Text = finalFloat.ToString() + "%";
                    double kBytes = (double)bufferLength / 1000;
                    double mBytes = (double)bufferLength / 1000000;
                    double gBytes = (double)bufferLength / 1000000000;
                    bytesProcessed.Text = bufferLength.ToString() + " bytes, or " + kBytes.ToString() + " kilobytes, or " + mBytes.ToString() + " megabytyes, or " + gBytes.ToString() + " gigabytes.";
                }

                fsIn.Close();

            }
            catch (Exception ex)
            {
                output.Items.Add("Error: " + ex.Message);
            }
            finally
            {
                cs.Close();
                fsCrypt.Close();
                output.Items.Add("Successfully encrypted file: " + inputFile);
                percentage.Value = 0;
                percentageText.Text = "";
                bytesProcessed.Text = "";
            }
        }
        else
        {
            FileStream fsCrypt = new FileStream(inputFile + ".encrypted", FileMode.Create);

            byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(password);

            AesManaged AES = new AesManaged();
            AES.KeySize = 256;
            AES.BlockSize = 128;


            var key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000);
            AES.Key = key.GetBytes(AES.KeySize / 8);
            AES.IV = key.GetBytes(AES.BlockSize / 8);



            fsCrypt.Write(salt, 0, salt.Length);

            CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write);

            FileStream fsIn = new FileStream(inputFile, FileMode.Open);

            byte[] buffer = new byte[1048576];
            int read;
            long bufferLength = 0;
            long fileLength = new FileInfo(inputFile).Length;
            float bufferFloat = 0;
            float fileFloat = 0;
            float finalFloat = 0;

            try
            {
                while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0)
                {
                    Application.DoEvents();
                    cs.Write(buffer, 0, read);
                    bufferLength += read;
                    bufferFloat = (float)bufferLength;
                    fileFloat = (float)fileLength;
                    finalFloat = (bufferFloat / fileFloat) * 100;
                    int finalInt = (int)finalFloat;
                    percentage.Value = finalInt;
                    percentageText.Text = finalFloat.ToString() + "%";
                    double kBytes = (double)bufferLength / 1000;
                    double mBytes = (double)bufferLength / 1000000;
                    double gBytes = (double)bufferLength / 1000000000;
                    bytesProcessed.Text = bufferLength.ToString() + " bytes, or " + kBytes.ToString() + " kilobytes, or " + mBytes.ToString() + " megabytyes, or " + gBytes.ToString() + " gigabytes.";
                }

                fsIn.Close();

            }
            catch (Exception ex)
            {
                output.Items.Add("Error: " + ex.Message);
            }
            finally
            {
                cs.Close();
                fsCrypt.Close();
                File.Delete(inputFile);
                output.Items.Add("Successfully encrypted file: " + inputFile);
                percentage.Value = 0;
                percentageText.Text = "";
                bytesProcessed.Text = "";
            }
        }

    }
    catch (Exception e)
    {
        output.Items.Add(e.Message);
    }

}
Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • 2
    If a process can be parallelized, multithreading can save time. But not every process can be parallelized. [Here](https://stackoverflow.com/a/41734693/9014097), the CBC-mode (default for [`AESManaged`](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.aesmanaged.mode?view=netcore-3.0)) is discussed with regard to parallelizability (also consider the comments). – Topaco Oct 14 '19 at 08:48
  • The encryption may take a long time because of the I/O involved, and not because the calculation is heavy. – Theodor Zoulias Oct 14 '19 at 10:52
  • Why are you explicitly using `AesManaged` rather than `Aes.Create()`? The managed version is software only; it will not e.g. make use of processor instructions such as AES-NI. – Maarten Bodewes Oct 14 '19 at 15:10
  • 1
    Usually file encryption is **not** sped up by using multiple threats. These actions are very much reliant on the I/O, and *sequential* disk access is much faster than random I/O. HDD's can speed down to 1,5 MiB/s or even lower on random access. That's one of the big reasons for preferring SSD's of course. – Maarten Bodewes Oct 14 '19 at 15:14
  • 1
    You could possibly take `Rfc2898DeriveBytes` out of the loop and let other threads create the keys instead. Let the function return the key and the salt and cache them until they are required. Quite obviously this method is slowing down your encryption algorithm. Or you could use a random data key and encrypt (wrap) that one with the key you calculate in a separate thread. (sorry about the many comments, but they are separate issues and hopefully of help :) ) – Maarten Bodewes Oct 14 '19 at 15:20
  • Ok, thank you guys for the help. About using AESManaged instead of AES.Create(), I used that because the encryption part of this I found online, and they were using RijndaelManaged. I read on microsoft docs that AES was better than Rijndael, so that's why i used it. – IWantHelpWithEverything Oct 14 '19 at 16:55
  • Also, should put everything inside a using statement (including filestream)? – IWantHelpWithEverything Oct 14 '19 at 17:14
  • Yes, if I remember correctly that's about resource management and using `using` for that is recommended. – Maarten Bodewes Oct 14 '19 at 22:16
  • Alright, I'm currently in the process of doing the multithreading work. Thank you everybody for your help. – IWantHelpWithEverything Oct 14 '19 at 22:42

0 Answers0