开发者

Mitigating RsaCryptoServiceProvider thread safety issues on a web server

开发者 https://www.devze.com 2023-03-08 09:37 出处:网络
I have an X509Certificate2 instance, and obtain its PrivateKey property, which is an RsaCryptoServiceProvider.MSDN documents that this RsaCryptoServiceProvider class is not thread-safe.So if given som

I have an X509Certificate2 instance, and obtain its PrivateKey property, which is an RsaCryptoServiceProvider. MSDN documents that this RsaCryptoServiceProvider class is not thread-safe. So if given some X.509 cert I need to perform asymmetric encryption on multiple threads (typical on a web server), what is the best way to create multiple instances of the RsaCryptoServiceProvider?

The private key 开发者_如何学Goon the X509Certificate2 is not marked as exportable, so I cannot simply export parameters on the original RsaCryptoServiceProvider and re-import them into another instance in order to workaround the thread-safety issues.

I obtained the original via the X509Store, but that seems to be a collection of X509Certificate2 instances such that if I want a new instance of RsaCryptoServiceProvider I have to instantiate a new X509Store to find a new X509Certificate2, to obtain a new RsaCryptoServiceProvider. It just seems awfully heavyweight to just getting .NET to clone the RsaCryptoServiceProvider instance.

Are there any better ways?


It would seem that RsaCryptoServiceProvider, despite its MSDN documentation stating it is not thread-safe, is thread-safe enough to encrypt/decrypt on multiple threads at once. I wrote the following app to test high concurrency using this class, and it hasn't crashed or failed to encrypt/decrypt correctly at all:

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

精彩评论

暂无评论...
验证码 换一张
取 消