Invalid files produced after encrypting/decrypting - c#

I have made a cryptography application and it was working fine until I started doing the encryption/decryption in blocks. here's my encryption/decryption code:
public static class AESCryptography
{
private const int keysize = 256;
public static void Encrypt(string Input, string passPhrase)
{
var chunkSize = 16;
PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
byte[] keyBytes = password.GetBytes(keysize / 8);
FileStream fsOutput = File.OpenWrite(Input.Substring(0, Input.LastIndexOf('.')) + ".aent");
FileStream fsInput = File.OpenRead(Input);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.GenerateIV();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, symmetricKey.IV);
CryptoStream cryptoStream = new CryptoStream(fsOutput, encryptor, CryptoStreamMode.Write);
fsOutput.Write(symmetricKey.IV, 0, symmetricKey.IV.Length);
for (long i = 0; i < fsInput.Length; i += chunkSize)
{
byte[] chunkData = new byte[chunkSize];
int bytesRead = 0;
while ((bytesRead = fsInput.Read(chunkData, 0, chunkSize)) > 0)
{
if (bytesRead != chunkSize)
{
for (int x = bytesRead - 1; x < chunkSize; x++)
{
chunkData[x] = 0;
}
}
cryptoStream.Write(chunkData, 0, chunkSize);
}
}
cryptoStream.FlushFinalBlock();
cryptoStream.Dispose();
fsOutput.Dispose();
fsInput.Dispose();
encryptor.Dispose();
symmetricKey.Dispose();
password.Dispose();
}
public static void Decrypt(string Input, string passPhrase)
{
var chunkSize = 16;
PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
byte[] keyBytes = password.GetBytes(keysize / 8);
FileStream fsInput = File.OpenRead(Input);
FileStream fsOutput = File.OpenWrite(Input.Substring(0, Input.LastIndexOf('.')) + ".txt");
byte[] initVectorBytes = new byte[16];
byte[] buffer = new byte[8];
fsInput.Read(buffer, 0, 8);
long fileLength = BitConverter.ToInt64(buffer, 0);
fsInput.Read(initVectorBytes, 0, 16);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);
CryptoStream cryptoStream = new CryptoStream(fsOutput, decryptor, CryptoStreamMode.Write);
for (long i = 0; i < fsInput.Length; i += chunkSize)
{
byte[] chunkData = new byte[chunkSize];
int bytesRead = 0;
while ((bytesRead = fsInput.Read(chunkData, 0, chunkSize)) > 0)
{
cryptoStream.Write(chunkData, 0, bytesRead);
}
}
}
}
if I encrypt a .txt file that says for example: "hello world! the app works!!" i will get some awkward Chinese text mixed with symbols I've never seen.

You've left some code incomplete. This code here in Decrypt is a problem:
byte[] buffer = new byte[8];
fsInput.Read(buffer, 0, 8);
long fileLength = BitConverter.ToInt64(buffer, 0);
This reads eight bytes that were never written by Encrypt, shifting everything you read after that by eight bytes. Comment out those three lines and your code works.
You are also missing a cryptoStream.FlushFinalBlock() after your loop in Decrypt. Because of the 8-byte offset by the bug, you would get a CryptographicException "Length of the data to decrypt is invalid." (You missed 8 bytes.) But after commenting out those lines, FlushFinalBlock() will succeed.

Related

Padding is invalid and cannot be removed with AES

