c# - 在一个线程中实例化 X509Certificate2 对象然后在多个线程中使用它进行 WCF 调用是否可以?

标签 c# .net x509certificate2

我有以下情况。我需要将 .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/

相关文章:

c# - 在 EF Code First 中, Foo 是否可以拥有 Bars 集合以及 "FavouriteBar"?

c# - 如何修复 MSSQL 上的 "Invalid Column Name"SQL 异常

c# - x509certificate2 中的空私钥

c# - 如何禁用对用于验证消息签名的 x509 证书的验证?

c# - 在 C# 中使用 Moq 设置索引器

c# - 将简单的 ForEach 转换为 Linq 语句

c# - 正则表达式:匹配表达式周围最接近的开闭花括号内的所有内容

c# - Azure 移动应用身份验证

.net - PrintDialog.ShowDialog(this) 在 Windows 7 上立即返回 DialogResult.Cancel

security - 据称,在 .NET 4.5 中实现 IDisposable 的 X509Store 在哪里?