关于.NET应用程序无法发送客户端证书:.NET应用程序无法发送客户端证书 – Win 7 vs Win XP?

.NET application fails to send client certificate - Win 7 vs Win XP?

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

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

1
2
3
4
5
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跟踪并看到了:

SecureChannel#26717201 - We have user-provided certificates. The server has specified 6 issuer(s). Looking for certificates that match any of the issuers.

SecureChannel#26717201 - Left with 0 client certificates to choose from.

...

InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=0, returned code=CertUnknown).

我启用了完整的schannel日志记录,并看到以下警告:

The remote server has requested SSL client authentication, but no suitable client certificate could be found. An anonymous connection will be attempted. This SSL connection request may succeed or fail, depending on the server's policy settings.

我运行wireshark,看到远程服务器发送了一个CertificateRequest,它似乎有一个Distinguished Name条目,其中包含我的客户机证书的cnouo值。之后,我的应用程序发送一个没有证书的证书响应。

在Windows7中,我似乎缺少设置此证书以与.NET应用程序正常工作的功能。我的最佳猜测是,与XP机器相比,我的新Windows7机器上有一些不同之处,这导致了现在的失败。我现在没有访问Windows XP环境的权限来确认这一点;我将在几天内完成,但我真的希望尽快解决这个问题。

任何想法都会受到赞赏。谢谢!

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

另外,我还要注意,远程服务器使用自签名的SSL证书。我最初认为这可能是问题所在,所以我在mmc中添加了一个可信根证书,这样该证书在我的计算机上就显得有效了。这并没有解决问题。


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

我可以使用我的代码成功地将请求发送到需要客户端证书的其他服务器。在发送这个成功的请求时,我在Wireshark中获取了一个捕获,并在将失败的请求发送到另一个服务器时获取了一个捕获。在wireshark捕获中,我找到了"服务器hello",并比较了从远程服务器发送的消息。"好"的远程服务器正在发送我的客户机证书,以及该消息的"证书请求"部分中的根证书。"坏"的远程服务器只发送我的客户端证书。

这提醒我System.NET诊断跟踪读取"服务器已指定6个颁发者…"保留0个客户端证书可供选择"。所以事实证明,"发行人"是这里的关键术语。最初很容易忽略,因为在我最初对TLS握手的分析中,服务器在证书请求中发送了我的客户机证书。事后看来,服务器应该发送根证书,而不是客户机证书本身,这是有意义的。


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

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

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

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

hkey_local_machinesystemcurrentcontrolsetcontrolsecurityprovidersschannel

值名称:sendTrustedSuerList值类型:注册表双字值数据:0(假)


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

System.Net Information: 0 : ProcessAuthentication(Protocol=Tls,
Cipher=TripleDes 168 bit strength, Hash=Sha1 160 bit strength, Key
Exchange=RsaKeyX 2048 bit strength).

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

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

还有一些注册表标志允许在不更改现有代码的情况下进行设置。