Mitigating RsaCryptoServiceProvider thread safety issues on a web server
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);
}
}
}
精彩评论