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 calculat开发者_如何学Going 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.
精彩评论