【问题标题】:Using StreamWriter to write in a Stream (for large Stream encryption)使用 StreamWriter 写入 Stream(用于大 Stream 加密)
【发布时间】:2015-03-13 04:59:18
【问题描述】:

我关注MSDN Example of Rijndael Encryption,只是我想加密并返回一个流。

以下不起作用。

没有抛出异常,但是单步执行代码后,返回值没有数据。

        public static Stream EncryptStream(Stream plainStream, byte[] Key, byte[] IV)
        {

            var encrypted = new MemoryStream()

            // Create an RijndaelManaged object 
            // with the specified key and IV. 
            using (RijndaelManaged rijAlg = new RijndaelManaged())
            {
                rijAlg.Key = Key;
                rijAlg.IV = IV;

                // Create a decrytor to perform the stream transform.
                ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

                // Create the streams used for encryption. 
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {

                            //Write all data to the stream.
                            swEncrypt.Write(plainStream);
                        }
                        msEncrypt.CopyTo(encrypted);
                    }
                }
            }
            return encrypted;


        }

我查看了documentation for the Stream.Writer class,认为它与不支持写入流有关。

我注意到有一个“对象”类型参数,所以我假设它会起作用……对吗?如果没有,我该怎么做?

顺便说一句,我将 FileStream 传递给它。单步执行代码,plainStream 确实包含数据。

【问题讨论】:

  • @AlexeiLevenkov 我没有问过“设置位置”。如果这就是解决方案所在,我不可能知道它与“设置位置”有关
  • @AlexeiLevenkov,它实际上并没有解决问题。如果您忽略 CopyTo() 调用并查看 msEncrypt(一个 Stream),它不包含数据。试试看。
  • @user919426 如果你在处理完csEncrypt 之后执行msEncrypt.CopyTo(encrypted); ,仍然会给你带来问题吗?
  • “plainStream.CopyTo(swEncrypt);”怎么样?告诉我这是否有效。
  • 旁注 - 我不相信“它不会引发异常”,因为您正在关闭流而不是尝试复制。

标签: c# encryption stream rijndaelmanaged


【解决方案1】:

以下是一些用于对流进行加密和解密的示例函数(将算法替换为您喜欢的算法):

public static void Decrypt(Stream input, Stream output, byte[] key, byte[] iv)
{
    using (SymmetricAlgorithm algo = SymmetricAlgorithm.Create()) // Creates the default implementation, which is RijndaelManaged. 
    {
        using (CryptoStream stream = new CryptoStream(input, algo.CreateDecryptor(key, iv), CryptoStreamMode.Read))
        {
            byte[] bytes = new byte[16];
            int read;
            do
            {
                read = stream.Read(bytes, 0, bytes.Length);
                output.Write(bytes, 0, read);
            }
            while (read > 0);
        }
    }
}

public static void Encrypt(Stream input, Stream output, byte[] key, byte[] iv)
{
    using (SymmetricAlgorithm algo = SymmetricAlgorithm.Create()) //Creates the default implementation, which is RijndaelManaged. 
    {
        using (CryptoStream stream = new CryptoStream(output, algo.CreateEncryptor(key, iv), CryptoStreamMode.Write))
        {
            byte[] bytes = new byte[16];
            int read;
            do
            {
                read = input.Read(bytes, 0, bytes.Length);
                stream.Write(bytes, 0, read);
            }
            while (read > 0);
        }
    }
}

您可以将它们与任何输出流一起使用。如果你想写入一个大的输出流,你可以直接使用那个输出流(例如 FileStream 或 ASP.NET Response.OutputStream 等),你不应该使用中间的 MemoryStream ,它会消耗内存而没有真正的目的.

话虽如此,如果你真的想使用 MemoryStream,你会这样做:

MemoryStream output = new MemoryStream();
Encrypt(input, output, key, iv);
output.Position = 0; // rewind the stream, so you can use it from the beginning

【讨论】:

  • 谢谢西蒙。我有一个简短的问题,为什么你选择了一个我认为是缓冲区的东西(字节 [] 字节 = 字节 [16])这与我的实际数据缓冲区长度有什么关系吗?
  • 哦,这是我从一段旧代码中继承下来的。实际上,您可以/应该设置更高的值以获得更好的性能(例如,如果您的流很大,则为 65536)。确保它不大于 85K (stackoverflow.com/questions/8951836/…)
  • 遇到using (CryptoStream 的处理大括号时抛出异常。它说“要加密的数据长度无效。”会尝试让它工作
  • 我删除了 CryptoStream 的 using 语句,因为它导致流被关闭。现在工作!
  • 是的,CryptoStream 有一些微妙之处……例如:blogs.msdn.com/b/winsdk/archive/2009/11/13/…
【解决方案2】:

使用流级复制将一个流的内容复制到另一个或使用相应的读取器/写入器对(如TextReader/TextWriter) - 如果您混合使用,则很可能会得到错误的结果。 IE。流级副本:

 plainStream.CopyTo(csEncrypt);

除了将数据实际写入加密流(而不是由于StreamWrite.Write(Object) 调用而获得的plainStream 的类型名称)之外,您还应该使用MemoryStream.ToArray 来复制结果内容 - 否则您将获得“对象已处置”例外”。

复制代码应如下所示,而不是 msEncrypt.CopyTo(encrypted);

var bytes = msEncrypt.ToArray();
encrypted.Write(bytes, 0, bytes.Length);

【讨论】:

  • 感谢您的详细解释。由于实施细节,将西蒙的答案标记为答案,但没有影响您的良好解释
猜你喜欢
  • 2020-06-03
  • 1970-01-01
  • 2019-09-07
  • 1970-01-01
  • 2012-12-15
  • 2018-07-13
  • 2011-01-15
  • 1970-01-01
  • 2015-02-27
相关资源
最近更新 更多