Can you help please...
I want to encrypt / decrypt the file using Aes. Encryption part is successful working. But my problem is when decrypting the text encrypted error is occurred : Padding is invalid and cannot removed
My error is occurred on line (private void FileDecrypt) :
while ((read = cryptoStream.Read(buffer, 0, buffer.Length)) > 0)
My code :
private void FileEncrypt(string inputFile, string outputFile, string password)
{
byte[] salt = GenerateSalt();
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
RijndaelManaged AES = new RijndaelManaged();
AES.KeySize = 256;//AES 256 bits
AES.BlockSize = 128;//AES 128 bits
AES.Padding = PaddingMode.PKCS7;
//AES.Padding = PaddingMode.Zeros;
var key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CFB;
using (FileStream filestreamCrypt = new FileStream(outputFile, FileMode.Create))
{
filestreamCrypt.Write(salt, 0, salt.Length);
using (CryptoStream cs = new CryptoStream(filestreamCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
using (FileStream filestreamIn = new FileStream(inputFile, FileMode.Open))
{
int readLength = (int)filestreamIn.Length;
byte[] buffer = new byte[readLength];
int read;
while ((read = filestreamIn.Read(buffer, 0, buffer.Length)) > 0)
{
cs.Write(buffer, 0, read);
cs.FlushFinalBlock();
}
}
}
filestreamCrypt.Dispose();
}
}
private void FileDecrypt(string inputFileName, string outputFileName, string password)
{
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
byte[] salt = new byte[32];
using (FileStream filestreamCrypt = new FileStream(inputFileName, FileMode.Open))
{
filestreamCrypt.Read(salt, 0, salt.Length);
RijndaelManaged AES = new RijndaelManaged();
AES.KeySize = 256;//AES 256 bits
AES.BlockSize = 128;//AES 128 bits
var key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Padding = PaddingMode.PKCS7;
//AES.Padding = PaddingMode.Zeros;
AES.Mode = CipherMode.CFB;
using (CryptoStream cryptoStream = new CryptoStream(filestreamCrypt, AES.CreateDecryptor(), CryptoStreamMode.Read))
{
using (FileStream filestreamOut = new FileStream(outputFileName, FileMode.Create))
{
int read;
int readLength = (int)filestreamCrypt.Length;
byte[] buffer = new byte[readLength];
//var fullCipher = Convert.FromBase64String(filestreamOut.ToString());
while ((read = cryptoStream.Read(buffer, 0, buffer.Length)) > 0) **//ERROR**
{
filestreamOut.Write(buffer, 0, read);
}
}
}
}
}
Thank you very much for help.
Replace this:
while ((read = filestreamIn.Read(buffer, 0, buffer.Length)) > 0)
{
cs.Write(buffer, 0, read);
cs.FlushFinalBlock(); <--- Flush final block every time around loop !
}
With this:
while ((read = filestreamIn.Read(buffer, 0, buffer.Length)) > 0)
{
cs.Write(buffer, 0, read);
}
cs.FlushFinalBlock();

Error while decrypting a bytes array using AES256 C#

I was doing a program for encrypting private files on another computer apart, where this same formula worked correctly, I don't understand why this part of code doesn't work, even though it worked before:
public static byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
{
byte[] decryptedBytes = null;
byte[] saltBytes = new byte[] { 2, 0, 0, 4, 0, 3, 0, 3 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 256;
AES.Padding = PaddingMode.PKCS7;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CFB;
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.Close();
}
decryptedBytes = ms.ToArray();
}
}
return decryptedBytes;
}
I've been trying to find out for about 2 hours because it gives me that error when decrypting, but counting on the little encryption knowledge that I have have it's absolutely impossible.
First of all, when i say ChunkSize i mean ChunkSize, because first of all I split the file into 5Mb bytes array, then i encrypt or decrypt the Byte Array and write the data into the file.
Problem was, that ChunkSize was like less than 1Mb and thats why CryptographicException arises.
if (File.Exists(FileToDecrypt)) {
byte[] PasswordBytes;
if (IsFileTypePassword) {
PasswordBytes = File.ReadAllBytes(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),"K3ys\\" + PasswordToDecrypt));
} else {
PasswordBytes = Encoding.UTF8.GetBytes(PasswordToDecrypt);
}
DecryptFile(5000000, FileToDecrypt, PasswordBytes); // Here was the problem
DecThread.Abort();
}
And here is the function that splits the file, its a little bit different than mine but it does the same, also this piece of code is grabbed from another Question form StackOverflow.
public static void SplitFile(string inputFile, int chunkSize, string path)
{
byte[] buffer = new byte[chunkSize];
using (Stream input = File.OpenRead(inputFile))
{
int index = 0;
while (input.Position < input.Length)
{
using (Stream output = File.Create(path + "\\" + index))
{
int chunkBytesRead = 0;
while (chunkBytesRead < chunkSize)
{
int bytesRead = input.Read(buffer,
chunkBytesRead,
chunkSize - chunkBytesRead);
if (bytesRead == 0)
{
break;
}
chunkBytesRead += bytesRead;
}
output.Write(buffer, 0, chunkBytesRead);
}
index++;
}
}
}

AES encryption on file over 1GB

