Безопасность потоков .NET классов шифрования?
У меня есть цель высокого уровня создания класса 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 в этом классе, этот блок примеров вызывает ряд других вопросов:
-
Можно ли повторно использовать EncryptorTransform и DecryptorTransform снова и снова? Свойства *.CanReuseTransform
и *.CanTransformMultipleBlocks
подразумевают "да", но есть ли какие-либо оговорки, о которых я должен знать?
-
Так как RijndaelManaged
реализует IDisposable
, мой наклон заключается в том, чтобы поместить его в блок using
, особенно потому, что он, вероятно, связан с внешними lib файлами на уровне ОС. Есть ли какие-либо оговорки в этом, поскольку я поддерживаю объекты ICryptoTransform
?
-
Потенциально самый важный вопрос в многопотоковой среде я столкнулся с проблемами при совместном использовании объектов ICryptoTransform
между потоками?
-
Если ответ на №3 заключается в том, что он не является потокобезопасным, будет ли я испытывать серьезную деградацию производительности от блокировки, когда я использую объекты ICryptoTransform
? (Зависит от нагрузки, я полагаю.)
-
Было бы более сложно просто создавать новые RijndaelManaged
каждый раз? Или сохранить один RijndaelManaged
и генерировать new RijndaelManaged().CreateEncryptor(...)
каждый раз?
Я надеюсь, что кто-то там знает, как они работают под капотом или испытывают проблемы из подобных реализаций. Я обнаружил, что многие из этих видов производительности и связанных с потоком проблем обычно не проявляются до тех пор, пока не будет значительного количества нагрузки.
Спасибо!
Ответы
Ответ 1
1) Да.
2) Вы избавляетесь от него, вы не можете его использовать. До этого момента вы можете использовать/использовать его (но см. Ниже).
3-4) Из MSDN:
"Любые публичные статические (Shared in Visual Basic) члены этого типа являются потокобезопасными. Любые члены экземпляра не гарантируют безопасность потоков."
Если вы хотите сохранить это и поделиться им между потоками, вам нужно будет реализовать блокировку и рассматривать ее как заблокированный ресурс. В противном случае я бы рекомендовал просто делать отдельные версии по мере необходимости и избавляться от них, когда вы закончите.
5) Я бы рекомендовал создать их по мере необходимости, а затем попытаться оптимизировать его, если позже вы обнаружите, что у вас есть проблемы с производительностью. Не беспокойтесь о влиянии производительности на создание новой версии, пока не увидите, что ее проблема после профилирования.
Ответ 2
Можно решить проблему concurrency просто с кешем на основе параллельного стека:
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);
}
}