C#:AES 错误:填充无效且无法删除。相同的钥匙和一切,帮助

Posted

技术标签:

【中文标题】C#:AES 错误:填充无效且无法删除。相同的钥匙和一切,帮助【英文标题】:C#: AES error: Padding is invalid and cannot be removed. Same key and everything, help 【发布时间】:2011-07-02 02:29:30 【问题描述】:

我对 C# 很陌生,所以请耐心等待。我知道这个问题被问了很多次,但我找不到我的问题的答案。

我正在保存一些数据,在将其写入文件之前,我将其转换为二进制并将其存储在数组中,然后加密然后写入文件。我以块(32 个字节)加密数据。以同样的方式,我以 32 个字节的块读取数据,然后解密该数据,然后这应该重复直到文件末尾。但是在解密时会抛出以下错误:

填充无效,无法移除。

我使用相同的密钥和 iv(硬编码,直到我让它工作)

这是我的加密代码,没有问题:

        //result
        byte[] data = new byte[32];

        //setup encryption (AES)
        SymmetricAlgorithm aes = Aes.Create();
        byte[] key =  145, 12, 32, 245, 98, 132, 98, 214, 6, 77, 131, 44, 221, 3, 9,50;
        byte[] iv =  15, 122, 132, 5, 93, 198, 44, 31, 9, 39, 241, 49, 250, 188, 80, 7 ;
        ICryptoTransform encryptor = aes.CreateEncryptor(key, iv);

        FileStream fStream = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read, 1024, false);

        //prepare data to write (byte array 'data') ...

        //encrypt
               MemoryStream m = new MemoryStream();
               using (Stream c = new CryptoStream(m, encryptor, CryptoStreamMode.Write))
                   c.Write(data, 0, data.Length);
               data = m.ToArray();
               fStream.Write(data, 0, data.Length);

这是我的解密代码:

FileStream fStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false);

            //setup encryption (AES)
            SymmetricAlgorithm aes = Aes.Create();
            byte[] key =  145, 12, 32, 245, 98, 132, 98, 214, 6, 77, 131, 44, 221, 3, 9, 50 ;
            byte[] iv =  15, 122, 132, 5, 93, 198, 44, 31, 9, 39, 241, 49, 250, 188, 80, 7 ;
            ICryptoTransform decryptor = aes.CreateDecryptor(key, iv);

            //result
            byte[] data = new byte[32];

            //loop for reading the whole file ...
            int len = fStream.Read(data, 0, 32);

            //decrypt
                MemoryStream m = new MemoryStream();
                using (Stream c = new CryptoStream(m, decryptor, CryptoStreamMode.Write))
                    c.Write(data, 0, data.Length); //The exception is thrown in this line                  
                data = m.ToArray();

                //using the decrypted data and then looping back to reading and decrypting...

我尝试了所有我能想到的(这并不多,因为我对密码学很陌生),我到处搜索,但找不到解决问题的方法。我还帮助自己阅读C# in a Nutshell一书。

如果有人知道为什么会发生这种情况,我将非常感激,因为我没有任何想法。

感谢您的时间和回答。

编辑: 看来加密数据的大小是 48 字节(比原来多 12 字节)。为什么呢?我认为如果它们不是块大小的倍数(16 字节,我的数据是 32 字节),它只会添加字节。数据是否总是更大,并且不断增加(我需要知道这一点才能正确读取和解密)。

注意:我不能直接使用其他流,因为我需要控制输出格式,我相信在内存中加密也更安全、更快。

【问题讨论】:

我想Ben就在这里,你能检查一下文件以查看加密数据的输出(即二进制文件是否大于32字节?)。您需要读取整个文件并对其进行解密。 other @Ben:48 字节比您的输入多 16 字节,而不是 12。是什么阻止您仅使用加密流的长度?将长度保存到加密数据嵌入的任何结构/包装信封/pdu 中应该只需要几个额外的字节。 @SwDevMan81 我在加密后和写入文件之前检查了数组,它有 48 个字节大,是的,还有 16 个而不是 12 个 =)。我不能,因为有时文件会变得非常大,我只需要来自我将计算的位置的一个特殊数据块,因此我必须能够以类似块的方式解密文件。 @Ben Voigt 很好,如果它总是(对于相同的块大小)创建相同的大小“增量”,那么我可以适应,但我发现这些东西加起来很快就会浪费空间。 有道理。我想在这种情况下我会关闭填充,否则你将永远不知道从哪里读取。 【参考方案1】:

