【问题标题】:Reading from a cryptostream to the end of the stream从加密流读取到流的末尾
【发布时间】:2012-03-15 17:03:16
【问题描述】:

我在使用下面的代码时遇到了一些问题。我在需要加密的临时位置有一个文件,此函数会加密该数据,然后将其存储在“pathToSave”位置。

经检查,似乎没有正确处理整个文件 - 我的输出中缺少一些位,我怀疑这与 while 循环没有在整个流中运行有关。

顺便说一句,如果我尝试在 while 循环之后调用 CryptStrm.Close(),我会收到一个异常。这意味着如果我尝试解密文件,我会收到文件已在使用错误!

尝试了所有通常的方法,并且我在这里查看了类似的问题,任何帮助都会很棒。

谢谢

public void EncryptFile(String tempPath, String pathToSave)
    {
        try
        {
            FileStream InputFile = new FileStream(tempPath, FileMode.Open, FileAccess.Read);
            FileStream OutputFile = new FileStream(pathToSave, FileMode.Create, FileAccess.Write);

            RijndaelManaged RijCrypto = new RijndaelManaged();

            //Key
            byte[] Key = new byte[32] { ... };

            //Initialisation Vector
            byte[] IV = new byte[32] { ... };

            RijCrypto.Padding = PaddingMode.None;
            RijCrypto.KeySize = 256;
            RijCrypto.BlockSize = 256;
            RijCrypto.Key = Key;
            RijCrypto.IV = IV;

            ICryptoTransform Encryptor = RijCrypto.CreateEncryptor(Key, IV);

            CryptoStream CryptStrm = new CryptoStream(OutputFile, Encryptor, CryptoStreamMode.Write);

            int data;
            while (-1 != (data = InputFile.ReadByte()))
            {
                CryptStrm.WriteByte((byte)data);
            }
        }
        catch (Exception EncEx)
        {
            throw new Exception("Encoding Error: " + EncEx.Message);
        }
    }

编辑:

我假设我的问题与加密有关。我的解密可能是罪魁祸首

        public String DecryptFile(String encryptedFilePath)
    {
        FileStream InputFile = new FileStream(encryptedFilePath, FileMode.Open, FileAccess.Read);

        RijndaelManaged RijCrypto = new RijndaelManaged();

        //Key
        byte[] Key = new byte[32] { ... };

        //Initialisation Vector
        byte[] IV = new byte[32] { ... };

        RijCrypto.Padding = PaddingMode.None;
        RijCrypto.KeySize = 256;
        RijCrypto.BlockSize = 256;
        RijCrypto.Key = Key;
        RijCrypto.IV = IV;

        ICryptoTransform Decryptor = RijCrypto.CreateDecryptor(Key, IV);

        CryptoStream CryptStrm = new CryptoStream(InputFile, Decryptor, CryptoStreamMode.Read);

        String OutputFilePath = Path.GetTempPath() + "myfile.name";
        StreamWriter OutputFile = new StreamWriter(OutputFilePath);

        OutputFile.Write(new StreamReader(CryptStrm).ReadToEnd());

        CryptStrm.Close();
        OutputFile.Close();

        return OutputFilePath;
    }

