5

我有以下情况。我需要将 .pfx 文件加载到X509Certificate2对象中并将其用于 WCF 调用:

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

现在的问题是 Windows Server 2008 中存在一个错误,每次将 .pfx 文件加载到X509Certificate2对象中时,都会导致两个临时文件泄漏。该错误已修复,但该修复在代码运行的地方不可用(并且将不可用),所以我必须解决这个问题。

我已经缓存了这个X509Certificate2对象,这样当同一个线程GetService()多次调用时,它X509Certificate2就会被重用。问题是GetService()需要从两个线程调用,所以我需要注意X509Certificate2从不同线程使用的线程安全。

所以我想有以下设计:我制作X509Certificate2对象static,无论哪个线程GetService()首先调用都会创建并缓存X509Certificate2对象,因此所有线程在组合对象时实际上都使用相同的WebChannelFactory对象。

X509Certificate2在对同一个服务进行 WCF 调用时,从多个线程重用同一个对象是否安全?

4

1 回答 1

7

从 MSDN,这个类“不保证是线程安全的”。

从常识来看,看看这个类的成员:有一个Handle属性。这意味着,这个类只是某个较低层的包装器,可能是 CAPI 服务的直接包装器,我实际上并不认为它是真正的线程不安全的。

我设法找到了这个讨论:http ://marc.info/?l=ms-cryptoapi&m= 103430170033615 指向一些“discussions@microsoft.com”,但它作为信息来源是有争议的。

使用 TypeDescriptor 浏览 X509Certificate 和 X509Cert2,似乎它大量使用了较低的层,甚至 getter 都未缓存并且直接从 CAPI 轮询所有值。如果只进行阅读,X509 对象内部似乎没有太大的可损坏性,但是,当然,既然 MSDN 说“不保证是线程安全的”,我也不会保证。

一些重要的点:

  • X509 对象只是 CAPI 上下文和句柄的管理器包装器
  • X509 对象似乎没有缓存任何东西,但 CAPI 可能会在每个 HANDLE 的基础上进行
  • X509 obj 在 .Net 端进行一些错误检查(即使在 getter 中),如果 CAPI 认为请求无效则抛出异常
  • CAPI 在阅读证书方面几乎肯定是线程安全的
  • 当同时发生证书删除或同样邪恶的事情发生时,CAPI 几乎肯定会返回错误 - 但你无法控制它,因为用户可能会手动卸载该证书 - 这让我更加确定 CAPI 完全在内部强化了多线程。

但是,我确信即使 x509 包装器以某种方式损坏,CAPI 也不会崩溃,也不会损坏证书数据库。我认为在.Net方面可能发生的最糟糕的事情是例外

您可能还会发现这个问题很有趣:Mitigating RsaCryptoServiceProvider thread safety issues on a web server

现在,让我们从这种乐观的观点退后一步。

您说的是错误和内存泄漏。它在哪里?它是在 .Net 加密类库中,还是在 CAPI 中?它是在 CAPI 中,如果你不能在那台机器上应用修复,那么你知道,我宁愿建议对它有点偏执。如果通过两个 X509 对象打开同一个文件,因此两个句柄导致泄漏,那么我真的要凭经验确保从多个线程重用同一个句柄也不会泄漏。虫子喜欢成群结队地走,“句柄”比“文件名”更脆弱。

另外,让我完全脱离这个问题;)-具有已知先决条件的已知内存泄漏通常并不危险,可以通过多种方式解决。

当然,如果您在每次服务请求时动态创建全新的 X509 对象,您最终会遇到内存问题,因为每个对象都会添加新的泄漏。但是即使没有对象共享,泄漏也很容易被控制并密封为恒定值。例如,您可以预先构造一个 10/100/1000 相同的 x509 对象,将它们放入任何线程安全的集合中,然后创建一个小型管理器类(实际上是几行代码),以在获取/释放的基础上提供对它们的访问. 这样,您将占用 10/100/1000 倍泄漏的内存成本,但它将是已知的且恒定的,并且不会进一步增长。当然,这意味着在给定时间点只能执行 10/100/1000 个并发证书相关作业。

于 2013-05-13T12:49:50.567 回答