.NET CryptoStream读取超过Dispose()中密文结尾的内容并爆炸

.NET CryptoStream读取超过Dispose()中密文结尾的内容并爆炸,.net,encryption,padding,cryptostream,.net,Encryption,Padding,Cryptostream,我对.NETCryptoStream类的一个怪癖感到困惑:它的Dispose()方法读取密文的末尾,寻找它不应该填充的内容,结果抛出一个CryptographicException 下面的C#程序加密几个字节,调整密文数组的大小,以便在密文结束后有更多(无意义的)字节,然后尝试解密。要点是: 密文是8字节,一个3DES密码块。由于我只将6个字节写入加密流,并且它使用的是填充模式.PKCS7(默认设置),因此块中剩余的两个字节将填充值0x02 密文数组随后被调整为16字节,两个3DES块。第二个

我对.NET
CryptoStream
类的一个怪癖感到困惑:它的
Dispose()
方法读取密文的末尾,寻找它不应该填充的内容,结果抛出一个
CryptographicException

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

  • 密文是8字节,一个3DES密码块。由于我只将6个字节写入
    加密流
    ,并且它使用的是
    填充模式.PKCS7
    (默认设置),因此块中剩余的两个字节将填充值0x02
  • 密文数组随后被调整为16字节,两个3DES块。第二个块是未初始化的无意义,不是有效的密码输出
  • 解密时,我从
    加密流
    中准确读取了6个字节;我不是要求它解密到无意义的部分,我也不是依靠它识别填充来计算它何时到达纯文本的末尾
问题是,当调用解密
加密流
Dispose()
时(在
使用
块结束时自动),我会得到一个
加密异常
,消息为“坏数据”。它的堆栈跟踪显示它正在执行
CryptoStream.FlushFinalBlock()
,并且所有16个字节都已从
ciphertextStream
中消耗,而不仅仅是与实际加密数据对应的8个字节

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

既然我只从
加密流
中读取了足够的数据,需要对一个块进行解密,而该块是正确填充的最后一个块,然后我关闭
加密流
,不再读取,为什么流认为它需要读取另一个块并寻找更多填充?为什么它甚至试图消耗更多的输入作为其
Dispose()
的一部分


使用系统;
使用System.IO;
使用System.Linq;
使用System.Security.Cryptography;
名称空间测试
{
班级计划
{
静态void Main(字符串[]参数)
{
字节[]明文={0,1,2,3,4};
使用(SymmetricAlgorithm tripleDes=TripleDESCryptoServiceProvider.Create())
{
//加密明文
字节[]密文;
使用(MemoryStream ciphertextStream=new MemoryStream())
{
使用(ICryptoTransform encryptor=tripleDes.CreateEncryptor())
{
使用(CryptoStream CryptoStream=新加密流(ciphertextStream,encryptor,CryptoStreamMode.Write))
{
cryptoStream.WriteByte((字节)明文.Length);
cryptoStream.Write(明文,0,明文.长度);
cryptoStream.FlushFinalBlock();
}
}
ciphertext=ciphertextStream.ToArray();
}
//***在末尾添加一些非密文垃圾***
调整数组大小(参考密文,密文长度+8);
//现在再次解密它
字节[]解密明文;
使用(MemoryStream ciphertextStream=新的MemoryStream(ciphertext,false))
{
使用(ICryptoTransform decryptor=tripleDes.CreateDecryptor())
{
使用(CryptoStream CryptoStream=新的加密流(ciphertextStream、解密器、CryptoStreamMode.Read))
{
int length=cryptoStream.ReadByte();
解密明文=新字节[长度];
int i=0;
while(i
您故意将垃圾添加到流的末尾,然后想知道流为什么会被垃圾阻塞

在密码学中,必须非常仔细地检查所有内容,以确保攻击者不是在尝试偷偷摸摸的东西。如果指定PKCS7填充,则流可以在末尾检查PKCS7填充,如果在流末尾未找到正确的填充,则可以引发异常


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

如果我在读取一个块后关闭流,并且一个块被正确填充为最终块,它不应该知道吗?被垃圾呛死了
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));
            }
        }
    }
}