Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Facing Memory Leaks in AES Encryption Method

Can anyone please identify is there any possible memory leaks in following code. I have tried with .Net Memory Profiler and it says "CreateEncryptor" and some other functions are leaving unmanaged memory leaks as I have confirmed this using Performance Monitors.

but there are already dispose, clear, close calls are placed wherever possible please advise me accordingly. its a been urgent.

public static string Encrypt(string plainText, string key)
    {
        //Set up the encryption objects
        byte[] encryptedBytes = null;
        using (AesCryptoServiceProvider acsp = GetProvider(Encoding.UTF8.GetBytes(key)))
        {
            byte[] sourceBytes = Encoding.UTF8.GetBytes(plainText);
            using (ICryptoTransform ictE = acsp.CreateEncryptor())
            {
                //Set up stream to contain the encryption
                using (MemoryStream msS = new MemoryStream())
                {
                    //Perform the encrpytion, storing output into the stream
                    using (CryptoStream csS = new CryptoStream(msS, ictE, CryptoStreamMode.Write))
                    {
                        csS.Write(sourceBytes, 0, sourceBytes.Length);
                        csS.FlushFinalBlock();

                        //sourceBytes are now encrypted as an array of secure bytes
                        encryptedBytes = msS.ToArray(); //.ToArray() is important, don't mess with the buffer

                        csS.Close();
                    }

                    msS.Close();
                }
            }

            acsp.Clear();
        }

        //return the encrypted bytes as a BASE64 encoded string
        return Convert.ToBase64String(encryptedBytes);
    }
    private static AesCryptoServiceProvider GetProvider(byte[] key)
    {
        AesCryptoServiceProvider result = new AesCryptoServiceProvider();
        result.BlockSize = 128;
        result.KeySize = 256;
        result.Mode = CipherMode.CBC;
        result.Padding = PaddingMode.PKCS7;

        result.GenerateIV();
        result.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

        byte[] RealKey = GetKey(key, result);
        result.Key = RealKey;
        // result.IV = RealKey;
        return result;
    }

    private static byte[] GetKey(byte[] suggestedKey, SymmetricAlgorithm p)
    {
        byte[] kRaw = suggestedKey;
        List<byte> kList = new List<byte>();

        for (int i = 0; i < p.LegalKeySizes[0].MaxSize; i += 8)
        {
            kList.Add(kRaw[(i / 8) % kRaw.Length]);
        }
        byte[] k = kList.ToArray();
        return k;
    }
like image 617
Mubashar Avatar asked May 24 '10 12:05

Mubashar


1 Answers

Update: After some more investigation I logged this as a bug on Microsoft connect. They have acknowledged the bug and have created a hotfix. (Obviously, this is a hotfix so the usual disclaimers apply. If you can, upgrading to .net 4.0 would probably be the preferred solution)


It seems that this code leaks in .net 3.5, but is works fine in .net 4.0.

I started in .net 4.0 and copied your code into a quick test app and called it 1,000,000 times, and the memory usage stayed constant at 22.4mb the whole time. I also tracked the GC heap sizes and handle counts, and they all stayed constant. As far as I can tell that code isn't leaking.

I then rebuilt the app under .net 3.5 and re-run the test and I got the exact leak you are describing. It started at around 24mb, and by the time it had made 100k calls, memory usage had doubled to over 50mb. Interestingly, it seemed to be the Gen2 heap that was increasing which suggests it is a managed memory leak rather than unmanaged handles/memory.

If possibly I would suggest you try to switch to .net 4.0.

My complete code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            String encryptedString;


            for (int j = 0; j < 1000; j++)
            {
                for (int i = 0; i < 1000; i++)
                {
                    encryptedString = Encrypt(String.Format("test string {0} {1}", j, i), "key");
                }
                Console.WriteLine("j = {0}", j);
            }

            Console.WriteLine("Finished");
            Console.ReadLine();

        }

        public static string Encrypt(string plainText, string key)
        {
            //Set up the encryption objects
            byte[] encryptedBytes = null;
            using (AesCryptoServiceProvider acsp = GetProvider(Encoding.UTF8.GetBytes(key)))
            {
                byte[] sourceBytes = Encoding.UTF8.GetBytes(plainText);
                using (ICryptoTransform ictE = acsp.CreateEncryptor())
                {
                    //Set up stream to contain the encryption
                    using (MemoryStream msS = new MemoryStream())
                    {
                        //Perform the encrpytion, storing output into the stream
                        using (CryptoStream csS = new CryptoStream(msS, ictE, CryptoStreamMode.Write))
                        {
                            csS.Write(sourceBytes, 0, sourceBytes.Length);
                            csS.FlushFinalBlock();

                            //sourceBytes are now encrypted as an array of secure bytes
                            encryptedBytes = msS.ToArray(); //.ToArray() is important, don't mess with the buffer

                            csS.Close();
                        }

                        msS.Close();
                    }
                }

                acsp.Clear();
            }

            //return the encrypted bytes as a BASE64 encoded string
            return Convert.ToBase64String(encryptedBytes);
        }
        private static AesCryptoServiceProvider GetProvider(byte[] key)
        {
            AesCryptoServiceProvider result = new AesCryptoServiceProvider();
            result.BlockSize = 128;
            result.KeySize = 256;
            result.Mode = CipherMode.CBC;
            result.Padding = PaddingMode.PKCS7;

            result.GenerateIV();
            result.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

            byte[] RealKey = GetKey(key, result);
            result.Key = RealKey;
            // result.IV = RealKey;
            return result;
        }

        private static byte[] GetKey(byte[] suggestedKey, SymmetricAlgorithm p)
        {
            byte[] kRaw = suggestedKey;
            List<byte> kList = new List<byte>();

            for (int i = 0; i < p.LegalKeySizes[0].MaxSize; i += 8)
            {
                kList.Add(kRaw[(i / 8) % kRaw.Length]);
            }
            byte[] k = kList.ToArray();
            return k;
        }

    }
}
like image 186
Simon P Stevens Avatar answered Oct 31 '22 01:10

Simon P Stevens