Устранение проблем с безопасностью потоков RsaCryptoServiceProvider на веб-сервере

У меня есть экземпляр X509Certificate2, и я получаю его свойство PrivateKey, которое является RsaCryptoServiceProvider. Документы MSDN указывают, что этот класс RsaCryptoServiceProvider не ориентирован на многопотоковое исполнение. Так что, если у меня есть какой-то сертификат X.509, мне нужно выполнить асимметричное шифрование в нескольких потоках (обычно на веб-сервере), как лучше всего создать несколько экземпляров RsaCryptoServiceProvider?

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

Я получил оригинал через X509Store, но, похоже, это набор экземпляров X509Certificate2, поэтому, если я хочу новый экземпляр RsaCryptoServiceProvider, мне нужно создать экземпляр нового X509Store, чтобы найти новый< /em> X509Certificate2, чтобы получить новый RsaCryptoServiceProvider. Просто заставить .NET клонировать экземпляр RsaCryptoServiceProvider кажется ужасно тяжеловесным.

Есть ли лучшие способы?


person Andrew Arnott    schedule 26.05.2011    source источник


Ответы (1)


Казалось бы, RsaCryptoServiceProvider, несмотря на то, что в документации MSDN указано, что он не является потокобезопасным, является достаточно потокобезопасным, чтобы шифровать/дешифровать несколько потоков одновременно. Я написал следующее приложение для проверки высокого уровня параллелизма с использованием этого класса, и оно вообще не давало сбоев или не могло правильно зашифровать/дешифровать:

using System;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Threading;

namespace ConsoleApplication1 {
    class Program {
        static bool exit;

        static void Main(string[] args) {
            var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            try {
                store.Open(OpenFlags.OpenExistingOnly);

                Func<RSACryptoServiceProvider> rsaFactory = null;
                X509Certificate2 winningCert = null;
                exit = true;
                foreach (X509Certificate2 cert in store.Certificates) {
                    try {
                        var result = store.Certificates.Find(X509FindType.FindByThumbprint, cert.Thumbprint, false).Cast<X509Certificate2>().FirstOrDefault();
                        rsaFactory = () => (RSACryptoServiceProvider)result.PrivateKey;
                        UseRsa(rsaFactory());
                        winningCert = cert;
                        break;
                    } catch (CryptographicException) {
                        Console.WriteLine("Cert {0} failed", cert.Thumbprint);
                    }
                }

                exit = false;
                Console.WriteLine("Winning cert: {0}", winningCert.Thumbprint);
                RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)winningCert.PrivateKey;
                rsaFactory = () => rsa;
                Thread[] threads = new Thread[16];
                for (int i = 0; i < threads.Length; i++) {
                    threads[i] = new Thread(state => UseRsa(rsaFactory()));
                    threads[i].Start();
                }

                Thread.Sleep(10000);

                exit = true;
                for (int i = 0; i < threads.Length; i++) {
                    threads[i].Join();
                }

                Console.WriteLine("Success.");
            } finally {
                store.Close();
            }
        }

        static void UseRsa(RSACryptoServiceProvider rsa) {
            var rng = RandomNumberGenerator.Create();
            var buffer = new byte[64];

            do {
                rng.GetBytes(buffer);
                var cipher = rsa.Encrypt(buffer, true);

                var plaintext = rsa.Decrypt(cipher, true);
                for (int i = 0; i < buffer.Length; i++) {
                    if (buffer[i] != plaintext[i]) {
                        Debugger.Break();
                    }
                }
            } while (!exit);
        }
    }
}
person Andrew Arnott    schedule 27.05.2011
comment
Я думаю, причина того, что это работает, заключается в том, что и X509Certificate2.PrivateKey, и X509Certificate2.PublicKey.Key создают новые экземпляры RSACryptoServiceProvider при каждом вызове. - person liwp; 29.11.2012