I am performing independent encrypt-decrypt (1st app performs enc 2nd decrypts)
While decryption I either get "Padding invalid" or "Length of data to decrypt is invalid" error.Any help is appreciated thanks
My encryption code:
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.Zeros;
using (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;
}
using (FileStream fsCrypt = new FileStream(file + ".ecc", FileMode.Create))
{
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 + ".ecc", FileMode.Open)) // automatically dispose fsCrypt
{
using (CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
fsCrypt.Seek(fileOffset, SeekOrigin.End);
cs.FlushFinalBlock();
cs.Write(buffer, 0, read);
}
}
}
}
and My decryption code:
public void DecryptFile(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.Zeros;
using (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;
}
string extension = System.IO.Path.GetExtension(file);
string result = file.Substring(0, file.Length - extension.Length);
using (FileStream destination = new FileStream(result, FileMode.CreateNew, FileAccess.Write, FileShare.None))
{
using (CryptoStream cryptoStream = new CryptoStream(destination, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
try
{
using (FileStream source = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
source.CopyTo(cryptoStream);
}
}
catch (CryptographicException exception)
{
if (exception.Message == "Padding is invalid and cannot be removed.")
throw new ApplicationException("Universal Microsoft Cryptographic Exception (Not to be believed!)", exception);
else
throw;
}
}
}
}
The following calls are executed in the wrong order:
cs.FlushFinalBlock();
and
cs.Write(buffer, 0, read);
FlushFinalBlock should always come last (it will be automatically called when the stream is closed though, so it should be OK if you don't close the underlying stream first). FlushFinalBlock should certainly never be called in a (while) loop.
The buffer handling is not correct: the streams should not be closed and opened all the time. Instead a block of bytes should be read, then written to the crypto stream. The using statements should be outside the while loop.
For CFB mode PaddingMode.PKCS7 should be set. Padding is not required at all for CFB. Unfortunately .NET seems to have a buggy implementation that does require padding to be used in combination to the streams.
Related
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();
Description
I am using a slightly modified security class taken straight from codeproject.com, I am using it to encrypt/decrypt aes byte arrays.
I have tested that the key generation method in these produces the same key, and they are given the same password and salt. they also have the same IV
the error is always on the line after the CryptoStream.Write in the AES_Decrypt function
"An unhandled exception of type 'System.Security.Cryptography.CryptographicException' occurred in mscorlib.dll
Additional information: Padding is invalid and cannot be removed."
(yes i know having a static salt is bad, this is only for testing purposes)
Solutions that didn't work
Paddingmode.Zeros
Paddingmode.None
.FlushFinalBlock();
Related Questions
"Padding is invalid and cannot be removed" using AesManaged
Padding is invalid and cannot be removed?
Padding is invalid and cannot be removed Exception while decrypting string using "AesManaged" C#
Error RijndaelManaged, "Padding is invalid and cannot be removed"
Related Links
https://social.msdn.microsoft.com/Forums/vstudio/en-US/d1788582-bf8c-43ec-a686-49647c359136/unexplained-cryptographicexception-padding-is-invalid?forum=netfxbcl
http://www.codeproject.com/Questions/379525/Padding-is-invalid-and-cannot-be-removed-Exception
Code
using System.Security.Cryptography;
using System.IO;
using System;
namespace FinexCore
{
public class Security
{
/*
* AES encrypt and decrypt from:
* http://www.codeproject.com/Articles/769741/Csharp-AES-bits-Encryption-Library-with-Salt
*/
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
private byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8}; //8 bytes
public byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.Padding = PaddingMode.PKCS7;
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(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.FlushFinalBlock();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
public byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
{
byte[] decryptedBytes = null;
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.Padding = PaddingMode.PKCS7;
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(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.FlushFinalBlock();
}
decryptedBytes = ms.ToArray();
}
}
return decryptedBytes;
}
public void Wipe()
{
Array.Clear(saltBytes, 0, saltBytes.Length);
}
}
}
note: the save and load functions are called with the exact same password
The code that calls the encryption
public void Load(byte[] password)
{
Security decryptor = new Security();
byte[] bytes = decryptor.AES_Decrypt(System.IO.File.ReadAllBytes(Pathname()), password);
using (MemoryStream stream = new MemoryStream(bytes))
{
var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
Root = (Folder)binaryFormatter.Deserialize(stream);
}
Array.Clear(bytes, 0, bytes.Length);
Array.Clear(password, 0, password.Length);
decryptor.Wipe();
}
public void Save(byte[] password)
{
byte[] bytes;
using (MemoryStream stream = new MemoryStream())
{
var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
binaryFormatter.Serialize(stream, Root);
bytes = stream.ToArray();
}
Security encryptor = new Security();
bytes = encryptor.AES_Encrypt(bytes, password);
System.IO.File.WriteAllBytes(Pathname(), bytes);
Array.Clear(bytes, 0, bytes.Length);
Array.Clear(password, 0, password.Length);
encryptor.Wipe();
}
Code for testing key generation
static void keytests()
{
byte[] passwordBytes = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
byte[] saltBytes = { 1, 2, 3, 4, 5, 6, 7, 8};
int iterations = 1000;
var key1 = new Rfc2898DeriveBytes(passwordBytes, saltBytes, iterations);
var key2 = new Rfc2898DeriveBytes(passwordBytes, saltBytes, iterations);
Console.WriteLine(BTS(key1.GetBytes(256 / 8)));
Console.WriteLine(BTS(key2.GetBytes(256 / 8)));
}
public static string BTS(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
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);
}
}
}
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;
}
I'm trying to encrypt XML, and after decryption I end up with 1 byte too many - probably because of padding. This is my code. How can I change this to make it work?
public byte[] encryptData(byte[] source,string key)
{
byte[] btKeyInBytes = UTF8Encoding.UTF8.GetBytes(key);
Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(key, btKeyInBytes);
AesManaged encryptor = new AesManaged();
encryptor.Padding = PaddingMode.Zeros;
using (MemoryStream encryptStream = new MemoryStream())
{
using (CryptoStream encStream = new CryptoStream(encryptStream, encryptor.CreateEncryptor(rfc.GetBytes(16), rfc.GetBytes(16)), CryptoStreamMode.Read))
{
//Read from the input stream, then encrypt and write to the output stream.
encStream.Write(source, 0, source.Length);
encStream.FlushFinalBlock();
encryptor.Clear();
}
encryptStream.Flush();
encryptedSource = encryptStream.ToArray();
}
return encryptedSource;
}
public byte[] decryptData(byte[] source, string key)
{
byte[] encryptedSource = null;
byte[] btKeyInBytes = UTF8Encoding.UTF8.GetBytes(key);
Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(key, btKeyInBytes);
AesManaged encryptor = new AesManaged();
encryptor.Padding = PaddingMode.Zeros;
using (MemoryStream encryptStream = new MemoryStream())
{
using (CryptoStream encStream = new CryptoStream(encryptStream, encryptor.CreateDecryptor(rfc.GetBytes(16), rfc.GetBytes(16)), CryptoStreamMode.Write))
{
//Read from the input stream, then encrypt and write to the output stream.
encStream.Write(source, 0, source.Length);
encStream.FlushFinalBlock();
encryptor.Clear();
}
encryptStream.Flush();
encryptedSource = encryptStream.ToArray();
}
return encryptedSource;
}
It seems that the padding gives me 1 extra byte during decryption
If your problem is padding, then PaddingMode.Zeros is about the worst choice, since zeros cannot always be reliably removed. Better to use PKCS7 padding.
It is also possible that the encoding of end-of-line has changed between systems. Some systems use a single byte while other systems use two bytes. You really need to look at exactly what is in the decrypted file, byte by byte, as #Rup suggests.
I got it!
Now let's try to explain.
Let's say I have a file of 927 bytes.
What I do is to read this file and split it in pieces of 656 bytes. This byte array of 656 bytes is being encrypted. The second array will be 271 bytes.
In every block for encryption I used padding. When decrypting, you will not be able to know in which block padding was used because every block now can be divided by 16 (because of the padding in the encryption). Basically I only want padding used for the last block(271).
so this is my new code:
public byte[] encryptData(byte[] source, string key, bool padding)
{
byte[] encryptedSource = null;
byte[] btKeyInBytes = UTF8Encoding.UTF8.GetBytes(key);
Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(key, btKeyInBytes);
AesManaged encryptor = new AesManaged();
//encryptor.Mode = CipherMode.CFB;
encryptor.KeySize = 128; // in bits
encryptor.Key = new byte[128 / 8]; // 16 bytes for 128 bit encryption
encryptor.IV = new byte[128 / 8];
if (padding) { encryptor.Padding = PaddingMode.PKCS7; }
else { encryptor.Padding = PaddingMode.None; }
using (MemoryStream encryptStream = new MemoryStream())
{
using (CryptoStream encStream =
new CryptoStream(encryptStream,
encryptor.CreateEncryptor(rfc.GetBytes(16),
rfc.GetBytes(16)),
CryptoStreamMode.Write))
{
//Read from the input stream, then encrypt and write to the output stream.
encStream.Write(source, 0, source.Length);
}
encryptStream.Flush();
encryptedSource = encryptStream.ToArray();
}
return encryptedSource;
}
public byte[] decryptData(byte[] source, string key,bool padding)
{
byte[] encryptedSource = null;
byte[] btKeyInBytes = UTF8Encoding.UTF8.GetBytes(key);
Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(key, btKeyInBytes);
AesManaged encryptor = new AesManaged();
encryptor.Key = new byte[128 / 8]; // 16 bytes for 128 bit encryption
encryptor.IV = new byte[128 / 8];
if (padding) { encryptor.Padding = PaddingMode.PKCS7; }
else { encryptor.Padding = PaddingMode.None; }
using (MemoryStream encryptStream = new MemoryStream())
{
using (CryptoStream encStream =
new CryptoStream(encryptStream,
encryptor.CreateDecryptor(rfc.GetBytes(16),
rfc.GetBytes(16)),
CryptoStreamMode.Write))
{
//Read from the input stream, then encrypt and write to the output stream.
encStream.Write(source, 0, source.Length);
}
encryptStream.Flush();
encryptedSource = encryptStream.ToArray();
}
return encryptedSource;
}
I hope this helps!