【问题标题】:Decrease memory consumption when encrypting files c#加密文件时减少内存消耗c#
【发布时间】:2014-08-20 11:57:09
【问题描述】:

我的加密应用程序占用了大量内存,它根本无法处理大文件,我该如何优化我的代码以处理大文件?我正在使用下面的代码将文件转换为 base64(显着增加文件大小)

Console.Write("Enter File Path: ");
docPath = Console.ReadLine();
extension = docPath.Substring(docPath.IndexOf(".")).Trim();
byte[] binarydata = File.ReadAllBytes(docPath);
text = System.Convert.ToBase64String(binarydata, 0, binarydata.Length);
var Encrypted = AESCryptography.Encrypt(text, m.ToString(), extension);
using (FileStream fs = File.Create(docPath.Substring(0,docPath.IndexOf(".")) + ".aent"))
{
    Byte[] info = new UTF8Encoding(true).GetBytes(Encrypted);
    // Add some information to the file.
    fs.Write(info, 0, info.Length);
}

如何分块执行此操作?这是我的加密类:

public static class AESCryptography
{
    private const int keysize = 256;
    public static string Encrypt(string plainText, string passPhrase, string extention)
    {
        byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
        using (PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null))
        {
            byte[] keyBytes = password.GetBytes(keysize / 8);
            using (RijndaelManaged symmetricKey = new RijndaelManaged())
            {
                symmetricKey.GenerateIV();
                symmetricKey.Mode = CipherMode.CBC;
                using (ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, symmetricKey.IV))
                {
                    using (MemoryStream memoryStream = new MemoryStream())
                    {
                        using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                        {
                            cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
                            cryptoStream.FlushFinalBlock();
                            byte[] cipherTextBytes = memoryStream.ToArray();
                            return Convert.ToBase64String(cipherTextBytes) + "\n" + Convert.ToBase64String(symmetricKey.IV) + "\n" + extention;
                        }
                    }
                }
            }
        }
    }

    public static string Decrypt(string cipherText, string passPhrase, string initVector)
    {
        byte[] initVectorBytes = Convert.FromBase64String(initVector);
        byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
        using (PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null))
        {
            byte[] keyBytes = password.GetBytes(keysize / 8);
            using (RijndaelManaged symmetricKey = new RijndaelManaged())
            {
                symmetricKey.Mode = CipherMode.CBC;
                using (ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes))
                {
                    using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes))
                    {
                        using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                        {
                            byte[] plainTextBytes = new byte[cipherTextBytes.Length];
                            int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
                            return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
                        }
                    }
                }
            }
        }
    }
}

【问题讨论】:

  • 除非有很好的理由,否则您应该远离字符串和 base64 编码,转而使用二进制。

标签: c# file memory encryption aes


【解决方案1】:
  • 首先从文件中读取字节:byte[] binarydata = File.ReadAllBytes(docPath);
  • 然后您将字节转换为字符串:text = System.Convert.ToBase64String(binarydata, 0, binarydata.Length);
  • 然后你从字符串中取回你的字节:byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

您当前将所有大文件两次存储在内存中(实际上是两倍以上,因为 base64 字符串比字节数组使用更多的内存)。

解决方案:

  • 删除转换为/从字符串转换的部分,这样您就根本不使用字符串,而只使用字节。
  • 然后删除整个 ReadAllBytes 东西,它会将所有文件加载到内存中,然后按小块进行处理。以Using Rijndael encryption for large files 为例

【讨论】:

  • 我不能只修改我当前的代码以分块执行吗?如果我更改我的加密类,我将不得不重新编写我的整个应用程序!
  • 当然可以,只需稍微调整一下代码即可。看看链接的答案。您将文件读入一小段字节,然后将它们写入 CryptoStream,最后调用 FlushFinalBlock。
  • 如果我“将 [the] 文件读入一小块字节,然后 [I] 将它们写入 CryptoStream,然后在最后调用 FlushFinalBlock”我是否必须对我的解密类做同样的事情?
【解决方案2】:
  • 不要将 IV 存储在文件的结尾。您正在强迫读者首先阅读整个加密文件,只是为了发现最需要阅读文件的 IV。将其存储在文件的开头。

  • 不要对文件进行 base64 编码。 Base64 是一种传输格式,不是存储格式。

  • 使用 Stream.CopyTo 读取文件并写入 CryptoStream,无需一次读取所有内存。通过目标文件流而不是内存流返回 CryptoStream。

  • 同上解密,读取 CryptoStream(由输入流支持,跳过 IV 后)并复制到目标文件流

【讨论】:

    猜你喜欢
    • 2013-06-13
    • 1970-01-01
    • 2023-03-18
    • 2015-05-10
    • 1970-01-01
    • 2018-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多