Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Would it be okay to instantiate X509Certificate2 object in one thread and then use it in multiple threads for WCF calls?

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?

like image 859
sharptooth Avatar asked May 13 '13 11:05

sharptooth


1 Answers

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:

  • X509 object is just a manager wrapper to the CAPI context and handle
  • X509 object does not seem to cache anything, but CAPI may do it on per-HANDLE basis
  • X509 obj does some error checking (even in getters) on the .Net side and throws exceptions if the CAPI feels that the request was invalid
  • CAPI is almost surely thread safe in terms of READing certificates
  • CAPI will almost certainly return errors when a certificate deletion occurs in the meantime or something equally evil happens - but you have no control over it, as the user may i.e. uninstall that certificate manually - and this makes me even more sure that CAPI is completely internally fortified against multithreading.

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.

like image 195
quetzalcoatl Avatar answered Sep 27 '22 19:09

quetzalcoatl