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
}
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);