I try to encrypt a file using the example MSDN https://msdn.microsoft.com/ru-ru/library/system.security.cryptography.aes(v=vs.110).aspx
When I encrypt a .txt file, then everything is fine, but when I try to encrypt other files (.bmp, .pdf ...), then the file is not decrypted.
Where is the error there?
I modified the code to download the file
internal static void EncryptAes(string pathData, string pathEnCrypt)
{
string plainText;
using (StreamReader sr = new StreamReader(pathData))
plainText = sr.ReadToEnd();
byte[] encrypted;
// Create an Aes object
// with the specified key and IV.
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
using (FileStream fstream = new FileStream(pathEnCrypt, FileMode.Create))
fstream.Write(encrypted, 0, encrypted.Length);
}
}
StreamReader is supposed to work with text data in particular encoding. Hence you can't use it for binary data.
If file is not huge, you can read file contents into MemmoryStream and use it latter for AES.
Acting on hex/binary data as if it was a string, will result in loss in data and so you won't be able to recover it fully. To get an/more idea you may want to check out this, it explains what you would like to do for VB.NET
Related
I am trying to encrypt a string in C# using this piece of code:
public static string AesEncrypt(string plainText, byte[] Key, byte[] IV)
{
// Create an Aes object with the specified key and IV
using Aes aesAlg = Aes.Create();
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create an encryptor to perform the stream transform
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption
using MemoryStream msEncrypt = new MemoryStream();
using CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
using StreamWriter swEncrypt = new StreamWriter(csEncrypt);
// Write all data to the stream
swEncrypt.Write(plainText);
return Encoding.UTF8.GetString(msEncrypt.ToArray());
}
This is largely based on the sample found here: example.
However, the results are somewhat strange and unpredictable - one time I ran the code and got some string as a result, the other time the resulting string (and the msEncrypt stream) were empty, and one time the application even froze. I don't understand what the problem is.
Edit:
I just ran the code again, on this line:
aesAlg.Key = Key;
something strange happened, I got this error:
Here is how I actually call this method:
public static class Authentication
{
...
private static readonly AesEncryption aes = new AesEncryption();
public static string GenerateToken()
{
AuthenticationData data = new AuthenticationData()
{
// some data
};
string serialized = JsonConvert.SerializeObject(data);
return AesEncryption.AesEncrypt(serialized, aes.Key, aes.IV);
}
}
Maybe some problem with buffers and flushing the streams.
Try to using scope just swEncript before msEncript.ToArray()
public static string AesEncrypt(string plainText, byte[] Key, byte[] IV)
{
// Create an Aes object with the specified key and IV
using Aes aesAlg = Aes.Create();
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create an encryptor to perform the stream transform
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption
using MemoryStream msEncrypt = new MemoryStream();
using CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) {
// Write all data to the stream
swEncrypt.Write(plainText);
}
return Encoding.UTF8.GetString(msEncrypt.ToArray());
}
There are two problems with this code.
The SteamWriter isn't closed before the stream is used. There's no call to StreamWriter.Flush either. StreamWriter uses a buffer internally and writes it out to the
stream when it's full.
The StreamWriter doesn't know it's writing to
a MemoryStream, all streams look the same to it. Given how small the
plaintext is in most examples, no flushing will occur before trying
to read the data from the memory stream.
UTF8 supports only a limited set of byte sequences. Good encryption algorithms though generate essentially random sequences. Some of them won't correspond to any valid UTF8 sequence. One of the most common way to encode binary data as text is to use Base64 encoding. That's what most encryption or hashing samples show.
The code should change to something like :
public static string AesEncrypt(string plainText, byte[] Key, byte[] IV)
{
// Create an Aes object with the specified key and IV
using Aes aesAlg = Aes.Create();
aesAlg.Key = Key;
aesAlg.IV = IV;
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using MemoryStream msEncrypt = new MemoryStream();
using CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
using StreamWriter swEncrypt = new StreamWriter(csEncrypt);
swEncrypt.Write(plainText);
//CHANGES HERE
swEncrypt.Flush();
return Convert.ToBase64String(msEncrypt.ToArray());
}
The sample doesn't suffer from this problem because it uses explicit using blocks, so the writer is closed before the memory stream is used :
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
Neither Visual Studio nor Resharper would suggest using a using statement in this case, as the writer is clearly closed in the innermost block.
Don't mix using styles
You can mix using blocks and statements, eg :
using MemoryStream msEncrypt = new MemoryStream();
using CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
Given the confusion caused already though, you probably shouldn't. The code isn't that much cleaner and it already caused a problem. Someone else looking at the same code in the future may get confused as well and use a bad refactoring
Maybe the key and IV pass in each time you call the AesEncrypt method were different.
For me it's working fine with the example.
static void Main(string[] args)
{
Aes aesAlg = Aes.Create();
AesEncrypt("Hello World", aesAlg.Key, aesAlg.IV);
AesEncrypt("Hello World", aesAlg.Key, aesAlg.IV);
AesEncrypt("Hello World", aesAlg.Key, aesAlg.IV);
AesEncrypt("Hello World", aesAlg.Key, aesAlg.IV);
AesEncrypt("Hello World", aesAlg.Key, aesAlg.IV);
Console.ReadKey();
}
public static string AesEncrypt(string plainText, byte[] Key, byte[] IV)
{
// Create an Aes object with the specified key and IV
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create an encryptor to perform the stream transform
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
Console.WriteLine(BitConverter.ToString(msEncrypt.ToArray()));
return Encoding.UTF8.GetString(msEncrypt.ToArray());
}
}
}
}
I am trying to decrypt a string in C# using AES:
public static string AesDecrypt(byte[] cipherText, byte[] Key, byte[] IV)
{
string plaintext = null;
// Create an Aes object with the specified key and IV
using Aes aesAlg = Aes.Create();
aesAlg.Padding = PaddingMode.Zeros;
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decryptor to perform the stream transform
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption
using MemoryStream msDecrypt = new MemoryStream(cipherText);
using CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
using StreamReader srDecrypt = new StreamReader(csDecrypt);
// Read the decrypted bytes from the decrypting stream and place them in a string
plaintext = srDecrypt.ReadToEnd();
return plaintext;
}
The encoded data is JSON, but when I decrypt it, I get all the right data except that the closing } of the JSON content is missing.
I think that the AES itself is not my problem here. I have doubts in the
plaintext = srDecrypt.ReadToEnd();
since only the last character is missing.
I don't know if I am supposed to flush any of the streams explicitly, but in any case it's a very curious problem.
Here's the full code for the encryption:
public static string AesEncrypt(string plainText, byte[] Key, byte[] IV)
{
// Create an Aes object with the specified key and IV
using Aes aesAlg = Aes.Create();
aesAlg.Padding = PaddingMode.Zeros;
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create an encryptor to perform the stream transform
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption
using MemoryStream msEncrypt = new MemoryStream();
using CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
using StreamWriter swEncrypt = new StreamWriter(csEncrypt);
// Write all data to the stream
swEncrypt.Write(plainText);
swEncrypt.Flush();
return Convert.ToBase64String(msEncrypt.ToArray());
}
And this is how I call the decryption method:
public static AuthenticationData ParseAuthenticationToken(string token)
{
byte[] tokenBytes = Convert.FromBase64String(token);
string json = AesEncryption.AesDecrypt(tokenBytes, aes.Key, aes.IV);
return JsonConvert.DeserializeObject<AuthenticationData>(json);
}
The problem is in your encryption code. Although you're calling seEncrypt.Flush(), you're not calling csEncrypt.FlushFinalBlock(). That automatically happens when the stream is disposed, but you're not doing that until after you've called msEncrypt.ToArray(). I would rewrite that code as:
MemoryStream msEncrypt = new MemoryStream();
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using StreamWriter swEncrypt = new StreamWriter(csEncrypt);
swEncrypt.Write(plainText);
// swEncrypt is disposed here, flushing it. Then csEncrypt is disposed,
// flushing the final block.
}
return msEncrypt.ToArray();
I'm using AesCryptoServiceProvider to encrypt and decrypt an XML document on disk. There's an example in the MSDN reference that was helpful. I generate the AES key from the SHA-256 hash of a given password. The first half of it is assigned as IV, since I don't know of any better thing to use here. As far as I know, both key and IV must be the same for encrypting and decrypting.
When I decrypt my file, this is what the beginning of it looks like:
I���H璧�-����[�="1.0" encoding="utf-8"?>
The rest of the document is perfectly fine. There's not even some random padding after the content as I would have expected maybe.
What is causing this random garbage at the beginning of the file?
Here's more of the reading code:
using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
{
using (SHA256CryptoServiceProvider sha = new SHA256CryptoServiceProvider())
{
this.cryptoKey = sha.ComputeHash(Encoding.Unicode.GetBytes(password));
}
aes.Key = this.cryptoKey;
Array.Copy(this.cryptoKey, aes.IV, 16);
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
using (CryptoStream cs = new CryptoStream(fs, decryptor, CryptoStreamMode.Read))
using (StreamReader sr = new StreamReader(cs))
{
string data = sr.ReadToEnd();
xdoc.LoadXml(data);
//xdoc.Load(sr);
}
}
And that's the encryption code:
XmlWriterSettings xws = new XmlWriterSettings();
xws.Encoding = Encoding.UTF8;
xws.Indent = true;
xws.IndentChars = "\t";
xws.OmitXmlDeclaration = false;
using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
{
aes.Key = this.cryptoKey;
Array.Copy(this.cryptoKey, aes.IV, 16);
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
using (CryptoStream cs = new CryptoStream(fs, encryptor, CryptoStreamMode.Write))
using (StreamWriter sw = new StreamWriter(cs, Encoding.UTF8))
{
XmlWriter writer = XmlWriter.Create(sw, xws);
xdoc.Save(writer);
writer.Close();
}
}
To begin with, don't generate the key material from an ad-hoc algorithm (and yes, when it comes to key derivation, SHA256 is an ad-hoc algorithm). Follow industry standards and use a trusted Password-Based Key Derivation Function. Current standard is PBKDF-2, see also RFC2898. .Net managed crypto implementation is the Rfc2898DeriveBytes class.
Second, you must show us the encryption code. Looks to me like the sample you used appends the IV used at the beginning of the encrypted stream. Which makes perfect sense, given that the IV should not be derived from the password. The key and IV should be derived from password+random, and the 'random' must be sent as part of the file.
Following Remus Rusanu's advice I've changed my code to use the Rfc2898DeriveBytes class for key generation and write the used salt and IV data to the encrypted file so that I don't need to transport it on a separate channel (like the password).
This is now working for me:
// Setup XML formatting
XmlWriterSettings xws = new XmlWriterSettings();
xws.Encoding = Encoding.UTF8;
xws.Indent = true;
xws.IndentChars = "\t";
xws.OmitXmlDeclaration = false;
// Encrypt document to file
byte[] salt = new byte[8];
new RNGCryptoServiceProvider().GetBytes(salt);
Rfc2898DeriveBytes keyGenerator = new Rfc2898DeriveBytes(password, salt);
using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
{
aes.Key = keyGenerator.GetBytes(aes.KeySize / 8);
using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
using (CryptoStream cs = new CryptoStream(fs, aes.CreateEncryptor(), CryptoStreamMode.Write))
using (StreamWriter sw = new StreamWriter(cs, Encoding.UTF8))
{
fs.Write(salt, 0, salt.Length);
fs.Write(aes.IV, 0, aes.IV.Length);
// Write XmlDocument to the encrypted file
XmlWriter writer = XmlWriter.Create(sw, xws);
xdoc.Save(writer);
writer.Close();
}
}
// Decrypt the file
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
byte[] salt = new byte[8];
fs.Read(salt, 0, salt.Length);
Rfc2898DeriveBytes keyGenerator = new Rfc2898DeriveBytes(this.password, salt);
using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
{
aes.Key = keyGenerator.GetBytes(aes.KeySize / 8);
byte[] iv = new byte[aes.BlockSize / 8];
fs.Read(iv, 0, iv.Length);
aes.IV = iv;
using (CryptoStream cs = new CryptoStream(fs, aes.CreateDecryptor(), CryptoStreamMode.Read))
using (StreamReader sr = new StreamReader(cs))
{
// Read stream into new XmlDocument
xdoc.Load(sr);
}
}
}
You are correct that the key and IV need to be the same for encrypting and decrypting. In many cases the IV is prepended to the cyphertext and needs to be removed before decryption. The symptom of this is extra garbage before the real start of the file. The garbage is the prepended IV.
Alternatively, you are not using the same IV, in which case the first 16 bytes of the file are garbage, and you get clean plaintext from the 17th byte onwards (AES has 16 byte blocks).
If both are happening, then you will get the first symptom.
What you have appears to be the first. Try using the first 16 bytes of the incoming file as your IV, and the rest as the actual cyphertext.
The first block of the content is mangled because you provided the wrong IV.
The rest of the content is fine because you provided the right Key.
Please suggest me where i need to update/refactor the code to get rid of exception. I am getting exception while I try to decrypt the encrypted string using following code.
Following line is throwing exception
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
public string EncryptAuthenticationTokenAes(string plainText)
{
byte[] encrypted;
// Create an AesManaged object
// with the specified key and IV.
using (AesManaged aesAlg = new AesManaged())
{
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
aesAlg.Padding = PaddingMode.None;
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return Convert.ToBase64String(encrypted);
}
public string DecryptPasswordAes(string encryptedString)
{
//Convert cipher text back to byte array
byte[] cipherText = Convert.FromBase64String(encryptedString);
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an AesManaged object
// with the specified key and IV.
using (AesManaged aesAlg = new AesManaged())
{
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
aesAlg.Padding = PaddingMode.None;
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
Pretty standard bug when using CryptoStream, you forgot to force it to encrypt the last bytes of the stream. It keeps bytes in an internal buffer until enough of them arrive to emit a block. You must force the last few bytes out. Fix:
using (var msEncrypt = new MemoryStream())
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
using (var swEncrypt = new StreamWriter(csEncrypt)) {
swEncrypt.Write(plainText);
csEncrypt.FlushFinalBlock();
encrypted = msEncrypt.ToArray();
}
You got the exception when decrypting it because encrypted is missing the final padding. The real problem is caused by the using statement, you wouldn't have this problem if you waited obtaining the encrypted bytes until after the CryptoStream is closed. But that doesn't work well because the using statement on the StreamWriter also closes the CryptoStream and the MemoryStream. Explicitly using FlushFinalBlock() is the best workaround.
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.
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);
}
}
The trick is to use MemoryStream.ToArray().
I also changed your code so that it uses the CryptoStream to 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.
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 Mode of the AesManaged instance, and use System.Security.Cryptography.Rfc2898DeriveBytes to derive the Key and IV from a password and salt.
see also:
- AesManaged
This exception can be caused by a mismatch of any one of a number of encryption parameters.
I used the Security.Cryptography.Debug interface to trace all parameters used in the encrypt/decrypt methods.
Finally I found out that my problem was that I set the KeySize property after setting the Key causing the class to regenerate a random key and not using the key that I was initially set up.
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 was naive and I wasted a day debugging it.
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 using block.
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).
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.
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:
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.