对大文件进行AES加密

我需要加密和解密大文件(~1GB)。 我尝试使用这个例子: http : //www.codeproject.com/Articles/769741/Csharp-AES-bits-Encryption-Library-with-Salt但我的问题是因为文件非常大,我得到outOfMemoryexception。 所以我需要用文件流替换内存流,我只是不知道该怎么做…

(添加我的代码:)

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

最终,这是适合我的代码:

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

所以我创建了一个相当快速和低内存消耗的版本:
我使用“临时缓冲区”并“使用随机盐并将其与密文一起存储”。
要加密:

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

要解密:

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

生成随机盐:

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

由于您正在从文件读取并写入文件,因此只需通过IOStream或FileStream替换内存流。

您将不得不重构一些程序,以便它们不会期望/返回字节数组。