根据您的编辑:

编辑:似乎加密数据的大小为 48 个字节(比原始数据多 12 个字节)。为什么呢?我认为如果它们不是块大小的倍数(16 字节,我的数据是 32 字节),它只会添加字节。数据是否总是更大,并且不断增加(我需要知道这一点才能正确读取和解密)。

如果加密数据为 48 字节,则比原始数组大 16 字节。这是有道理的,因为算法会填充数据,因为默认值为PKCS7(即使大小与块大小匹配,因为它填充到块大小的 next 倍数)。如果您希望保持 32 个字节,只需将 Padding 更改为 None

aes.Padding = PaddingMode.None;

【讨论】:

非常感谢,这解决了我的问题。所以它总是填充到下一个块大小的倍数?因为“KeithS”发布:如果您的消息不是 16 字节的偶数倍...它必须是偶数吗?顺便说一句,它只是 aes.Padding = ... @Ben - 它将填充到下一个块大小,因此如果设置它总是会填充。我更新了代码,谢谢。很高兴这有帮助。 好吧,我好像忘记运行这个方法了,它似乎认为它正在工作,但实际上它不是,仍然是同样的问题。我也再次调试了一下,数据还是扩容到了48字节。 @Ben - 对我来说似乎没问题,当我复制你所拥有的并将 Padding 设置为 None 时,它​​会生成一个 32 字节的输出。确保在创建加密器之前设置填充。【参考方案2】:

您似乎将明文的长度视为密文的长度。这不是一个安全的假设。

为什么要在FileStreamMemoryStream之间进行复制,可以直接将FileStream传递给加密器/解密器。

在 PKCS7 中,至少有一个填充字节(用于存储填充字节的数量)。所以输出大小将是Ceil16(input.Length + 1),或(input.Length & ~15) + 1

【讨论】:

嗯,在他的算法中,他正在滚动他自己的正好 32 字节的字节数组,这是块大小的偶数倍,因此密文实际上将与输入字节数组完全匹配。问题是这样做,他做了自己的“填充”,这混淆了解密步骤。 @Keith:你绝对确定加密器不会生成额外的标头块或类似的东西? 那么如果我要创建一个 31 字节的数组,我可以在解密后传递 32 字节? 其他 @Ben:我预测是的,你可以。【参考方案3】:

简而言之,AES 将消息加密为 16 字节的块。如果您的消息不是 16 字节的偶数倍,则最后一个块的算法需要稍有不同;具体来说,最后一个块必须用算法已知的值作为填充值“填充”(通常为零,有时像空格字符值这样的其他值)。

您自己就是这样做的,将数据放入一个固定长度的字节数组中。您自己填充了数据,但解密器现在正尝试对最后一个块进行去填充,并获取它无法识别为其加密器对应方将添加的填充的字节值。

关键是不要填充消息。您可以使用 BitConverter 类将字节数组转换为 IConvertible 类型(值类型和字符串),然后使用它而不是滚动您自己的字节数组。然后,当您解密时,您可以从解密流中读取到密文长度,但不要期望解密结果中有那么多实际字节。

【讨论】:

以上是关于C#:AES 错误:填充无效且无法删除。相同的钥匙和一切,帮助的主要内容,如果未能解决你的问题,请参考以下文章

AES加密无效填充

WebResource.axd 上的“填充无效且无法删除”异常

AES 加密在 iOS 13.4 上无法正常工作

为啥密码错误会导致“填充无效,无法删除”?

C#(加密)Des很容易被破解吗?

C#中无效的Resx文件。