我有以下情况。我需要将 .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
对象时实际上都使用同一个对象。
在对同一服务进行 WCF 调用时,从多个线程重用相同的 X509Certificate2
对象是否安全?
最佳答案
根据 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 个并发的证书相关作业。
关于c# - 在一个线程中实例化 X509Certificate2 对象然后在多个线程中使用它进行 WCF 调用是否可以?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16521440/