Question

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

Print screen from debugger

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:

  1. Client connects to server.
  2. 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).
  3. Client create message in XML, message can contain files which are read to base64 and after that they are encrypted with AES.
  4. The message is written to db.

recieving message look like:

  1. Connection to server.
  2. Establish secure connection.
  3. Get messages from server.
  4. Decrypt AES key using RSA private key.
  5. Decrypt message using decrypted AES key.
  6. 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.

Was it helpful?

Solution

The way you were using the streams meant that there was no data being written to the underlying streams, I do not know why but I did find an alternative approach that I have reworked.

With the following code you should be able to run as a console application and then reshape it so it works for your purposes. Let me know how it goes:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace Encrypto
{
    public static class Program
    {
        public static void Main()
        {

            const string password = "test";
            const string text = "test";
            var cipherText = Aes.Encrypt(password, text);
            var decrypted = Aes.Decrypt(password, cipherText);

            Console.WriteLine(decrypted);
            Console.ReadKey();
        }
    }

    internal static class Aes
    {
        public static EncryptedData Encrypt(string password, string data)
        {
            return Transform(true, password, data, null) as EncryptedData;
        }

        public static string Decrypt(string password, EncryptedData data)
        {
            return Transform(false, password, data.DataString, data.SaltString) as string;
        }

        private static object Transform(bool encrypt, string password, string data, string saltString)
        {
            using (var aes = new AesManaged())
            {
                aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.PKCS7;
                var keyLen = aes.KeySize/8;
                var ivLen = aes.BlockSize/8;
                const int saltSize = 8;
                const int iterations = 8192;

                var salt = encrypt ? new byte[saltSize] : Convert.FromBase64String(saltString);
                if (encrypt)
                {
                    new RNGCryptoServiceProvider().GetBytes(salt);
                }

                var bcKey = new Rfc2898DeriveBytes("BLK" + password, salt, iterations).GetBytes(keyLen);
                var iv = new Rfc2898DeriveBytes("IV" + password, salt, iterations).GetBytes(ivLen);
                var macKey = new Rfc2898DeriveBytes("MAC" + password, salt, iterations).GetBytes(16);

                aes.Key = bcKey;
                aes.IV = iv;

                var rawData = encrypt ? Encoding.UTF8.GetBytes(data) : Convert.FromBase64String(data);

                using (var transform = encrypt ? aes.CreateEncryptor() : aes.CreateDecryptor())
                using (var memoryStream = encrypt ? new MemoryStream() : new MemoryStream(rawData))
                using (var cryptoStream = new CryptoStream(memoryStream, transform, encrypt ? CryptoStreamMode.Write : CryptoStreamMode.Read))
                {
                    if (encrypt)
                    {
                        cryptoStream.Write(rawData, 0, rawData.Length);
                        cryptoStream.FlushFinalBlock();
                        return new EncryptedData(salt, macKey, memoryStream.ToArray());
                    }
                    var originalData = new byte[rawData.Length];
                    var count = cryptoStream.Read(originalData, 0, originalData.Length);

                    return Encoding.UTF8.GetString(originalData, 0, count);
                }
            }
        }

        public class EncryptedData
        {
            public EncryptedData(byte[] salt, byte[] mac, byte[] data)
            {
                Salt = salt;
                MAC = mac;
                Data = data;
            }

            private byte[] Salt { get; set; }

            public string SaltString
            {
                get { return Convert.ToBase64String(Salt); }
            }

            private byte[] MAC { get; set; }

            private byte[] Data { get; set; }

            public string DataString
            {
                get { return Convert.ToBase64String(Data); }
            }
        }
    }
}

Sources for providing this answer: Using AES encryption in .NET - CryptographicException saying the padding is invalid and cannot be removed

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top