【问题标题】:.NET application fails to send client certificate - Win 7 vs Win XP?.NET 应用程序无法发送客户端证书 - Win 7 与 Win XP?
【发布时间】:2012-04-09 03:20:10
【问题描述】:

我正在开发一个使用 HttpWebRequest 向另一台服务器发送请求的 ASP.NET Web 应用程序。它通过 HTTPS 发送请求,远程服务器需要客户端证书。 .NET 应用程序中的请求失败,显然无法发送正确的客户端证书。如果我只需使用网络浏览器(特别是 Chrome)访问 URL,我能够成功连接并发送客户端证书。

下面的代码是一个简单的复制,只有一个基本的 GET 请求。

var r = WebRequest.Create(url) as HttpWebRequest;
r.ClientCertificates = new X509CertificateCollection { myX509Cert };
using (var resp = r.GetResponse() as HttpWebResponse) {
    ...
}

我得到了我们最喜欢的异常,“无法创建 SSL/TLS 安全通道”。通常,这些类型的问题指向证书私钥的权限问题。我尝试了我能想到的一切来确保这一切都正确配置,但也许我错过了一些东西。长话短说,远程服务器正在发送一个 TLS CertificateRequest,其中包含一个似乎可以正确识别我的客户端证书的列表,但我的应用程序无法响应任何客户端证书。

这是我的设置:

  • Windows 7 专业版 64 位
  • 能够在 Visual Studio 开发服务器中运行的 ASP.NET MVC 3 / .NET 4 应用程序、在本地 IIS 中运行的 ASP.NET WebForms / .NET 3.5 应用程序以及 .NET 控制台应用程序 / .NET 4
  • 最近安装了 Microsoft .NET Framework 4.5。尚未检查这是否是个问题

这是我尝试过的所有内容,以及我所知道的:

  • 此代码在 Windows XP 机器上运行时似乎运行良好
  • 我确保将我的客户端证书导入到本地计算机的个人证书存储中,并为我自己和所有相关 IIS 用户正确配置了私钥权限
  • 我尝试在我的机器上重新安装证书几次
  • 我已确认.NET 应用程序可以访问X509 证书和HasPrivateKey = true
  • 确保我的客户端证书有效。它实际上是运行此应用程序的 Web 服务器的 SSL 证书
  • 我在请求对象中设置了PreAuthenticate = true。没有影响
  • 我试过设置ServicePointManager.Expect100Continue = false,没有效果
  • 我尝试设置 ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3,但显然远程服务器需要 TLS,所以没有帮助
  • 我将ServicePointManager.ServerCertificateValidationCallback 委托设置为始终返回true。但是请求在 TLS 握手的早期就失败了,甚至在调用这个委托之前就失败了
  • 当我在 Chrome 中访问 URL 时,它要求我提供客户端证书,并提供正确的客户端证书作为选项,我选择了该证书并得到有效响应。当我在 Fiddler 中构建请求时也可以正常工作。因此,这绝对是我的 .NET 代码的具体问题,而不是证书本身或远程服务器等。
  • 与我合作的供应商在不同的远程服务器上设置了相同的服务,并且该服务需要不同的客户端证书。所以我用不同的 URL 和客户端证书尝试了同样的事情并得到了同样的失败

我添加了System.Net trace 并看到了这个:

SecureChannel#26717201 - 我们有用户提供的证书。服务器已指定 6 个颁发者。寻找与任何颁发者匹配的证书。

SecureChannel#26717201 - 剩下 0 个客户端证书可供选择。

...

InitializeSecurityContext(In-Buffers 计数=2,Out-Buffer 长度=0,返回代码=CertUnknown)。

我启用了完整的SCHANNEL logging,并看到了这个警告:

远程服务器已请求 SSL 客户端身份验证,但找不到合适的客户端证书。将尝试匿名连接。此 SSL 连接请求可能成功或失败,具体取决于服务器的策略设置。

我运行了 Wireshark,看到远程服务器发送了一个 CertificateRequest,并且它似乎有一个 Distinguished Name 条目,其中我的客户端证书的 CN\OU\O 值被准确指定。之后,我的应用程序发送一个没有证书的证书响应。

似乎我在设置此证书以在 Windows 7 中与 .NET 应用程序正常工作时缺少一些东西。我最好的猜测是,与 XP 机器相比,我的新 Windows 7 机器上有一些不同之处,即导致现在失败。我目前无法访问 Windows XP 环境来确认这一点;我会在几天后解决这个问题。

