Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multithreading encryption in C#

I am new to encryption and am encrypting files using the following method:

private static void encryptFile(string filePath, byte[] password, byte[] salt)
{
    Rfc2898DeriveBytes rdb = new Rfc2898DeriveBytes(password, salt, 1000);
    AesManaged algorithm = new AesManaged();

    byte[] rgbKey = rdb.GetBytes(algorithm.KeySize / 8);
    byte[] rgbIV = rdb.GetBytes(algorithm.BlockSize / 8);
    GCHandle keyHandle = GCHandle.Alloc(rgbKey, GCHandleType.Pinned);
    GCHandle IVHandle = GCHandle.Alloc(rgbIV, GCHandleType.Pinned);

    ICryptoTransform cryptoAlgorithm = algorithm.CreateEncryptor(rgbKey, rgbIV);

    using (FileStream readStream = File.Open(filePath, FileMode.Open))
    {
        using (FileStream writeStream = new FileStream(filePath + ".enc", FileMode.Create, FileAccess.Write))
        {
            using (CryptoStream cryptoStream = new CryptoStream(writeStream, cryptoAlgorithm, CryptoStreamMode.Write))
            {
                while (readStream.Position < readStream.Length)
                {
                    byte[] buffer = new byte[4096];
                    int amountRead = readStream.Read(buffer, 0, buffer.Length);
                    cryptoStream.Write(buffer, 0, amountRead);
                }
                cryptoStream.Flush();
            }
        }
    }

    UtilityMethods.destroyBytes(rgbKey);
    UtilityMethods.destroyBytes(rgbIV);
    keyHandle.Free();
    IVHandle.Free();
}

What I want to do is multithread the process for faster encryption. Using a single thread, this has taken over 5 min to encrypt a ~3GB file. I'm looking to be able to do that encryption in under 1 min if possible (under 30 seconds would be fantastic, but I think I might be stretching).

I believe the answer is to create multiple streams (although I'm not sure), assigning each stream a chunk of the file to encrypt, but I'm not sure how to "break the file apart" to assign a chunk to each stream, or "put the file back together" after each part has gone through the stream that it was assigned to. Could someone point me in the right direction?

Thanks very much!

P.S. I've viewed this (Rijndael algorithm and CryptoStream: is it possible to encrypt / decrypt multithreaded?), but I don't understand the answer (ECB, CBC?). If the answer to my question lies there, could you possibly provide some sample code to get me going in the right direction?

Thanks again!

like image 242
Brian Avatar asked Oct 17 '22 18:10

Brian


1 Answers

CBC (Cipher Block Chaining) is the default block mode for AesManaged.

You cannot encrypt different blocks in parallel with AES if you are using CBC because the side effect of encrypting block 1, for example, is to set a new IV for block 2. This is called "IV feedback." If you are running in parallel, this won't work. You will need to pick a different cipher block mode.

You could use ECB mode (which doesn't have IV feedback), but if you do, blocks that repeat themselves in plaintext will also repeat themselves in ciphertext, which opens you up to certain types of attacks.

Best would be to use CTR, where IV is based on a counter, but alas .NET does not seem to support it.

I would suggest you break the single file into several "virtual" files, each with its own cipherstream. Use CBC and populate the IV with bytes from the RNGCryptoServiceProvider. Seed the RNGCryptoServiceProvider with a value derived from the index of the virtual file within the physical file. That way you have an IV that varies from block to block and repeated plaintext won't show up in the ciphertext. Repeat the process when decrypting.

See also this question.

I would suggest you post your solution on security.stackexchange.com for review when you are ready.

like image 107
John Wu Avatar answered Nov 02 '22 14:11

John Wu