I am making a app that will encrypt 5 video files. The problem is that it only encrypts 4 out of 5 files(the ones <1gb).
On the 5th file, which is over 1GB, the System.OutOfMemoryException is thrown.
(i know i asked it previously ,but i made some changes as suggested but it still wont work, i dont mean to spam)
Here's my code:
//Encrypts single file
public void EncryptFile(string file, string password)
{
byte[] bytesToBeEncrypted = File.ReadAllBytes(file);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
// Hash the password with SHA256
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] salt = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
FileStream fsCrypt = new FileStream(file + ".enc", FileMode.Create);
//Set Rijndael symmetric encryption algorithm
RijndaelManaged AES = new RijndaelManaged();
AES.KeySize = AES.LegalKeySizes[0].MaxSize;
AES.BlockSize = AES.LegalBlockSizes[0].MaxSize;
AES.Padding = PaddingMode.PKCS7;
//"What it does is repeatedly hash the user password along with the salt." High iteration counts.
var key = new Rfc2898DeriveBytes(passwordBytes, salt, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CFB;
//write salt to the beginning of the output file, so in this case can be random every time
fsCrypt.Write(salt, 0, salt.Length);
CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write);
FileStream fsIn = new FileStream(file, FileMode.Open);
//create a buffer (1mb) so only this amount will allocate in the memory and not the whole file
byte[] buffer = new byte[5048576];
int read;
try
{
while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0)
{
Application.DoEvents();
cs.Write(buffer, 0, read);
}
fsIn.Close();
fsIn.Dispose();
}
catch (System.OutOfMemoryException ex)
{
cs.Flush();
cs.Dispose();
}
finally
{
cs.Close();
fsCrypt.Close();
}
You are reading the entire file at the start of the method:
byte[] bytesToBeEncrypted = File.ReadAllBytes(file);
This is causing the OutOfMemoryException. Here's an idea of how you'd do this:
static void EncryptFile(string file, string password)
{
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
byte[] salt = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
RijndaelManaged AES = new RijndaelManaged();
AES.KeySize = AES.LegalKeySizes[0].MaxSize;
AES.BlockSize = AES.LegalBlockSizes[0].MaxSize;
AES.Padding = PaddingMode.PKCS7;
//"What it does is repeatedly hash the user password along with the salt." High iteration counts.
using (var key = new Rfc2898DeriveBytes(passwordBytes, salt, 1000)) // automatically dispose key
{
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CFB;
}
using (FileStream fsCrypt = new FileStream(file + ".enc", FileMode.Create)) // automatically dispose fsCrypt
{
//write salt to the beginning of the output file, so in this case can be random every time
fsCrypt.Write(salt, 0, salt.Length);
}
int bytesToRead = 128 * 1024 * 1024; // 128MB
byte[] buffer = new byte[bytesToRead]; // create the array that will be used encrypted
long fileOffset = 0;
int read = 0;
bool allRead = false;
while (!allRead)
{
using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read))
{
fs.Seek(fileOffset, SeekOrigin.Begin); // continue reading from where we were...
read = fs.Read(buffer, 0, bytesToRead); // read the next chunk
}
if (read == 0)
allRead = true;
else
fileOffset += read;
using (FileStream fsCrypt = new FileStream(file + ".enc", FileMode.Open)) // automatically dispose fsCrypt
{
using (CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
fsCrypt.Seek(fileOffset, SeekOrigin.End);
cs.Write(buffer, 0, read);
}
}
}

AES encryption on large files

