I have the following situation. I need to load a .pfx file into X509Certificate2
object and use that for WCF calls:
private IThatWcfService GetService()
{
var binding = new WebHttpBinding();
binding.Security.Mode = WebHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType =
HttpClientCredentialType.Certificate;
// some more minor tweaking of the binding object here which makes reusing the
// WebChannelFactory object below impossible
var endpointAddress = new EndpointAddress( ThatServiceUriConstant );
var contract = ContractDescription.GetContract( typeof( IThatWcfService ) );
var endpoint = new ServiceEndpoint( contract, binding, endpointAddress );
var certificate = loadCertificate(); // news X509Certificate2()
var factory = new WebChannelFactory<IThatWcfService >( endpoint );
factory.Credentials.ClientCertificate.Certificate = certificate;
var service = factory.CreateChannel();
return service;
}
Now the problem is there's a bug in Windows Server 2008 which causes two temporary files to leak each time a .pfx file is loaded into X509Certificate2
object. The bug is fixed, but the fix is not available (and will not be available) where the code runs, so I have to work this around.
I've already cached the X509Certificate2
object so that when the same thread calls GetService()
multiple times the X509Certificate2
is reused. The problem is that GetService()
needs to be called from two threads and so I need to care of thread safety of using X509Certificate2
from different threads.
So I'd like to have the following design: I make the X509Certificate2
object static
and whichever thread calls GetService()
first creates and caches the X509Certificate2
object and so all thread in effect use the same object when composing the WebChannelFactory
object.
Will it be safe to reuse the same X509Certificate2
object from multiple threads when making WCF calls to the same service?
From MSDN, this class is "not guaranteed to be thread safe".
From common sense, look at this class's members: there's a Handle
property. This means, that this class is only a wrapper to some lower layer, probably direct wrapper to CAPI services, which I actually don't believe to be really thread-not-safe.
I've managed to find this one discussion: http://marc.info/?l=ms-cryptoapi&m=103430170033615 pointing to some "[email protected]", but it's disputable as an information source.
Browsing X509Certificate and X509Cert2 with TypeDescriptor, it seems that it heavily uses the lower layers and even the getters are uncached and are polling all values directly from CAPI. If you only perform reading, there doesn't seem to be much damageable inside the X509 object, but, of course, since MSDN says "not guaranteed to be thread safe", I will not guarantee it either.
Some important points:
However, I am sure that even if somehow the x509 wrapper gets damaged, the CAPI will not crash and will not corrupt the certificate database. I think that the worst thing that may happen an exception on .Net side
You may also find this question interesting: Mitigating RsaCryptoServiceProvider thread safety issues on a web server
Now, let's step back from this optimistic point of view.
You say about a bug and memory leak. Where was it? Was it in the .Net crypto class library, or was it inside the CAPI? It it was in CAPI, and if you cannot apply fixes at that machine, then you know, I'd rather advise to be a little paranoid a little with it. If opening the same file by two X509 objects and therefore two handles caused a leak, then I'd really to empirically ensure that reusing the same handle from many threads doesn't also leak. Bugs like to walk in swarms and "handle" is more fragile than "filename".
Also, let me drift from the question completely ;) - a known memory leak with known preconditions is usually not dangerous and can be worked around in many ways.
Surely, if you were creating brand new X509 objects on the fly at each service request, you'd end up with memory problems since each object would add new leak. But the leak may be easily contained and sealed to constant value even with no object sharing. For example, you may preconstruct a 10/100/1000 identical x509 objects, drop them into a any thread safe collection, and create a small manager class (few lines of code, really) that will provide access to them on acquire/release basis. This way you will take a memory cost 10/100/1000 times the leak, but it will be known and constant and will not grow further. Of course, it will mean that only 10/100/1000 concurrent certificate-related jobs can be performed at given point of time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With