首页 > 解决方案 > C# 处理和加密大文件时内存不足

问题描述

我试图用 AES 加密来加密一个大文件(Camtasia.exe)。现在由于某种原因,我得到了“内存不足”异常。我对此真的很陌生,我不知道如何解决这个问题。这是我的代码

我用它来调用我的加密方法。

bytes = File.ReadAllBytes("Camtasia.exe");
Cryptography.Encryption.EncryptAES(System.Text.Encoding.Default.GetString(bytes), encryptionKey);

这是 AES 加密本身

public static string EncryptAES(string content, string password)
    {
        byte[] bytes = Encoding.UTF8.GetBytes(content);

        using (SymmetricAlgorithm crypt = Aes.Create())
        using (HashAlgorithm hash = MD5.Create())
        using (MemoryStream memoryStream = new MemoryStream())
        {
            crypt.Key = hash.ComputeHash(Encoding.UTF8.GetBytes(password));
            // This is really only needed before you call CreateEncryptor the second time,
            // since it starts out random.  But it's here just to show it exists.
            crypt.GenerateIV();

            using (CryptoStream cryptoStream = new CryptoStream(
                memoryStream, crypt.CreateEncryptor(), CryptoStreamMode.Write))
            {
                cryptoStream.Write(bytes, 0, bytes.Length);
            }

            string base64IV = Convert.ToBase64String(crypt.IV);
            string base64Ciphertext = Convert.ToBase64String(memoryStream.ToArray());

            return base64IV + "!" + base64Ciphertext;
        }
    }

这是我在顶部调用函数“EncryptAES”时再次遇到的错误。如果有人能解释这是如何发生的以及如何解决它,我会很高兴 https://imgur.com/xqcLsKW

标签: c#fileencryptionmemory

解决方案


您正在将整个 exe 读入内存,将其解释为 UTF-16 字符串 (??!),然后将其转换回 UTF-8 字节,然后对其进行加密。这种与字符串的转换非常糟糕。可执行文件不是人类可读的字符串,即使它是,你在使用哪种编码方面也很困惑。我认为您可以删除整个字符串。

您还将整个内容读入内存(实际上是多次,因为整个字符串内容),这很浪费。您不需要这样做:您可以逐位加密它。为此,请使用 Stream。

像这样的东西应该可以工作(未经测试):至少它可以理解一般概念。我们设置了一系列流,让我们逐位从输入文件中读取数据,并将它们逐位写入输出文件。

// The file we're reading from
using var inputStream = File.OpenRead("Camtasia.exe");

// The file we're writing to
using var outputStream = File.OpenWrite("EncryptedFile.txt");

using var HashAlgorithm hash = MD5.Create();
using var aes = Aes.Create();

aes.Key = hash.ComputeHash(Encoding.UTF8.GetBytes(password));

// Turn the IV into a base64 string, add "!", encode as UTF-8, and write to the file
string base64IV = Convert.ToBase64String(aes.IV) + "!";
byte[] base64IVBytes = Encoding.UTF8.GetBytes(base64IV);
outputStream.Write(base64IVBytes, 0, base64IVBytes.Length);

// Create a stream which, when we write bytes to it, turns those into
// base64 characters and writes them to outputStream
using var base64Stream = new CryptoStream(outputStream, new ToBase64Transform(), CryptoStreamMode.Write);

// Create a stream which, when we write bytes to it, encrypts them and sends them to
// base64Stream
using var encryptStream = new CryptoStream(base64Stream, aes.CreateEncryptor(), CryptoStreamMode.Write);

// Copy the entirety of our input file into encryptStream. This will encrypt them and
// push them into base64Stream, which will base64-encode them and push them into
// outputStream
inputStream.CopyTo(encryptStream);

请注意,使用 MD5 派生密钥字节不是最佳实践。使用Rfc2898DeriveBytes.

另请注意,您不一定需要在将加密结果写入文件之前对其进行 base64 编码——您可以直接将加密字节写入。要做到这一点,摆脱base64Stream,并告诉encryptStream直接写到outputStream


推荐阅读