Утечка памяти в .NET при использовании собственных поставщиков криптографии - PullRequest
4 голосов
/ 02 марта 2012

У меня есть приложение, которое должно шифровать и дешифровать строки.Ниже мой метод расшифровки:

  public static string Decrypt(string cipherText)
  {
     try
     {
        //Decrypt:
        byte[] keyArray;
        byte[] toDecryptArray = Convert.FromBase64String(cipherText);
        keyArray = UTF8Encoding.UTF8.GetBytes(key);
        AesCryptoServiceProvider Aes = new AesCryptoServiceProvider();
        Aes.Key = keyArray;
        Aes.Mode = CipherMode.CBC;
        Aes.Padding = PaddingMode.PKCS7;
        Aes.IV = IV;
        ICryptoTransform cTransform = Aes.CreateDecryptor();
        byte[] resultArray = cTransform.TransformFinalBlock(toDecryptArray, 0, toDecryptArray.Length);
        Aes.Clear();
        return UTF8Encoding.UTF8.GetString(resultArray, 0, resultArray.Length);
     }
     catch (Exception ex)
     {
        return "FAILED:*" + cipherText + "*" + ex.Message;
     }
  }

Однако, это, кажется, утечка.Как видите, все переменные имеют локальную область видимости, поэтому они должны быть освобождены после завершения этого блока.В качестве фона я вызываю этот метод иногда почти без остановки.

Чтобы определить, что произошла утечка, я заваливаю свой сервер многими запросами.Тщательно отслеживая каждый запрос по каждому пути кода, который он принимает, я определил, что этот фрагмент кода является виновником.Когда закомментированное в моей памяти использование резко и линейно возрастает, когда закомментированное (и просто возвращающее cipherText) использование памяти остается ровным.

Ответы [ 4 ]

21 голосов
/ 02 марта 2012

AesCryptoServiceProvider реализует IDisposable.Попробуйте использовать его в using -блоке.Примерно так:

using(AesCryptoServiceProvider Aes = new AesCryptoServiceProvider()){
        Aes.Key = keyArray; 
        Aes.Mode = CipherMode.CBC; 
        Aes.Padding = PaddingMode.PKCS7; 
        Aes.IV = IV; 
        using (ICryptoTransform cTransform = Aes.CreateDecryptor()){
            byte[] resultArray = cTransform.TransformFinalBlock(toDecryptArray, 0, toDecryptArray.Length); 
            Aes.Clear(); 
            return UTF8Encoding.UTF8.GetString(resultArray, 0, resultArray.Length); 
        }
}
5 голосов
/ 02 марта 2012

Как правило, вы всегда должны располагать объекты, которые реализуют IDisposable, такие как AesCryptoServiceProvider. Обычно они используют неуправляемые ресурсы, которые не очищаются сборщиком мусора. Так что вместо

AesCryptoServiceProvider Aes = new AesCryptoServiceProvider();

запись

using (AesCryptoServiceProvider Aes = new AesCryptoServiceProvider())
{
}
4 голосов
/ 02 марта 2012
  1. Удалите Aes в блоке finally {} , поэтому он будет удален, даже если произойдет исключение.
  2. Утилизировать ICryptoTransform. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 10 "*" также как "* 1010" *

    public static string Decrypt(string cipherText)
    {
       AesCryptoServiceProvider Aes;
    
       try
       {
          //Decrypt:
          byte[] keyArray;
          byte[] toDecryptArray = Convert.FromBase64String(cipherText);
          keyArray = UTF8Encoding.UTF8.GetBytes(key);
          Aes = new AesCryptoServiceProvider();
          Aes.Key = keyArray;
          Aes.Mode = CipherMode.CBC;
          Aes.Padding = PaddingMode.PKCS7;
          Aes.IV = IV;
    
          using (ICryptoTransform cTransform = Aes.CreateDecryptor())
          {
            byte[] resultArray = cTransform.TransformFinalBlock(toDecryptArray, 0, toDecryptArray.Length);
            Aes.Clear();
            return UTF8Encoding.UTF8.GetString(resultArray, 0, resultArray.Length);
          }
       }
       catch (Exception ex)
       {
          return "FAILED:*" + cipherText + "*" + ex.Message;
       }
       finally
       {
          Aes.Dispose();
       }
    }
    
1 голос
/ 02 марта 2012

Как уже было сказано, объекты, которые реализуют IDisposable, не получают сборщик мусора немедленно. Если вы не вызываете Dispose для одноразового объекта, явным образом или путем его использования, то для сбора мусора этому объекту потребуется больше времени.

Если вы используете попытку, вы должны рассмотреть возможность объявления таких переменных вне попытки и реализовать удаление в блоке finally. Особенно с AesCryptoServiceProvider, где вы хотите убедиться, что метод Clear () выполняется даже при возникновении ошибки, так как использование не сделает этого за вас.

public static string Decrypt(string cipherText)
{
   string decryptedMessage = null;
   AesCryptoServiceProvider Aes = null;
   ICryptoTransform cTransform = null;

   try
   {
      //Decrypt:
      byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
      byte[] toDecryptArray = Convert.FromBase64String(cipherText);

      AesCryptoServiceProvider Aes = new AesCryptoServiceProvider();
      Aes.Key = keyArray;
      Aes.Mode = CipherMode.CBC;
      Aes.Padding = PaddingMode.PKCS7;
      Aes.IV = IV;

      ICryptoTransform cTransform = Aes.CreateDecryptor();

      byte[] resultArray = cTransform.TransformFinalBlock(toDecryptArray, 0, toDecryptArray.Length);
      decryptedMessage = UTF8Encoding.UTF8.GetString(resultArray, 0, resultArray.Length);
   }
   catch (Exception ex)
   {
      decryptedMessage = "FAILED:*" + cipherText + "*" + ex.Message;
   }
   finally
   {
       if (cTransform != null)
       {
           cTransform.Dispose();
       }

       if (Aes != null)
       {
           Aes.Clear();
           Aes.Dispose();
       }
   }

   return decryptedMessage;
}

Вам также следует подумать о том, чтобы выбросить исключение, исключив блок catch и сохранив finally, и обработав его вне этого метода.

Вы также можете вернуть bool для успеха / неудачи и передать расшифрованную строку, используя out. Таким образом, вы не путаете ваши ошибки с содержанием вашего сообщения:

public bool string Decrypt(string cipherText, out string decryptedMessage)
{
   bool succeeded = false;
   decryptedMessage = null;
   AesCryptoServiceProvider Aes = null;
   ICryptoTransform cTransform = null;

   try
   {
      //Decrypt:
      byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
      byte[] toDecryptArray = Convert.FromBase64String(cipherText);

      AesCryptoServiceProvider Aes = new AesCryptoServiceProvider();
      Aes.Key = keyArray;
      Aes.Mode = CipherMode.CBC;
      Aes.Padding = PaddingMode.PKCS7;
      Aes.IV = IV;

      ICryptoTransform cTransform = Aes.CreateDecryptor();

      byte[] resultArray = cTransform.TransformFinalBlock(toDecryptArray, 0, toDecryptArray.Length);
      decryptedMessage = UTF8Encoding.UTF8.GetString(resultArray, 0, resultArray.Length);
      succeeded = true;
   }
   catch (Exception ex)
   {
      decryptedMessage = "FAILED:*" + cipherText + "*" + ex.Message;
   }
   finally
   {
       if (cTransform != null)
       {
           cTransForm.Dispose();
       }

       if (Aes != null)
       {
           Aes.Clear();
           Aes.Dispose();
       }
   }

   return succeeded;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...