如何加速这个 C# Filestream 的加密方法

5

我有一种加密方法,运行速度非常慢。加密几百MB的数据需要大约20分钟时间。我不确定我的方法是否正确。如果您能提供帮助、想法或建议,将不胜感激。

    private void AES_Encrypt(string inputFile, string outputFile, byte[] passwordBytes, byte[] saltBytes)
    { 
        FileStream fsCrypt = new FileStream(outputFile, 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);

        fsCrypt.Flush();
        cs.Flush();
        fsIn.Flush();

        fsIn.Close();
        cs.Close();
        fsCrypt.Close();
}

感谢您提供的帮助!

1
如果我猜的没错的话,每次读写一个字节可能会拖慢你的速度。建议尝试从FileStream中使用ReadAllBytes方法读取所有字节,然后将结果缓冲区通过Write方法写入到CryptoStream中。 - Jonathon Chase
3个回答

10

虽然加密可能会变慢,但我不认为这是问题所在。我怀疑是逐字节的IO导致了不必要的开销。最简单的解决方法是通过明智地调用Stream.CopyTo - 而且在此过程中,您应该使用using语句以适当地进行清理:

private void AesEncrypt(string inputFile, string outputFile, byte[] passwordBytes, byte[] saltBytes)
{ 
    var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
    RijndaelManaged aes = new RijndaelManaged
    {
        KeySize = 256,
        BlockSize = 128,
        Key = key.GetBytes(AES.KeySize / 8),
        IV = key.GetBytes(AES.BlockSize / 8),
        Padding = PaddingMode.Zeros,
        Mode = CipherMode.CBC
    };

    using (var output = File.Create(outputFile))
    {
        using (var crypto = new CryptoStream(output, aes.CreateEncryptor(), CryptoStreamMode.Write))
        {
            using (var input = File.OpenRead(inputFile))
            {
                input.CopyTo(crypto);
            }
        }
    }
}

正如其他答案所指出的那样,这并不是一种好的生成IV的方式。通常情况下,我更喜欢使用Rijndael.Create()而不是指定RijndaelManaged - 并且您可能还想为此使用using语句。


谢谢!你太棒了!我会试一下的! - Missy
太好了!节省了我20%的时间!非常感谢 :) - Missy
1
@Missy:说实话,这并没有让我期望的那样有所改善。你尝试过使用Rijndael.Create而不是一定使用RijndaelManaged吗? - Jon Skeet
我不知道如何使用Rigindael创建,而且我还面临着紧迫的截止日期。如果您能提供一个示例,我将不胜感激,并一定会尝试使用它,但所有这些加密内容对我来说都有点可怕。就像IV注释一样-我肯定会担心自己无法解密它。如果您能以任何权威的方式解释一下,也许我就能弄清楚解密方面的问题 :) - Missy
4
@Missy: 这只是一个返回Rijndael引用的静态方法。直接使用它,而不是new RijndaelManaged。不过我不会开始回答这个问题的其他部分 - Stack Overflow旨在每个帖子上讨论单个问题。 - Jon Skeet
1
实际上,你应该使用 Aes.Create() 而不是 Rijndael.Create()(或者 new RijndaelManaged())。在块大小为 128 时,它们是相同的算法,但是 Rijndael 允许许多平台不支持的选项。(而且 Aes.Create() 将使用新的处理器指令,在可能的情况下使 AES 更快) - bartonjs

6
你每次只读取一个字节,这会产生很多开销。
为了加快处理速度,可以一次读取更多的字节或者调用内部复制函数:
fsIn.CopyTo(cs);

MSDN


2

每次读取一个字节是一个糟糕的想法。使用内置的Stream.CopyTo方法:

fsIn.CopyTo(cs);

请注意,从与密钥相同的材料中推导出初始化向量是不好的做法,可能会导致安全漏洞。在某些情况下,这甚至可以允许攻击者访问明文。您应该为每个加密操作随机生成一个初始化向量。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接