【问题讨论】:

    标签: c# encryption cryptography aes rijndaelmanaged


    【解决方案1】:

    嗯,这里有几件事出了问题。

    1。您将 IV 的大小设置得太大。如果您的密钥大小为 256,则 Rijndael 的 IV 为 16 个字节。如果您尝试使用比这更多的字节设置密钥,则会出现异常。 IV 大小应设置为 Block size / 8 see MSDN,因此您拥有的 IV 大小是正确的。

    1. 您的填充模式为无。除非您的数据是完全正确的块大小,否则您将遇到问题。您可能想选择一种填充模式。
    2. 您通常需要在完成加密流的写入后调用 FlushFinalBlock,以确保所有数据都写入流中。

    我正在发布一些使用内存流的示例代码来显示加密和解密。

    var tempData = "This is the text to encrypt. It's not much, but it's all there is.";
    
    using (var rijCrypto = new RijndaelManaged())
    {
        byte[] encryptedData;
        rijCrypto.Padding = System.Security.Cryptography.PaddingMode.ISO10126;
        rijCrypto.KeySize = 256;
    
        using (var input = new MemoryStream(Encoding.Unicode.GetBytes(tempData)))
        using (var output = new MemoryStream())
        {
            var encryptor = rijCrypto.CreateEncryptor();
    
            using (var cryptStream = new CryptoStream(output, encryptor, CryptoStreamMode.Write))
            {
                var buffer = new byte[1024];
                var read = input.Read(buffer, 0, buffer.Length);
                while (read > 0)
                {
                    cryptStream.Write(buffer, 0, read);
                    read = input.Read(buffer, 0, buffer.Length);
                }
                cryptStream.FlushFinalBlock();
                encryptedData = output.ToArray();
            }
        }
    
        using (var input = new MemoryStream(encryptedData))
        using (var output = new MemoryStream())
        {
            var decryptor = rijCrypto.CreateDecryptor();
            using (var cryptStream = new CryptoStream(input, decryptor, CryptoStreamMode.Read))
            {
                var buffer = new byte[1024];
                var read = cryptStream.Read(buffer, 0, buffer.Length);
                while (read > 0)
                {
                     output.Write(buffer, 0, read);
                     read = cryptStream.Read(buffer, 0, buffer.Length);
                }
                cryptStream.Flush();
                var result = Encoding.Unicode.GetString(output.ToArray());
            }
        }
    }
    

    【讨论】:

    • 感谢 pstrjds - 我做了一些更改,但仍有问题。 IV 现在是 16 个字节,填充是零,我已经改变了我的 while 循环,看起来更像你的建议,但是当我调用 FlushFinalBlock 时我仍然遇到错误 - “方法在 CryptoStream 上被调用了两次。它只能被调用一次” .我不相信它一开始就被调用了!
    • 嗯,你应该只在循环外调用 FlushFinalBlock 作为加密时的最后一件事。解密时不需要调用它,解密时调用Flush即可。解密时您的块大小应该是正确的,因此不需要添加任何填充。至于信任是否已被调用,您可以在调试器下运行代码并在该行设置断点。如果它没有击中它,那么它就没有被调用。
    • 如果您的密钥大小为 256,则 Rijndael 的 IV 为 16 个字节。如果您尝试使用比这更多的字节设置密钥,则会出现异常。 这是不正确的,如果您有 BlockSize = 256,那么您的 IV 应该是 32。如果它们不正确匹配,那么您将得到一个例外。
    • @RichardGriffiths - 我有 answered 你的问题与工作样本。
    • @DomBat - Ideone link 到一个工作示例,您可以在其中使用单独的 RijndaelManaged 对象进行加密和解密(从加密中保存 IV 和密钥,并使用保存的 IV 和密钥进行解密)。没有填充异常,一切正常。我选择 ISO10126 是因为我更喜欢用随机数据填充。
    【解决方案2】:

    另一种读取 CryptoStream 的方法是使用 CopyTo 方法:

    ...
    
        using (var output = new MemoryStream())
        {
           cryptStream.CopyTo(output);
           return output.ToArray();
        }
    
    ...
    

    【讨论】:

      【解决方案3】:

      必须首先指定 KeySize。这看起来像一个错误,例如这行得通

                  using (var aes = new AesManaged())
                  {
                      aes.KeySize = 256;
                      aes.Mode = CipherMode.CBC;
                      aes.IV = iv;
                      aes.Key = passwordHash;
                      aes.Padding = PaddingMode.PKCS7;
      

      但这不是

                  using (var aes = new AesManaged())
                  {
                      aes.Mode = CipherMode.CBC;
                      aes.IV = iv;
                      aes.Key = passwordHash;
                      aes.Padding = PaddingMode.PKCS7;
                      aes.KeySize = 256;
      

      【讨论】:

      • 这根本没有解决 OP 的问题。
      猜你喜欢
      • 2014-09-09
      • 2012-12-28
      • 1970-01-01
      • 1970-01-01
      • 2018-08-05
      • 1970-01-01
      • 1970-01-01
      • 2013-06-05
      • 1970-01-01
      相关资源
      最近更新 更多