【问题标题】:"Padding is invalid and cannot be removed" using AesManaged使用 AesManaged 的​​“填充无效且无法移除”
【发布时间】:2010-10-10 21:24:39
【问题描述】:

我正在尝试使用 AesManaged 进行简单的加密/解密,但在尝试关闭解密流时我不断收到异常。这里的字符串被正确加密和解​​密,然后在 Console.WriteLine 打印正确的字符串后得到 CryptographicException "Padding was invalid and cannot be removed"。

有什么想法吗?

MemoryStream ms = new MemoryStream();
byte[] rawPlaintext = Encoding.Unicode.GetBytes("This is annoying!");

using (Aes aes = new AesManaged())
{
  aes.Padding = PaddingMode.PKCS7;
  aes.Key = new byte[128/8];
  aes.IV = new byte[128/8];

  using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(),
                                            CryptoStreamMode.Write))
  {
    cs.Write(rawPlaintext, 0, rawPlaintext.Length);
    cs.FlushFinalBlock();
  }

  ms = new MemoryStream(ms.GetBuffer());
  using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(),
                                            CryptoStreamMode.Read))
  {
    byte[] rawData = new byte[rawPlaintext.Length];
    int len = cs.Read(rawData, 0, rawPlaintext.Length);
    string s = Encoding.Unicode.GetString(rawData);
    Console.WriteLine(s);
  }
}

【问题讨论】:

    标签: c# .net encryption


    【解决方案1】:

    诀窍是使用MemoryStream.ToArray()。 我还更改了您的代码,使其在加密和解密中都使用CryptoStream 来写入。而且您不需要显式调用CryptoStream.FlushFinalBlock(),因为您在using() 语句中拥有它,并且刷新将在Dispose() 上发生。以下对我有用。

    byte[] rawPlaintext = System.Text.Encoding.Unicode.GetBytes("This is all clear now!");
    
    using (Aes aes = new AesManaged())
    {
        aes.Padding = PaddingMode.PKCS7;
        aes.KeySize = 128;          // in bits
        aes.Key = new byte[128/8];  // 16 bytes for 128 bit encryption
        aes.IV = new byte[128/8];   // AES needs a 16-byte IV
        // Should set Key and IV here.  Good approach: derive them from 
        // a password via Cryptography.Rfc2898DeriveBytes 
        byte[] cipherText= null;
        byte[] plainText= null;
    
        using (MemoryStream ms = new MemoryStream())
        {
            using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
            {
                cs.Write(rawPlaintext, 0, rawPlaintext.Length);
            }
    
            cipherText= ms.ToArray();
        }
    
    
        using (MemoryStream ms = new MemoryStream())
        {
            using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
            {
                cs.Write(cipherText, 0, cipherText.Length);
            }
    
            plainText = ms.ToArray();
        }
        string s = System.Text.Encoding.Unicode.GetString(plainText);
        Console.WriteLine(s);
    }
    

    另外,我猜你知道你会想要显式设置 AesManaged 实例的 Mode,并使用 System.Security.Cryptography.Rfc2898DeriveBytes 从密码和盐中派生密钥和 IV。

    另见:
    - AesManaged

    【讨论】:

    • 我有同样的问题,但使用 RijndaelManaged(也是对称的)并且不知道发生了什么。原来MemoryStream.GetBuffer() 得到了一个 unflushed 版本的数据,而最后的大部分数据块都是空的,这弄乱了我的填充。 MemoryStream.ToArray() 获取真正的数组。非常感谢这个解决方案!
    • 这是迄今为止我见过的最好/最小的实现。
    • 请致电Dispose。在处理 CryptoStream 之前,我打电话给 ms.ToArray()。将那条线移到 using 之外为我修复了它。
    • 哦,我的日子,一百万谢谢@cheeso!在解密和 memoryStream.ToArray() 时写入加密流......再次感谢一百万!这结束了 10 小时的拉头发!
    【解决方案2】:

    此异常可能由多个加密参数中的任何一个不匹配引起。

    我使用Security.Cryptography.Debug 接口来跟踪加密/解密方法中使用的所有参数。

    最后我发现我的问题是我在设置Key 之后设置了KeySize 属性,导致类重新生成一个随机密钥,而不是使用我最初设置的密钥。

    【讨论】:

    • 我的问题也是在设置密钥后设置KeySize,谢谢!
    • +1 非常感谢,如果可以的话,我会投票给你 100 次。我花了一天多的时间试图弄清楚为什么我无法正确解密,结果发现我是在代码中的密钥之后设置密钥大小。
    • Security.Cryptography.Debug 是否仍然是最新的?自 2010 年以来似乎没有任何变化
    【解决方案3】:

    为了它的价值,我会记录我所面临的。我试图在 CryptoStream 关​​闭之前读取加密器内存流。我太天真了,浪费了一天时间调试它。

        public static byte[] Encrypt(byte[] buffer, byte[] sessionKey, out byte[] iv)
        {
            byte[] encrypted;
            iv = null;
            using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 })
            {
                aesAlg.Key = sessionKey;
                iv = aesAlg.IV;
                ICryptoTransform encryptor = aesAlg.CreateEncryptor(sessionKey, iv);
    
                // Create the streams used for encryption.
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        csEncrypt.Write(buffer, 0, buffer.Length);
                        
                        //This was not closing the cryptostream and only worked if I called FlushFinalBlock()
                        //encrypted = msEncrypt.ToArray(); 
                    }
    
                    encrypted = msEncrypt.ToArray();
    
                    return encrypted;
                }
            }
        }
    

    在 cypto 流关闭后移动读取的加密器内存流解决了该问题。正如 Cheeso 提到的。如果您使用的是using 块,则无需调用FlushFinalBlock()

    【讨论】:

    • 哇,太棒了!!!,你拯救了我的一天,大约七个小时试图弄清楚......真的,我不能感谢你
    【解决方案4】:

    byte[] rawData = new 字节[rawPlaintext.Length];

    您需要读取缓冲区的长度,这可能包括必要的填充(IIRC,已经有几年了)。

    【讨论】:

      【解决方案5】:

      没有人回答,实际上 MemoryStream.GetBuffer 返回分配的缓冲区,而不是此缓冲区中的真实数据。在这种情况下,它返回 256 字节的缓冲区,而它只包含 32 字节的加密数据。

      【讨论】:

        【解决方案6】:

        正如其他人所提到的,如果未正确初始化密钥/iv 以进行解密,则可能会发生此错误。就我而言,我需要从一些更大的缓冲区中复制 key 和 iv 。这是我做错的:

        不起作用:(填充无效,无法移除)

        aes.Key = new byte[keySize];
        Buffer.BlockCopy(someBuffer, keyOffset, aes.Key, 0, keySize);
        
        aes.IV = new byte[ivSize];
        Buffer.BlockCopy(someBuffer, ivOffset, aes.IV, 0, ivSize);
        

        作品:

        var key = new byte[keySize];
        Buffer.BlockCopy(someBuffer, keyOffset, key, 0, keySize);
        aes.Key = key;
        
        var iv = new byte[ivSize];
        Buffer.BlockCopy(someBuffer, ivOffset, iv, 0, ivSize);
        aes.IV = iv;
        

        OP 没有犯此错误,但这可能对其他看到相同错误的人有所帮助。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-07-26
          • 1970-01-01
          • 1970-01-01
          • 2014-05-16
          相关资源
          最近更新 更多