. net CryptoStream在Dispose()中读取密文的末尾并爆炸



我对。net CryptoStream类的奇怪之处感到困惑:它的Dispose()方法读取了密文的末尾,寻找它不应该填充的内容,并抛出CryprographicException作为结果。

下面的c#程序加密几个字节,调整密文数组的大小,以便在密文结束后有更多(无意义的)字节,然后尝试解密它。重点是:

  • 密文长度为8字节,一个3DES密码块。因为我只写6个字节到CryptoStream,它使用PaddingMode.PKCS7(默认值),块中剩余的两个字节填充填充值0x02。
  • 密文数组随后被调整为16字节,两个3DES块。第二个块是未初始化的无意义内容,不是有效的密码输出。
  • 解密时,我从CryptoStream读取正好6个字节;我是而不是要求它解密到无意义的部分,我是而不是依靠它识别填充来弄清楚它何时到达明文的末尾。

问题是,当解密CryptoStreamDispose()被调用时(自动在using块的末尾),我得到一个带有消息"坏数据"的CryptographicException。它的堆栈跟踪显示它正在执行CryptoStream.FlushFinalBlock(),并且ciphertextStream中的所有16个字节都被消耗了,而不仅仅是对应于实际加密数据的8个字节。

如果我删除调整ciphertext数组大小的行,程序将正常工作。如果我在解密之前执行tripleDes.Padding = PaddingMode.None,程序也可以正常工作-但这基本上使填充字节成为明文的一部分,所以我宁愿不这样做。显然,这个问题与填充物有关;据我所知,它解密了第二个块,并期望在它的末尾找到有效的PKCS7样式的填充。

由于我只从CryptoStream中读取足够的内容以要求解密一个块,并且该块是一个正确填充的最终块,然后我关闭CryptoStream而不读取任何内容,为什么流认为它需要读取另一个块并寻找更多填充?为什么它甚至试图消耗更多的输入作为其Dispose()的一部分?


using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            byte[] plaintext = { 0, 1, 2, 3, 4 };
            using (SymmetricAlgorithm tripleDes = TripleDESCryptoServiceProvider.Create())
            {
                // Encrypt the plaintext
                byte[] ciphertext;
                using (MemoryStream ciphertextStream = new MemoryStream())
                {
                    using (ICryptoTransform encryptor = tripleDes.CreateEncryptor())
                    {
                        using (CryptoStream cryptoStream = new CryptoStream(ciphertextStream, encryptor, CryptoStreamMode.Write))
                        {
                            cryptoStream.WriteByte((byte)plaintext.Length);
                            cryptoStream.Write(plaintext, 0, plaintext.Length);
                            cryptoStream.FlushFinalBlock();
                        }
                    }
                    ciphertext = ciphertextStream.ToArray();
                }
                // *** Add some non-ciphertext garbage to the end ***
                Array.Resize(ref ciphertext, ciphertext.Length + 8);
                // Now decrypt it again
                byte[] decryptedPlaintext;
                using (MemoryStream ciphertextStream = new MemoryStream(ciphertext, false))
                {
                    using (ICryptoTransform decryptor = tripleDes.CreateDecryptor())
                    {
                        using (CryptoStream cryptoStream = new CryptoStream(ciphertextStream, decryptor, CryptoStreamMode.Read))
                        {
                            int length = cryptoStream.ReadByte();
                            decryptedPlaintext = new byte[length];
                            int i = 0;
                            while (i < length)
                            {
                                int bytesRead = cryptoStream.Read(decryptedPlaintext, i, (length - i));
                                if (bytesRead == 0) break;
                                else i += bytesRead;
                            }
                        }  // CryptographicException: "Bad Data"
                    }
                }
                System.Diagnostics.Debug.Assert(decryptedPlaintext.SequenceEqual(plaintext));
            }
        }
    }
}

你故意在流的末尾添加垃圾,然后想知道为什么流阻塞在垃圾上。

在密码学中,所有都必须非常仔细地检查,以确保攻击者没有尝试偷偷摸摸的东西。如果指定PKCS7填充,则流有权在流的末尾检查PKCS7填充,如果在流的末尾没有找到正确的填充,则有权抛出异常。

流无法知道实际的密文在流的中间结束,而不是在流的末尾。你怎么能指望它知道?在加密中,规则是标记任何和所有异常,文档会告诉您流末尾的错误填充会导致异常。

相关内容

  • 没有找到相关文章

最新更新