【问题标题】:AES decrypt fails with "Padding is invalid and cannot be removed"AES 解密失败并显示“填充无效且无法删除”
【发布时间】:2016-02-02 16:24:33
【问题描述】:

我正在制作跨平台应用程序,服务器是用 Java 编写的,所以我最终选择了适用于 Windows 的 C#。 我终于克服了这两种语言(Java:big endian,C#:little)中BigIntegers的不同字节序等所有障碍,所以我也成功地进行了密钥交换。 现在这就是问题所在,在 Java 中我使用 AES/CBC/PKCS5Padding 进行加密,但在 C# 中没有可用的 PKCS5,但正如我在 SO 上的其他帖子中读到的关于此主题的其他帖子一样,PKCS7 被告知与PKCS5 或 Java 真正使用 PKCS7 而不是 PKCS5。我不知道什么是正确的。

所以这是代码:

using (System.Security.Cryptography.RijndaelManaged rijndael = new System.Security.Cryptography.RijndaelManaged())
{
    byte[] iv = new byte[16];
    for (int i = 0; i < 16; i++)
        iv[i] = 0;
    rijndael.Padding = PaddingMode.PKCS7;
    rijndael.Mode = CipherMode.CBC;
    rijndael.Key = Sys.PrivateKey;
    rijndael.KeySize = 128;
    rijndael.BlockSize = 128;
    rijndael.IV = iv;
    Sys.LogWrite("Decrypt input bytes length: " + buff.Length + ", keyLength: " + Sys.PrivateKey.Length);
    Sys.LogWriteBuffer("Input bytes", buff);
    Sys.LogWriteBuffer("Input key", Sys.PrivateKey);
    ICryptoTransform decryptor = rijndael.CreateDecryptor(rijndael.Key, rijndael.IV);
    buff=rijndael.CreateDecryptor().TransformFinalBlock(buff, 0, buff.Length);
    Sys.LogWriteBuffer("buffer: ", buff);
}

请忽略 IV 始终为 00 00 00 00,这仅用于测试目的。 数据始终来自服务器,密钥在密钥交换中生成:

Decrypt input bytes length: 32, keyLength: 16
Input bytes: 7C 25 3F 49 9E D5 51 67 E2 86 F9 86 2E C1 8F 22 70 51 65 74 FC 39 2C 52 A6 83 36 B5 9A C7 27 B9 
Input key: 1C 13 C2 33 50 57 00 DB FD 60 E2 1C 27 2A A5 00 

如果我把它放到像这样http://aes.online-domain-tools.com/ 的任何在线 AES 解密器上,我总是得到正确的结果:[[27,{"Established":true}]] 但在 C# 中我只得到这个:

System.Security.Cryptography.CryptographicException: Padding is invalid and cannot be removed.
   at System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]& outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast)
   at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)

我什至尝试过 PaddingMode.None,它没有抛出任何异常,但输出只是随机字节。 即使在线解密器也能正确解决,我做错了什么?

应该产生相同结果的其他示例输入数据[[27,{"Established":true}]]

Decrypt input bytes length: 32, keyLength: 16
Input bytes: 85 5C 55 24 44 B8 77 A5 EF CE E7 A1 45 EC F3 84 2F 8B 74 1F AB D9 BE D0 82 64 BC 0D B0 50 73 63 
Input key: B2 21 FA 17 63 E6 4C 25 48 03 84 64 8B 70 05 00 

Decrypt input bytes length: 32, keyLength: 16
Input bytes: 8B FC 47 B4 91 05 B7 E1 6C 0E 61 78 D2 51 6B 77 EF 80 30 49 37 05 DA 79 47 52 D1 24 B9 DE A7 F3 
Input key: E7 BF E0 AA AC F1 26 42 06 D6 59 44 F9 33 74 63 

注意 IV 总是 16 个空字节。

【问题讨论】:

  • 我认为您应该尝试像AES/CBC/PKCS5Padding Encryption/Decryption in C# 这样的有效解决方案,看看您做了什么不同
  • 不,这行不通。没有字符串,只有从 TCP Recv 解析的字节流byte buff[]。如上面给出的示例(Input bytes, Input key),输入数据是绝对有效的,即使在上面提供的在线 AES 解密器和 Java 客户端也可以解密它们。
  • 解决了。就是不能接受自己的答案-_-
  • 我个人会尝试使用streaming API。只需调用TransformFinalBlock 可能会起作用,但请阅读 API 规范:“TransformFinalBlock 是一个特殊函数,用于转换流中的最后一个块或部分块。它返回一个包含剩余转换字节的新数组。返回一个新数组,因为在添加填充时,最后返回的信息量可能大于单个块。”。仅使用这种方法是黑客行为,而不是编码。
  • 你说,“注意 IV 总是 16 个空字节。”这是不安全的,实际上意味着您的第一个块是在 ECB 模式下加密的,而不是 CBC 模式。为了安全起见,每条消息的 IV 必须是随机的且不同。

标签: c# cryptography aes


【解决方案1】:

我终于找到了问题所在,难以置信! 而不是以这种方式创建解密器:

ICryptoTransform decryptor = rijndael.CreateDecryptor(rijndael.Key, rijndael.IV);

我应该这样创建它:

ICryptoTransform decryptor = rijndael.CreateDecryptor(Sys.PrivateKey, iv);

所以如果以后有人遇到类似的问题,这里已经是通用的功能了:

        public static String Decrypt(byte[] buff, byte[] key, byte[] iv)
        {
            using (System.Security.Cryptography.RijndaelManaged rijndael = new System.Security.Cryptography.RijndaelManaged())
            {
                rijndael.Padding = PaddingMode.PKCS7;
                rijndael.Mode = CipherMode.CBC;
                rijndael.KeySize = 128;
                rijndael.BlockSize = 128;
                ICryptoTransform decryptor = rijndael.CreateDecryptor(key, iv);
                System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(buff);
                CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
                byte[] output = new byte[buff.Length];
                int readBytes = cryptoStream.Read(output, 0, output.Length);
                return System.Text.Encoding.UTF8.GetString(output, 0, readBytes);
            }
        }

希望对你有帮助。

【讨论】:

  • 您可能只需要调用CreateDecryptor() 就可以了,因为您已经在rijndael 对象上设置了密钥和iv。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-07-18
  • 2018-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-15
相关资源
最近更新 更多