I have problem with AES encryption/decryption. The commented code worked but sometimes gave error "padding is invalid and cannot be removed" so I changed it as it is explained here Padding is invalid and cannot be removed Exception while decrypting string using "AesManaged" C#
but when I tried it the code below during decryption gives an empty string. I don't know where I make mistake. The two static functions bytesToString and stringToBytes has nothing to do with encryption and I use them in some other place. The key length and block size is OKAY. I found this in debbuger:
"'csEncrypt.Length' threw an exception of type 'System.NotSupportedException'"
I Work on 3.5 .NET Visual STudio 2008
here is prtscr from debugger as u can see after leaving block encrypted is 0 byte long and cryptostream has some exceptions
How to fix it? Please give me some clues.
static class Aes
{
public static string bytesToHexString(byte[] key)
{
return BitConverter.ToString(key).Replace("-", String.Empty);
}
public static byte[] stringToBytes(string key)
{
return Enumerable.Range(0, key.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(key.Substring(x, 2), 16))
.ToArray();
}
public static void generateKeyAndIv(out byte[] key, out byte[] IV)
{
using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
{
aesAlg.BlockSize = 128;
aesAlg.KeySize = 256;
aesAlg.Padding = PaddingMode.None;
//aesAlg.Mode = CipherMode.CBC;
aesAlg.GenerateKey();
aesAlg.GenerateIV();
key = aesAlg.Key;
IV = aesAlg.IV;
}
}
public static string EncryptStringToString(string plainText, byte[] Key, byte[] IV)
{
byte[] bytes =EncryptStringToBytes_Aes(plainText, Key, IV);
return Convert.ToBase64String(bytes);
//return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
}
public static string DecryptStringToString(string cipherText, byte[] Key, byte[] IV)
{
//byte[] bytes = Encoding.UTF8.GetBytes(cipherText);
byte[] bytes = Convert.FromBase64String(cipherText);
return DecryptStringFromBytes_Aes(bytes, Key, IV);
}
public static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");
/*byte[] encrypted;
// Create an AesCryptoServiceProvider object
// with the specified key and IV.
using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
{
aesAlg.BlockSize = 128;
aesAlg.KeySize = 256;
aesAlg.Padding = PaddingMode.PKCS7;
aesAlg.Mode = CipherMode.CBC;
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();
}
}*/
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.
aesAlg.Padding = PaddingMode.None;
aesAlg.BlockSize = 128;
aesAlg.KeySize = 256;
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
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();
}
}
//return encrypted;
// Return the encrypted bytes from the memory stream.
return encrypted;
}
public static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an AesCryptoServiceProvider object
// with the specified key and IV.
using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
{
aesAlg.BlockSize = 128;
aesAlg.KeySize = 256;
aesAlg.Padding = PaddingMode.PKCS7;
aesAlg.Mode = CipherMode.CBC;
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decrytor 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;
}
}
OK, maybe I will explain the whole situation a bit more. I wrote my own secure e-mail. I have 2x encryption. The communication between server and client is encrypted by RSA and AES. Also messages created by users are encrypted by RSA + AES.
sending message looks like:
- Client connects to server.
- They establish secure connection (server sends its public key, client generates AES key encrypts it via server's public key and send it to server. After that server and client uses AES key to communicate).
- Client create message in XML, message can contain files which are read to base64 and after that they are encrypted with AES.
- The message is written to db.
recieving message look like:
- Connection to server.
- Establish secure connection.
- Get messages from server.
- Decrypt AES key using RSA private key.
- Decrypt message using decrypted AES key.
- If there are files then decrypt them using AES and base64_decode them to bytes and save.
Now the problem is with encryption of large data. Even 200-300 kB is sometimes problem.
Another interesting thing I've discovered is that when I run the code via debugger it works but when I run code without it. It doesn't work.
Solution
I found the solution of problem. Because I used AES encryption/decryption twice very fast after each other with different keys/ivs Garbage Collector didn't clean these objects. The solution was adding
GC.Collect();
GC.WaitForPendingFinalizers();
just before returning value by DecryptStringFromBytes_Aes and EncryptStringToBytes_Aes
I hope that it will help someone who is having same issue like I had.