【问题标题】:Multithreading encryption in C#C#中的多线程加密
【发布时间】:2017-06-03 17:01:57
【问题描述】:

我是加密新手,正在使用以下方法加密文件:

private static void encryptFile(string filePath, byte[] password, byte[] salt)
{
    Rfc2898DeriveBytes rdb = new Rfc2898DeriveBytes(password, salt, 1000);
    AesManaged algorithm = new AesManaged();

    byte[] rgbKey = rdb.GetBytes(algorithm.KeySize / 8);
    byte[] rgbIV = rdb.GetBytes(algorithm.BlockSize / 8);
    GCHandle keyHandle = GCHandle.Alloc(rgbKey, GCHandleType.Pinned);
    GCHandle IVHandle = GCHandle.Alloc(rgbIV, GCHandleType.Pinned);

    ICryptoTransform cryptoAlgorithm = algorithm.CreateEncryptor(rgbKey, rgbIV);

    using (FileStream readStream = File.Open(filePath, FileMode.Open))
    {
        using (FileStream writeStream = new FileStream(filePath + ".enc", FileMode.Create, FileAccess.Write))
        {
            using (CryptoStream cryptoStream = new CryptoStream(writeStream, cryptoAlgorithm, CryptoStreamMode.Write))
            {
                while (readStream.Position < readStream.Length)
                {
                    byte[] buffer = new byte[4096];
                    int amountRead = readStream.Read(buffer, 0, buffer.Length);
                    cryptoStream.Write(buffer, 0, amountRead);
                }
                cryptoStream.Flush();
            }
        }
    }

    UtilityMethods.destroyBytes(rgbKey);
    UtilityMethods.destroyBytes(rgbIV);
    keyHandle.Free();
    IVHandle.Free();
}

我想做的是多线程处理以加快加密速度。使用单线程,加密一个约 3GB 的文件需要 5 分钟。如果可能的话,我希望能够在 1 分钟内完成加密(在 30 秒内会很棒,但我想我可能会延长)。

我相信答案是创建多个流(虽然我不确定),为每个流分配一个文件块进行加密,但我不确定如何“将文件分开”以分配一个块到每个流,或在每个部分通过分配给它的流之后“将文件重新组合在一起”。有人能指出我正确的方向吗?

非常感谢!

附:我看过这个(Rijndael algorithm and CryptoStream: is it possible to encrypt / decrypt multithreaded?),但我不明白答案(ECB,CBC?)。如果我的问题的答案就在那里,您能否提供一些示例代码让我朝着正确的方向前进?

再次感谢!

【问题讨论】:

  • 并行/多线程加密只能通过 ECB 实现,否则单个块相互依赖。关于 ECB、CBC:请参阅 Encryption operating modes: ECB vs CBCCipherMode Enumeration。另请注意有关 ECB 的评论:“重要提示:不建议使用此模式,因为它为多种安全漏洞打开了大门。”
  • 这可能是 I/O 问题而不是 CPU 问题。在不同的物理磁盘或 SSD 上尝试输入和输出相同的代码,您会看到更好的性能。

标签: c# multithreading encryption


【解决方案1】:

CBC(密码块链接)是 AesManaged 的​​默认块模式。

如果您使用 CBC,则无法使用 AES 并行加密不同的块,因为加密块 1 的副作用是为块 2 设置新的 IV。这称为“IV 反馈”。如果您并行运行,这将不起作用。你需要选择一个不同的cipher block mode

您可以使用 ECB 模式(没有 IV 反馈),但如果这样做,以明文形式重复的块也会以密文形式重复,这使您面临某些类型的攻击。

最好使用 CTR,其中 IV 基于计数器,但可惜 .NET 似乎不支持它。

我建议您将单个文件分成几个“虚拟”文件,每个文件都有自己的密码流。使用 CBC 并用来自RNGCryptoServiceProvider 的字节填充 IV。使用从物理文件中的虚拟文件索引派生的值来播种 RNGCryptoServiceProvider。这样一来,您的 IV 会因块而异,并且重复的明文不会出现在密文中。解密时重复该过程。

另见this question

我建议您在准备好后将您的解决方案发布到security.stackexchange.com 以供审核。

【讨论】:

  • 我同意你的回答,但我不确定我是否同意你的解决方案。您不认为自己实施 CTR 模式(相对简单)会比您建议的方法更好吗?对您的意见感兴趣。
  • 如果性能是我们要解决的问题,在 c# 中实现数学密集型加密可能没有帮助。我建议的解决方案应该没问题,或者至少不会比一遍又一遍地使用 CBC 更糟。但是你是对的,滚动自己的安全是一件危险的事情。可能最好的答案是购买第三方 CTR 实施并使用它。
  • CTR 数学密集程度如何? Is 不使用比 CBC 模式更多的异或操作。还是你说的是编译 C# 本身的速度?
  • c# 本身的速度。 .NET 加密库调用非托管计算函数,其执行速度比 IL 快得多。此外,如果通过 WIn32 Crypto API 安装,他们可以利用专用的加密硬件。如果你自己动手,你什么都做不了,除非你真的很投入并且不介意编写 C++。
  • 有趣的是,可能存在显着差异。我可能会写一个我认为的例子。
【解决方案2】:

您需要将 Stream.Seek 用于您的读取流。看看这个问题: StreamReader and seeking 帮助您避免常见的陷阱。

请记住,您必须准确记住每个流的开始和停止位置(尤其是加密流的长度),否则您将无法解密文件切片。

祝你好运!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-01
    • 2010-12-14
    • 1970-01-01
    • 2011-06-02
    • 2016-07-04
    相关资源
    最近更新 更多