I need to encrypt and decrypt large file (~1GB).
I tried using this example: http://www.codeproject.com/Articles/769741/Csharp-AES-bits-Encryption-Library-with-Salt
But my problem is since the file is very large, I'm getting outOfMemory exception.
So I need to replace the memory stream with file stream, I just not sure how to do it...
(Adding my code:)
private static void AES_Encrypt(string srcFile, string encryptedFile, byte[] passwordBytes)
{
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8};
FileStream fsInput = new FileStream(srcFile,
FileMode.Open,
FileAccess.Read);
FileStream fsEncrypted = new FileStream(encryptedFile,
FileMode.Create,
FileAccess.Write);
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(fsEncrypted, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
byte[] bytearrayinput = new byte[fsInput.Length - 1];
fsInput.Read(bytearrayinput, 0, bytearrayinput.Length);
cs.Write(bytearrayinput, 0, bytearrayinput.Length);
cs.Close();
fsInput.Flush();
fsInput.Close();
fsEncrypted.Close();
}
}
}
public static void AES_Decrypt(string encryptedFile, string decryptedFile, byte[] passwordBytes)
{
byte[] decryptedBytes = null;
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8};
FileStream fsread = new FileStream(encryptedFile,
FileMode.Open,
FileAccess.Read);
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
FileStream fsDecrypted = new FileStream(decryptedFile,
FileMode.Create,
FileAccess.Write);
using (var cs = new CryptoStream(fsDecrypted, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
byte[] bytearrayinput = new byte[fsread.Length - 1];
fsread.Read(bytearrayinput, 0, bytearrayinput.Length);
cs.Write(bytearrayinput, 0, bytearrayinput.Length);
cs.Close();
fsread.Close();
fsDecrypted.Close();
}
}
}
Eventually, this is the code that worked for me:
private static void AES_Encrypt(string inputFile, string outputFile, byte[] passwordBytes)
{
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8};
string cryptFile = outputFile;
FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create);
RijndaelManaged AES = new RijndaelManaged();
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Padding = PaddingMode.Zeros;
AES.Mode = CipherMode.CBC;
CryptoStream cs = new CryptoStream(fsCrypt,
AES.CreateEncryptor(),
CryptoStreamMode.Write);
FileStream fsIn = new FileStream(inputFile, FileMode.Open);
int data;
while ((data = fsIn.ReadByte()) != -1)
cs.WriteByte((byte)data);
fsIn.Close();
cs.Close();
fsCrypt.Close();
}
private static void AES_Decrypt(string inputFile, string outputFile, byte[] passwordBytes)
{
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8};
FileStream fsCrypt = new FileStream(inputFile, FileMode.Open);
RijndaelManaged AES = new RijndaelManaged();
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Padding = PaddingMode.Zeros;
AES.Mode = CipherMode.CBC;
CryptoStream cs = new CryptoStream(fsCrypt,
AES.CreateDecryptor(),
CryptoStreamMode.Read);
FileStream fsOut = new FileStream(outputFile, FileMode.Create);
int data;
while ((data = cs.ReadByte()) != -1)
fsOut.WriteByte((byte)data);
fsOut.Close();
cs.Close();
fsCrypt.Close();
}
}
So I created a fairly fast and low memory consumption version:
I use a "temporary buffer" and also "use a random salt and store it with the ciphertext".
To encrypt:
private void AES_Encrypt(string inputFile, string password)
{
//http://stackoverflow.com/questions/27645527/aes-encryption-on-large-files
//generate random salt
byte[] salt = GenerateRandomSalt();
//create output file name
FileStream fsCrypt = new FileStream(inputFile + ".aes", FileMode.Create);
//convert password string to byte arrray
byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(password);
//Set Rijndael symmetric encryption algorithm
RijndaelManaged AES = new RijndaelManaged();
AES.KeySize = 256;
AES.BlockSize = 128;
AES.Padding = PaddingMode.PKCS7;
//http://stackoverflow.com/questions/2659214/why-do-i-need-to-use-the-rfc2898derivebytes-class-in-net-instead-of-directly
//"What it does is repeatedly hash the user password along with the salt." High iteration counts.
var key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
//Cipher modes: http://security.stackexchange.com/questions/52665/which-is-the-best-cipher-mode-and-padding-mode-for-aes-encryption
AES.Mode = CipherMode.CFB;
//write salt to the begining of the output file, so in this case can be random every time
fsCrypt.Write(salt, 0, salt.Length);
CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write);
FileStream fsIn = new FileStream(inputFile, FileMode.Open);
//create a buffer (1mb) so only this amount will allocate in the memory and not the whole file
byte[] buffer = new byte[1048576];
int read;
try
{
while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0)
{
Application.DoEvents(); // -> for responsive GUI, using Task will be better!
cs.Write(buffer, 0, read);
}
//close up
fsIn.Close();
}
catch (Exception ex)
{
Debug.WriteLine("Error: " + ex.Message);
}
finally
{
cs.Close();
fsCrypt.Close();
}
}
To decrypt:
private void AES_Decrypt(string inputFile, string password)
{
//todo:
// - create error message on wrong password
// - on cancel: close and delete file
// - on wrong password: close and delete file!
// - create a better filen name
// - could be check md5 hash on the files but it make this slow
byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(password);
byte[] salt = new byte[32];
FileStream fsCrypt = new FileStream(inputFile, FileMode.Open);
fsCrypt.Read(salt, 0, salt.Length);
RijndaelManaged AES = new RijndaelManaged();
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);
AES.Padding = PaddingMode.PKCS7;
AES.Mode = CipherMode.CFB;
CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateDecryptor(), CryptoStreamMode.Read);
FileStream fsOut = new FileStream(inputFile + ".decrypted", FileMode.Create);
int read;
byte[] buffer = new byte[1048576];
try
{
while ((read = cs.Read(buffer, 0, buffer.Length)) > 0)
{
Application.DoEvents();
fsOut.Write(buffer, 0, read);
}
}
catch (System.Security.Cryptography.CryptographicException ex_CryptographicException)
{
Debug.WriteLine("CryptographicException error: " + ex_CryptographicException.Message);
}
catch (Exception ex)
{
Debug.WriteLine("Error: " + ex.Message);
}
try
{
cs.Close();
}
catch (Exception ex)
{
Debug.WriteLine("Error by closing CryptoStream: " + ex.Message);
}
finally
{
fsOut.Close();
fsCrypt.Close();
}
}
To generate random salt:
public static byte[] GenerateRandomSalt()
{
//Source: http://www.dotnetperls.com/rngcryptoserviceprovider
byte[] data = new byte[32];
using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
{
// Ten iterations.
for (int i = 0; i < 10; i++)
{
// Fill buffer.
rng.GetBytes(data);
}
}
return data;
}
Since you are reading from a file and writing to a file just replace the memory streams by IOStream or FileStream.
You'll have to refactor the procedures a bit so they don't expect/return byte arrays.
For anyone still looking into this and don't want the output to a file but rather a stream
the key is to make sure to call cryptoStream.FlushFinalBlock(); otherwise the decrypt will miss the last few characters.
public MemoryStream FileEncrypt(string inputFilePath, byte[] passwordBytes)
{
var saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
var memoryStream = new MemoryStream();
var aes = new RijndaelManaged {KeySize = 256, BlockSize = 128};
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
aes.Key = key.GetBytes(aes.KeySize / 8);
aes.IV = key.GetBytes(aes.BlockSize / 8);
aes.Padding = PaddingMode.Zeros;
aes.Mode = CipherMode.CBC;
var cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write);
var fileStream = new FileStream(inputFilePath, FileMode.Open);
int data;
while ((data = fileStream.ReadByte()) != -1)
cryptoStream.WriteByte((byte)data);
cryptoStream.FlushFinalBlock();
return memoryStream;
}
public MemoryStream FileDecrypt(Stream encryptedFileStream, byte[] passwordBytes)
{
var saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
var AES = new RijndaelManaged {KeySize = 256, BlockSize = 128};
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Padding = PaddingMode.Zeros;
AES.Mode = CipherMode.CBC;
var cryptoStream = new CryptoStream(encryptedFileStream, AES.CreateDecryptor(), CryptoStreamMode.Read);
var memoryStream = new MemoryStream();
int data;
while ((data = cryptoStream.ReadByte()) != -1)
memoryStream.WriteByte((byte)data);
return memoryStream;
}

