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