任何想法将不胜感激。谢谢!

EDIT 如上所述,在 Chrome 中连接 URL 时,浏览器要求我提供客户端证书,我可以提供正确的证书并成功连接。但是,我无法在 Internet Explorer (9) 中成功执行此操作。我只是得到“Internet Explorer 无法显示网页”,没有其他提示或解释。有人告诉我这可能是相关的,因为WebRequest.Create 具有与 IE 相似的行为。我正在研究这可能意味着什么,但会重视对此的任何想法。

编辑 另外,我应该注意远程服务器使用自签名 SSL 证书。我最初认为这可能是问题所在,因此我将证书添加为 MMC 中的受信任根,以便证书在我的机器上显示为有效。这并没有解决问题。

【问题讨论】:

  • 证书是否包含“客户端身份验证 (1.3.6.1.5.5.7.3.2)”增强密钥使用扩展?如果没有,可能是 Chrome 不小心允许它被选中。
  • 原来客户端证书没有这个Enhanced Key Usage扩展。我很确定该代码过去在指向不同的远程 URL 和不同的客户端证书时可以正常工作,即使该客户端证书没有客户端身份验证增强密钥使用。不过,这给了我一个调查的途径,谢谢。
  • 实际上 Damien,我错了 - 所有这些证书都包含客户端身份验证增强密钥使用扩展。
  • 完全没有必要在wiki上做这个。你们都很好。

标签: .net client certificate ssl


【解决方案1】:

结果证明这是一个相当简单的问题,但很难发现。我的应用程序的远程服务器在其密钥库中有我的客户端证书,但在我的客户端证书的信任链中没有任何根证书。

我能够使用我的代码成功地将请求发送到需要客户端证书的不同服务器。我在发送此成功请求时在 Wireshark 中进行了捕获,并且在将失败的请求发送到另一台服务器时也进行了捕获。在 Wireshark 捕获中,我找到了“Server Hello”,并比较了从远程服务器发送的消息。 “好”的远程服务器在该消息的“证书请求”部分发送我的客户端证书和它的根证书。 “坏”的远程服务器只发送我的客户端证书。

这提醒了我 System.Net 诊断跟踪显示“服务器已指定 6 个颁发者......剩下 0 个客户端证书可供选择”。所以事实证明,“发行人”是这里的关键术语。最初很容易忽略,因为在我对 TLS 握手的初步分析中,服务器在证书请求中发送了我的客户端证书。事后看来,服务器应该发送您的根证书而不是您的客户端证书本身是有道理的。

【讨论】:

  • 您能否帮助澄清您为解决此问题所做的工作?我使用的是自签名证书,只使用以下内容来验证证书:ServicePointManager.ServerCertificateValidationCallback = delegate(object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; };我的主机已更新到 .net 4.5,现在我的服务返回以下错误“无法创建 SSL/TLS 安全通道”
  • 这也解决了我的问题。我花了整整 2 周的时间才弄清楚这一点。远程服务器仅发送客户端证书,不包括 RSA CA 根证书。一旦我阅读了这个答案并建议了在“证书请求”握手中包含 RSA CA 根证书的解决方案,它就解决了这个问题。谢谢!!!
【解决方案2】:

我想为这个问题添加另一个“解决方案”。

如果列表太长,服务器可能无法发送正确的颁发者列表。 这似乎是微软的限制。 http://support.microsoft.com/kb/933430

来源: http://netsekure.org/2011/04/tls-client-authentication-and-trusted-issuers-list/

解决方案是要求服务器根本不发送列表。 (通过编辑注册表值)

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL

值名称:SendTrustedIssuerList 值类型:REG_DWORD 数值数据:0(假)

【讨论】:

    【解决方案3】:

    对我来说,这些确切的症状是由使用服务器不允许的 TLS 1.0 引起的。这可以在跟踪日志中找到,如下所示:

    System.Net 信息:0 : ProcessAuthentication(Protocol=Tls, Cipher=TripleDes 168 位强度,Hash=Sha1 160 位强度,密钥 Exchange=RsaKeyX 2048 位强度)。

    TLS 1.0 是 .NET 4.5 的默认设置,但可以通过设置覆盖它:

    ServicePointManager.SecurityProtocol = 
        SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
    

    还有some registry flags 允许在不更改现有代码的情况下进行设置。

    【讨论】:

      猜你喜欢
      • 2011-04-24
      • 1970-01-01
      • 1970-01-01
      • 2022-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-17
      • 1970-01-01
      相关资源
      最近更新 更多