Search code examples
c#encryptionaes

CryptographicException: "Padding is invalid and cannot be removed" when encrypting/decrypting large files using AesCryptoServiceProvider


I am trying to encrypt/decrypt files with AES in C#. My code works until I try it on large files (I'm testing on a 4GB file). When I try the code on a 4gb file it throws the error "CryptographicException: Padding is invalid and cannot be removed." during decrypt.

Here is my code:

public static void Encrypt(FileStream fileWriteStream,
    FileStream fileReadStream,
    byte[] key,
    byte[] iv)
{
    const int megabyte = 1024 * 1024;

    var aes = new AesCryptoServiceProvider();
    aes.Key = key;
    aes.IV = iv;
    aes.Mode = CipherMode.CBC;
    aes.Padding = PaddingMode.PKCS7;


    using (ICryptoTransform encrypt = aes.CreateEncryptor())
    {
        using (CryptoStream cs = new CryptoStream(fileWriteStream, encrypt, CryptoStreamMode.Write))
        {

            using (fileReadStream)
            {
                var bytesToRead = fileReadStream.Length;
                var chunksToRead = megabyte < fileReadStream.Length ? (int)megabyte : (int)fileReadStream.Length;

                byte[] buffer = new byte[chunksToRead];

                var pos = 0;
                long totalBytesRead = 0;

                fileReadStream.Seek(0, SeekOrigin.Begin);

                int bytesRead = fileReadStream.Read(buffer, 0, chunksToRead);
                var totalBytesReadInChunk = (long)bytesRead;
                while (bytesRead > 0)
                {
                    cs.Write(buffer, 0, chunksToRead);

                    var bytesLeft = fileReadStream.Length - totalBytesReadInChunk;
                    chunksToRead = bytesLeft > megabyte ? megabyte : (int)bytesLeft;

                    bytesRead = fileReadStream.Read(buffer, 0, chunksToRead);
                    totalBytesReadInChunk += bytesRead;
                }
                totalBytesRead += totalBytesReadInChunk;

                cs.FlushFinalBlock();
            }

        }
    }
}


public static void DecryptToBytes(Stream cipherBytes,
    FileStream outputStream,
    byte[] key,
    byte[] iv)
{
    const int megabyte = 1024 * 1024;


    byte[] plainBytes;
    // Instantiate a new Aes object to perform string symmetric encryption
    Aes encryptor = Aes.Create();
    encryptor.Padding = PaddingMode.PKCS7;
    encryptor.Mode = CipherMode.CBC;

    // Set key and IV
    encryptor.IV = iv;

    byte[] aesKey = new byte[32];
    Array.Copy(key, 0, aesKey, 0, 32);
    encryptor.Key = aesKey;


    //// Instantiate a new MemoryStream object to contain the encrypted bytes
    //MemoryStream memoryStream = new MemoryStream();

    // Instantiate a new encryptor from our Aes object
    ICryptoTransform aesDecryptor = encryptor.CreateDecryptor();

    // Instantiate a new CryptoStream object to process the data and write it to the 
    // memory stream
    CryptoStream cryptoStream = new CryptoStream(outputStream, aesDecryptor, CryptoStreamMode.Write);


    var bytesToRead = megabyte < cipherBytes.Length ? (int)megabyte : (int)cipherBytes.Length;

    byte[] buffer = new byte[bytesToRead];

    cipherBytes.Seek(0, SeekOrigin.Begin);
    int bytesRead = cipherBytes.Read(buffer, 0, bytesToRead);
    var totalBytesRead = bytesRead;
    while (bytesRead > 0)
    {
        cryptoStream.Write(buffer, 0, bytesToRead);

        var bytesLeft = cipherBytes.Length - totalBytesRead;
        bytesToRead = bytesLeft > megabyte ? megabyte : (int)bytesLeft;

        bytesRead = cipherBytes.Read(buffer, 0, bytesToRead);
        totalBytesRead += bytesRead;
    }
    cryptoStream.FlushFinalBlock();  //****************ERROR OCCURS HERE

}

Solution

  • You are writing to the decrypting stream rather than reading it.

    Honestly you don't need any of this code, you can just use CopyTo.

    public static void Encrypt(FileStream fileWriteStream,
        FileStream fileReadStream,
        byte[] key,
        byte[] iv)
    {
        using var aes = Aes.Create()
        aes.Key = key;
        aes.IV = iv;
        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;
    
        using ICryptoTransform encrypt = aes.CreateEncryptor();
        using CryptoStream cs = new CryptoStream(fileWriteStream, encrypt, CryptoStreamMode.Write);
        fileReadStream.CopyTo(cs);
    }
    
    public static void Decrypt(
        Stream cipherBytes,
        FileStream outputStream,
        byte[] key,
        byte[] iv)
    {
        using var aes = Aes.Create();
        aes.Padding = PaddingMode.PKCS7;
        aes.Mode = CipherMode.CBC;
        aes.IV = iv;
        if (key.Length != 32)
        {
            var aesKey = new byte[32];
            Array.Copy(key, 0, aesKey, 0, 32);
            key = aesKey;
        }
        encryptor.Key = key;
    
        using ICryptoTransform aesDecryptor = encryptor.CreateDecryptor();
        using CryptoStream cryptoStream = new CryptoStream(cipherBytes, aesDecryptor, CryptoStreamMode.Read);
        cryptoStream.CopyTo(outputStream);
    }
    

    Consider using async functions as well.

        await cryptoStream.CopyToAsync(outputStream, someCancellationTokenHere);