2010-05-24 8 views
5

¿Puede alguien identificar por favor si hay posibles pérdidas de memoria en el siguiente código. Lo he intentado con .Net Memory Profiler y dice "CreateEncryptor" y algunas otras funciones están dejando pérdidas de memoria no administradas ya que lo he confirmado usando los monitores de rendimiento.Enfrentando fugas de memoria en el método de cifrado AES

pero ya existen, claro, las llamadas de cerca se realizan siempre que sea posible por favor avísenme en consecuencia. es urgente.

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; 
    } 
+0

Lo soporto en cada llamada no hay un escenario específico solo llame a la función cifrar me enfrenté a pérdidas de 30-40MB en 100K llamadas –

+0

Si su generador de perfiles dice que la pérdida de memoria ocurre en CreateEncryptor, ¿por qué cree que ocurre en el código que ¿al corriente? – dtb

+0

Porque puede ser que necesite limpiar algo más también. –

Respuesta

7

actualización: Después de un poco más de investigación que registra esto como un error en Microsoft connect. Han reconocido el error y han creado un hotfix. (Obviamente, se trata de una revisión por lo que se aplican las renuncias habituales. Si es posible, actualizar a .NET 4.0 probablemente sería la solución preferida)


Parece que este código fugas en .NET 3.5, pero es obra bien en .net 4.0.

Comencé en .net 4.0 y copié el código en una aplicación de prueba rápida y lo llamé 1,000,000 veces, y el uso de la memoria se mantuvo constante en 22,4mb todo el tiempo. También rastreé los tamaños de pila del GC y manejaba los recuentos, y todos se mantuvieron constantes. Hasta donde puedo decir que el código no tiene fugas.

Luego reconstruí la aplicación en .net 3.5 y volví a ejecutar la prueba y obtuve la fuga exacta que está describiendo. Comenzó alrededor de los 24mb, y para el momento en que realizó 100k llamadas, el uso de memoria se había duplicado a más de 50mb. Curiosamente, parecía ser el montón de Gen2 el que aumentaba, lo que sugiere que se trata de una fuga de memoria administrada en lugar de manejos/memoria no administrados.

Si es posible, le sugiero que intente cambiar a .NET 4.0.

Mi código completo:

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; 
     } 

    } 
} 
+0

Gracias Stevens por un esfuerzo extra. Lo probaría en 4.0 –

+0

Sí, tiene razón, está trabajando en 4.0, ¿significa esto que es un error de Framework 3.5? –

+0

Bueno, como no hay ninguna solución para esto en netfx3.5, marcándolo como respuesta, gracias Simon por ayuda. –

0

realmente no tratar de secuestrar un hilo, pero ¿cuál es la principal diferencia entre su cifrar como éste?


/// 
     /// Encrypts a string 
     /// 
     /// Text to be encrypted 
     /// Password to encrypt with 
     /// Salt to encrypt with 
     /// Can be either SHA1 or MD5 
     /// Number of iterations to do 
     /// Needs to be 16 ASCII characters long 
     /// Can be 128, 192, or 256 
     /// An encrypted string 
     public static string Encrypt(string PlainText, string Password, 
      string Salt, string HashAlgorithm, 
      int PasswordIterations, string InitialVector, 
      int KeySize) 
     { 
      try 
      { 
       if (string.IsNullOrEmpty(PlainText)) 
        return ""; 
       byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector); 
       byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt); 
       byte[] PlainTextBytes = Encoding.UTF8.GetBytes(PlainText); 
       PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations); 
       byte[] KeyBytes = DerivedPassword.GetBytes(KeySize/8); 
       RijndaelManaged SymmetricKey = new RijndaelManaged(); 
       SymmetricKey.Mode = CipherMode.CBC; 
       byte[] CipherTextBytes = null; 
       using (ICryptoTransform Encryptor = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes)) 
       { 
        using (MemoryStream MemStream = new MemoryStream()) 
        { 
         using (CryptoStream CryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write)) 
         { 
          CryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length); 
          CryptoStream.FlushFinalBlock(); 
          CipherTextBytes = MemStream.ToArray(); 
          MemStream.Close(); 
          CryptoStream.Close(); 

          CryptoStream.Dispose(); 
          MemStream.Dispose(); 
         } 
        } 
        Encryptor.Dispose(); 
       } 
       SymmetricKey.Clear(); 
       return Convert.ToBase64String(CipherTextBytes); 
      } 
      catch { throw; } 
     } 

Funcioné esto debajo de 3.5 y no moví más allá de 22mb. Mis habilidades de cifrado son un poco delgadas, pero me pregunto en general por qué uno es mejor que el otro, o si lo es. Parece que hay muchas maneras de hacer las mismas cosas.