C# “填充无效,无法删除”使用 AesManaged

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/604210/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 10:00:37  来源:igfitidea点击:

"Padding is invalid and cannot be removed" using AesManaged

c#.netencryption

提问by TimK

I'm trying to get simple encryption/decryption working with AesManaged, but I keep getting an exception when trying to close the decryption stream. The string here gets encrypted and decrypted correctly, and then I get the CryptographicException "Padding was invalid and cannot be removed" after Console.WriteLine prints the correct string.

我正在尝试使用 AesManaged 进行简单的加密/解密,但是在尝试关闭解密流时我不断收到异常。此处的字符串已正确加密和解​​密,然后在 Console.WriteLine 打印正确的字符串后,我得到 CryptographicException“填充无效且无法删除”。

Any ideas?

有任何想法吗?

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);
  }
}

采纳答案by Cheeso

The trick is to use MemoryStream.ToArray(). I also changed your code so that it uses the CryptoStreamto Write, in both encrypting and decrypting. And you don't need to call CryptoStream.FlushFinalBlock()explicitly, because you have it in a using()statement, and that flush will happen on Dispose(). The following works for me.

诀窍是使用MemoryStream.ToArray(). 我还更改了您的代码,以便它CryptoStream在加密和解密时都使用to Write。而且您不需要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);
}

Also, I guess you know you will want to explicitly set the Modeof the AesManaged instance, and use System.Security.Cryptography.Rfc2898DeriveBytesto derive the Key and IV from a password and salt.

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

see also:
- AesManaged

另见:
- AesManaged

回答by leppie

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

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

You need to read the length of the buffer, that probably includes the necessary padding (IIRC, been a few years).

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

回答by athina.bikaki

This exception can be caused by a mismatch of any one of a number of encryption parameters.

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

I used the Security.Cryptography.Debuginterface to trace all parameters used in the encrypt/decrypt methods.

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

Finally I found out that my problem was that I set the KeySizeproperty after setting the Keycausing the class to regenerate a random key and not using the key that I was initially set up.

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

回答by Nickolay Olshevsky

Nobody answered, that actually MemoryStream.GetBuffer returns the allocated buffer, not the real data in this buffer. In this case it returns 256-byte buffer, while it contains only 32 bytes of encrypted data.

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

回答by Hyman7

For whats its worth, I'll document what I faced. I was trying to read the encryptor memory stream before the CryptoStream was closed. I know it was naive and I wasted a day debugging it.

对于它的价值,我将记录我所面临的情况。我试图在 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;
            }
        }
    }

Moving the encryptor memory stream read after the cypto stream was closed solved the problem. As Cheeso mentioned. You don't need to call the FlushFinalBlock()if you're using the usingblock.

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

回答by Robert Hegner

As others have mentioned, this error can occur if the key/iv is not correctly initialized for decryption. In my case I need to copy key and iv from some larger buffer. Here's what I did wrong:

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

Does not work:(Padding is invalid and cannot be removed)

不起作用:(填充无效且无法删除)

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);

Works:

作品:

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;

The OP did not make this mistake, but this might be helpful for others seeing the same error.

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