Reputation: 3
I'm implementing encryption/decryption of files, where only certain amount of bytes should be encrypted. For example: I have 500 MB large file and I want to encrypt (and ofcourse decrypt) only 2 MB of file.
I have implemented everything, Encryption works fine (without an error), but when I run Decryption it always throws out this error:
System.Security.Cryptography.CryptographicException: Padding is invalid and cannot be removed.
at System.Security.Cryptography.CapiSymmetricAlgorithm.DepadBlock(Byte[] block, Int32 offset, Int32 count) at System.Security.Cryptography.CapiSymmetricAlgorithm.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
at System.Security.Cryptography.CryptoStream.FlushFinalBlock()
at System.Security.Cryptography.CryptoStream.Dispose(Boolean disposing)
at System.IO.Stream.Close()
at System.IO.Stream.Dispose()
at ...
Then I've tried to set the Padding to encryptor.Padding = PaddingMode.None;
(and also tried PaddingMode.Zeros, etc.). After running Encryption and Decryption with this Padding modes the result of Decryption is without error/exception, but encrypted part of a file is still encrypted, but a bit different. I've also checked that password is correct
Now I'dont have anymore solutions how to handle this "partial" encryption of files. Any ideas? How to deal with this padding or bytes?
Here is my code of Encryption and Decryption process of certain amount of bytes (sorry for the length, but only that you will know :) ):
static byte[] FILE_HEADER = Encoding.Default.GetBytes("header_of_file"); //this is written to the first line of encrypted file
static long limitBytes = 4096 * 8; //limit encryption to this amount of bytes
public static bool Encrypt(string inputFilePath, string outputfilePath, string EncryptionKey)
{
int bytesRead = 1;
long byteWriteCounter = 0;
long encryptedByteCounter = 0;
byte[] blength = null;
byte[] intBytes = null;
int bufferLen = 4096;
byte[] buffer = new byte[bufferLen];
blength = new byte[FILE_HEADER.Length];
long sumBytesRead = 0;
try
{
using (FileStream fsInput = new FileStream(inputFilePath, FileMode.Open))
{
fsInput.Read(blength, 0, blength.Length);
if (!blength.SequenceEqual(FILE_HEADER)) //read the FILE_HEADER - if not equal that we can encrypt otherwise is already encrypted
{
fsInput.Position = 0;
using (FileStream fsOutput = new FileStream(outputfilePath, FileMode.Create))
{
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (CryptoStream cs = new CryptoStream(fsOutput, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
{
try
{
while (bytesRead != 0)
{
bytesRead = fsInput.Read(buffer, 0, bufferLen);
sumBytesRead += bytesRead;
if (sumBytesRead <= limitBytes) //limit encryption
{
if (bytesRead < bufferLen)
{
cs.Write(buffer, 0, bytesRead);
encryptedByteCounter += bytesRead;
byteWriteCounter += bytesRead;
intBytes = Encoding.Default.GetBytes(encryptedByteCounter.ToString("D7"));
fsOutput.Position = 0;
fsOutput.Write(FILE_HEADER, 0, FILE_HEADER.Length); //we write our header to file, to know which is encrypted
fsOutput.Write(intBytes, 0, intBytes.Length); //we write number of encrypted bytes next to header, for decryption to know
bytesRead = 0;
}
else
{
cs.Write(buffer, 0, bytesRead);
encryptedByteCounter += bytesRead;
byteWriteCounter += bytesRead;
}
}
else //if the file is bigger than limit, we continue to write normal data (unencrypted)
{
if (bytesRead < bufferLen)
{
fsOutput.Write(buffer, 0, bytesRead);
byteWriteCounter += bytesRead;
intBytes = Encoding.Default.GetBytes(encryptedByteCounter.ToString("D7"));
fsOutput.Position = 0;
fsOutput.Write(FILE_HEADER, 0, FILE_HEADER.Length);
fsOutput.Write(intBytes, 0, intBytes.Length);
bytesRead = 0;
}
else
{
fsOutput.Write(buffer, 0, bytesRead);
byteWriteCounter += bytesRead;
}
}
}
}
catch (SystemException se)
{
Console.WriteLine("Exception ENC: " + se);
}
}
}
}
return true;
}
else
{
//file is already encrypted
return false;
}
}
}
catch (SystemException se)
{
Console.WriteLine("Main ENC exception: "+se);
return false;
}
}
... and Decryption function:
public static bool Decrypt(string inputFilePath, string outputfilePath, string EncryptionKey)
{
byte[] blength = null;
blength = new byte[FILE_HEADER.Length];
long byteWriteCounter = 0;
int bufferLen = 4096;
byte[] buffer = new byte[bufferLen];
int bytesRead = 1;
long decryptedByteCounter = 0;
long sumBytesRead = 0;
byte[] bufferEncBytes = new byte[7];
try
{
using (FileStream fsInput = new FileStream(inputFilePath, FileMode.Open))
{
fsInput.Read(blength, 0, blength.Length);
if (blength.SequenceEqual(FILE_HEADER))//check if is our encrypted file
{
fsInput.Read(bufferEncBytes, 0, bufferEncBytes.Length);
int numOfDecBytes = Convert.ToInt32(Encoding.Default.GetString(bufferEncBytes)); //get number of encrypted bytes
using (FileStream fsOutput = new FileStream(outputfilePath, FileMode.Create))
{
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (CryptoStream cs = new CryptoStream(fsOutput, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
{
try
{
while (bytesRead != 0)
{
bytesRead = fsInput.Read(buffer, 0, bufferLen);
sumBytesRead += bytesRead;
if (sumBytesRead <= numOfDecBytes) //decrypt until limit
{
if (bytesRead < bufferLen)
{
cs.Write(buffer, 0, bytesRead);
decryptedByteCounter += bytesRead;
byteWriteCounter += bytesRead;
bytesRead = 0; //we are at the end of file, end everything
}
else
{
cs.Write(buffer, 0, bytesRead);
decryptedByteCounter += bytesRead;
byteWriteCounter += bytesRead;
}
}
else //if we have decrypted encrypted bytes, continue with rest of the (normal) data
{
if (bytesRead < bufferLen)
{
fsOutput.Write(buffer, 0, bytesRead);
byteWriteCounter += bytesRead;
bytesRead = 0;
}
else
{
fsOutput.Write(buffer, 0, bytesRead);
byteWriteCounter += bytesRead;
}
}
}
}
catch (SystemException se)
{
Console.WriteLine("Exception DECR: " + se);
}
}
}
}
return true;
}
else
{
//not right file for decryption
return false;
}
}
}
catch (SystemException eks)
{
Console.WriteLine("Error: " + eks); //here the exception of Invalid Padding is thrown
return false;
}
}
Upvotes: 0
Views: 2894
Reputation: 15685
When you encrypt with a block cypher, most modes will add padding. That means that the encrypted data will be longer that the original plaintext, because of the extra padding. You need to keep the padding with the encrypted data, otherwise you will get the bad padding error.
You can either make sure that you handle the padding carefully, or switch to CTR mode, which does not use padding, giving a cyphertext the same length as the plaintext.
Using a Stream Cypher, like Rabbit or Salsa20 would have the same effect.
Upvotes: 2