1

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.

Community
  • 1
  • 1
Robert
  • 19,800
  • 5
  • 55
  • 85
  • 1
    It is going to be easier for you find the problem if you tidy up your implementation a little. – Mr. Mr. Mar 01 '13 at 08:32
  • Now I tried to use http://msdn.microsoft.com/en-us/library/system.security.cryptography.aesmanaged.aspx code from here and this code gives me "padding invalid" exception. So either I have an empty string or sometimes "padding invalid exception" when I use sample code from MSDN. – Robert Mar 01 '13 at 08:34
  • I found out that code from msdn does not work where there is large amount of data to decrypt/encrypt. – Robert Mar 01 '13 at 08:52
  • The code you had didn't work on small data also, not sure why, I saw the comment from Hans Passant too, but it did not work. From what I saw you had wired it up as you were told. – Mr. Mr. Mar 01 '13 at 10:08
  • If you have a new problem you need to post as a different question, i.e., the 'problem of encrypting large data' needs to be posted in a new question do not change this question as you progress it is not how this site is supposed to work. – Mr. Mr. Mar 01 '13 at 12:04

1 Answers1

1

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

Community
  • 1
  • 1
Mr. Mr.
  • 4,257
  • 3
  • 27
  • 42
  • It's the same. I explained in details the whole problem of mine in the first post. I don't undersrtand why I have problem, I use the example from MSDN. – Robert Mar 01 '13 at 10:26
  • Ok, the code you tried from MSDN is not working for you, it is for me, so you need to look closer at what you have and possibly try something else. Did you run the code I linked in a new console project or is it in your existing application? – Mr. Mr. Mar 01 '13 at 10:43
  • You are not using the code in the same way as the example, the bits that are different are what is likely to be causing your issues for example `generateKeyAndIv()` – Mr. Mr. Mar 01 '13 at 10:48
  • The code from MSDN and yours is working but when I try to encrypt larger amount of data it gives "padding invalid or removed" error. generateKeyAndIV() only assigns key and IV which I need to encrypt via RSA and send to server. – Robert Mar 01 '13 at 11:06
  • can you give me a sample of data please? In your question. – Mr. Mr. Mar 01 '13 at 11:07
  • Here you have sample of encrypted xml message http://www75.zippyshare.com/v/55689166/file.html – Robert Mar 01 '13 at 11:28
  • I escaped the text and had it all on a single line (which might be a problem in itself) assigned it to a variable and successfully encrypted it and decrypted it. No problem using the code I supplied in my answer and using the MSDN example. I also loaded it and read it from a file and it worked using both the MSDN code and the code I provided in my answer. – Mr. Mr. Mar 01 '13 at 11:43
  • How could you encrypt and decrypt it without having private rsa key? I'm confused. – Robert Mar 01 '13 at 11:46
  • I am using only the example code from MSDN and the code in your question and the code I constructed in my answer, nothing else. I did it symmetrically. – Mr. Mr. Mar 01 '13 at 11:51
  • I thought your problem was relating to the AES part as your example was not even encrypting the data let alone implementing with public-private keys. – Mr. Mr. Mar 01 '13 at 12:01
  • It is related to AES because RSA is only used to encrypt AES KEY AND IV and it works GOOD. I checked keys after encryption and decryption and they are the same. My first post explains why I use RSA too. 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. Maybe there are some issues related to GC? – Robert Mar 01 '13 at 12:03
  • I edited the first post with solution. It worked for me. Thank you @monkieboy for helping and your time. – Robert Mar 01 '13 at 12:13
  • You are welcome, you should create an answer here and mark it as the solution, it helps others find the solution that way. – Mr. Mr. Mar 01 '13 at 12:18
  • 1
    I couldn't because of "reputation points" haha the best thing now is that when i moved the solution to dedicated server running on windows 2008 RC it doesn't work. ehhh – Robert Mar 01 '13 at 12:30
  • It is strange. Because sometimes it works and sometimes it doesn't I use the same data, but keys are generated randomly. – Robert Mar 01 '13 at 12:42