Calculation of encrypted file size does not match true size

I have the need to calculate the size of a file I am encrypting using Rijndael.
According to other answers on this site, and on google, the following is the correct way of calculating encrypted data length:
EL = UL + (BS - (UL Mod BS) Mod BS)
Where:
EL = Encrypted Length
UL = Unencrypted Length
BS = Block Size
In my instance, the unencrypted file length is 5,101,972 bytes, and I am using a 128bit encryption key, giving me a block size of 16 bytes.
Therefore, the equation is:
EL = 5101972 + (16 - (5101972 Mod 16) Mod 16)
EL = 5101972 + (16 - 4 Mod 16)
EL = 5101972 + (12 Mod 16)
EL = 5101972 + 12
EL = 5101984
Giving an encrypted file length of 5,101,984 bytes.
However, the size of my file after encryption weighs in at 5,242,896
A massive difference in sizes of 140,912 bytes!
Now.. I'm obviously doing SOMETHING wrong, but I can't work out what it is.
Below is my encryption and decryption test code, as well as the method used to calculate the encrypted size:
private static void Enc(string decryptedFileName, string encryptedFileName)
{
PasswordDeriveBytes passwordDB = new PasswordDeriveBytes("ThisIsMyPassword", Encoding.ASCII.GetBytes("thisIsMysalt!"), "MD5", 2);
byte[] passwordBytes = passwordDB.GetBytes(128 / 8);
using (FileStream fsOutput = File.OpenWrite(encryptedFileName))
{
using(FileStream fsInput = File.OpenRead(decryptedFileName))
{
byte[] IVBytes = Encoding.ASCII.GetBytes("1234567890123456");
fsOutput.Write(BitConverter.GetBytes(fsInput.Length), 0, 8);
fsOutput.Write(IVBytes, 0, 16);
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC,Padding=PaddingMode.Zeros};
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(passwordBytes, IVBytes);
using (CryptoStream cryptoStream = new CryptoStream(fsOutput, encryptor, CryptoStreamMode.Write))
{
for (long i = 0; i < fsInput.Length; i += chunkSize)
{
byte[] chunkData = new byte[chunkSize];
int bytesRead = 0;
while ((bytesRead = fsInput.Read(chunkData, 0, chunkSize)) > 0)
{
cryptoStream.Write(chunkData, 0, chunkSize);
}
}
}
}
}
}
private static void Dec(string encryptedFileName, string decryptedFileName)
{
PasswordDeriveBytes passwordDB = new PasswordDeriveBytes("ThisIsMyPassword", Encoding.ASCII.GetBytes("thisIsMysalt!"), "MD5", 2);
byte[] passwordBytes = passwordDB.GetBytes(128 / 8);
using (FileStream fsInput = File.OpenRead(encryptedFileName))
{
using (FileStream fsOutput = File.OpenWrite(decryptedFileName))
{
byte[] buffer = new byte[8];
fsInput.Read(buffer, 0, 8);
long fileLength = BitConverter.ToInt64(buffer, 0);
byte[] IVBytes = new byte[16];
fsInput.Read(IVBytes, 0, 16);
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC,Padding=PaddingMode.Zeros};
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(passwordBytes, IVBytes);
using (CryptoStream cryptoStream = new CryptoStream(fsOutput, decryptor, CryptoStreamMode.Write))
{
for (long i = 0; i < fsInput.Length; i += chunkSize)
{
byte[] chunkData = new byte[chunkSize];
int bytesRead = 0;
while ((bytesRead = fsInput.Read(chunkData, 0, chunkSize)) > 0)
{
cryptoStream.Write(chunkData, 0, bytesRead);
}
}
fsOutput.SetLength(fileLength);
}
}
}
}
private static void CalcEncSize(string decryptedFileName)
{
FileInfo fi = new FileInfo(decryptedFileName);
if (fi.Exists)
{
long blockSize = 128/8;
long fileLength = fi.Length;
long encryptedSize = fileLength + ((blockSize - (fileLength % blockSize)) % blockSize);
encryptedSize += 24; //16 bytes for the IV, and 8 more for the filelength, both stored at the start of the file.
Console.WriteLine("Estimated Encryption Size: " + encryptedSize.ToString());
}
}
Note: In the calculations at the start, I am NOT including the extra 24 bytes that are used at the start of the encrypted file by myself to store the original file length, and the IV... I know this, but didn't want to complicate the equation more than necessary.
You shouldn't write to the output file before the actual encryption process. The CryptoStream will handle all the necessary padding when it is closed. Also the for loop isn't necessary as the inner while loop will read the entire file. Also you should only write as much as was read from the file. Try these changes.
private static void Enc(string decryptedFileName, string encryptedFileName)
{
PasswordDeriveBytes passwordDB = new PasswordDeriveBytes("ThisIsMyPassword", Encoding.ASCII.GetBytes("thisIsMysalt!"), "MD5", 2);
byte[] passwordBytes = passwordDB.GetBytes(128 / 8);
using (FileStream fsOutput = File.OpenWrite(encryptedFileName))
{
using(FileStream fsInput = File.OpenRead(decryptedFileName))
{
byte[] IVBytes = Encoding.ASCII.GetBytes("1234567890123456");
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC,Padding=PaddingMode.Zeros};
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(passwordBytes, IVBytes);
using (CryptoStream cryptoStream = new CryptoStream(fsOutput, encryptor, CryptoStreamMode.Write))
{
byte[] chunkData = new byte[chunkSize];
int bytesRead = 0;
while ((bytesRead = fsInput.Read(chunkData, 0, chunkSize)) > 0)
{
cryptoStream.Write(chunkData, 0, bytesRead); //KEY FIX
}
}
}
}
}
[edit]
Oh I've missed out on a lot of information. I misread what your sizes were thinking 140,912 was the size, not difference. Now that I see that, I can make more intelligible response. Based on your code, the difference in sizes should be comparable to your chunk size. Since the chunkSize can be somewhat large, your code will typically write up to chunkSize more data than is actually in your input file (as Greg and caf pointed out). The line that I've marked is the reason for the discrepancy.

Categories