Потокобезопасность классов шифрования .NET? - PullRequest
13 голосов
/ 12 июня 2009

У меня есть общая цель - создать служебный класс static , который инкапсулирует шифрование для моего приложения .NET. Внутри я бы хотел минимизировать ненужные создания объектов.

У меня такой вопрос: Что такое потокобезопасность классов, которые реализуют симметричное шифрование в .NET Framework? В частности System.Security.Cryptography.RijndaelManaged и типы ICryptoTransform, которые он генерирует.

Например, в конструкторе моего класса я могу просто сделать что-то вроде следующего:

static MyUtility()
{
    using (RijndaelManaged rm = new RijndaelManaged())
    {
        MyUtility.EncryptorTransform = rm.CreateEncryptor(MyUtility.MyKey, MyUtility.MyIV);
        MyUtility.DecryptorTransform = rm.CreateDecryptor(MyUtility.MyKey, MyUtility.MyIV);
    }
}

Обойдя стороной вопрос о том, насколько безопасно иметь Ключ и IV в этом классе , этот пример блока поднимает ряд других вопросов:

  1. Могу ли я снова и снова использовать EncryptorTransform и DecryptorTransform снова и снова? Свойства *.CanReuseTransform и *.CanTransformMultipleBlocks подразумевают "да", но есть ли какие-то предостережения, о которых я должен знать?

  2. Поскольку RijndaelManaged реализует IDisposable, я склонен помещать его в блок using, тем более что он, вероятно, связан с внешними библиотеками уровня ОС. Есть ли какие-то предостережения с этим, так как я держу ICryptoTransform объекты вокруг?

  3. Потенциально самый важный вопрос, в многопоточной среде, возникнут ли у меня проблемы с разделением объектов ICryptoTransform между потоками?

  4. Если ответ на вопрос № 3 заключается в том, что он не является поточно-ориентированным, столкнусь ли я с серьезным снижением производительности из-за блокировки при использовании объектов ICryptoTransform? (Зависит от нагрузки, я полагаю.)

  5. Будет ли эффективнее просто создавать новые RijndaelManaged каждый раз? Или хранить один RijndaelManaged и генерировать new RijndaelManaged().CreateEncryptor(...) каждый раз?

Я надеюсь, что кто-то там знает, как они работают под капотом или испытывают проблемы с аналогичными реализациями. Я обнаружил, что многие из этих проблем с производительностью и потоками, как правило, не проявляются до тех пор, пока не произойдет значительная нагрузка.

Спасибо!

Ответы [ 2 ]

15 голосов
/ 12 июня 2009

1) Да.

2) Если вы им распоряжаетесь, вы не можете его использовать. До этого момента вы можете поделиться / использовать его (но см. Ниже)

3-4) От MSDN :

«Любые открытые статические (Shared в Visual Basic) члены этого типа являются поточно-ориентированными. Любые члены экземпляра не гарантируют поточно-ориентированные.»

Если вы хотите сохранить это и разделить его между потоками, вам нужно реализовать блокировку и рассматривать ее как заблокированный ресурс. В противном случае, я бы порекомендовал просто делать отдельные версии по мере необходимости и утилизировать их, когда вы закончите.

5) Я бы порекомендовал создать их по мере необходимости, а затем попытаться оптимизировать их, если позже у вас возникнут проблемы с производительностью. Не беспокойтесь о влиянии на производительность при создании новой версии, пока не увидите, что это проблема после профилирования.

4 голосов
/ 21 июня 2013

Можно решить проблему параллелизма просто с помощью кэша, основанного на параллельном стеке:

static ConcurrentStack<ICryptoTransform> decryptors = new ConcurrentStack<ICryptoTransform>();

void Encrypt()
{
   // Pop decryptor from cache...
   ICryptoTransform decryptor;
   if (!decryptors.TryPop(out decryptor))
   {
       // ... or create a new one since cache is depleted
       AesManaged aes = new AesManaged();
       aes.Key = key;
       aes.IV = iv;
       decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
    }

    try
    {
       //// use decryptor
    }
    finally
    {
       decryptors.Push(decryptor);
    }
 }
...