我正在开发一个ASP.NET Web应用程序,它使用HttpWebRequest向另一台服务器发送请求。它通过HTTPS发送请求,远程服务器需要客户端证书。请求在.NET应用程序中失败,显然无法发送正确的客户端证书。如果我只是使用网络浏览器访问网址(特别是Chrome),我我能够成功连接并发送客户端证书。
下面的代码是一个简单的复制,只有一个基本的GET请求。
var r = WebRequest.Create(url) as HttpWebRequest;
r.ClientCertificates = new X509CertificateCollection { myX509Cert };
using (var resp = r.GetResponse() as HttpWebResponse) {
...
}
我得到了我们最喜欢的例外,"无法创建SSL / TLS安全通道"。通常,这些类型的问题指出了证书的私钥权限问题。我尝试了所有我能想到的东西,以确保这一切都正确配置,但也许我错过了一些东西。简而言之,远程服务器正在发送一个TLS CertificateRequest
,其列表似乎可以正确识别我的客户端证书,但我的应用程序无法响应任何客户端证书。
这是我的设置:
以下是我尝试的所有内容,以及我所知道的内容:
HasPrivateKey
= true PreAuthenticate = true
。没有什么区别ServicePointManager.Expect100Continue = false
,没有什么区别ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3
,但显然远程服务器需要TLS才能帮助ServicePointManager.ServerCertificateValidationCallback
委托以始终返回true。但是在TLS握手之前,请求失败了,甚至在它调用此委托之前就失败了我添加System.Net trace并看到了这一点:
SecureChannel#26717201 - 我们有用户提供的证书。服务器指定了6个发行者。寻找与任何发行人匹配的证书。
SecureChannel#26717201 - 剩下0个客户端证书可供选择。
...
InitializeSecurityContext(In-Buffers count = 2,Out-Buffer length = 0,返回代码= CertUnknown)。
我已启用完整SCHANNEL logging,并看到此警告:
远程服务器已请求SSL客户端身份验证,但找不到合适的客户端证书。将尝试匿名连接。此SSL连接请求可能成功或失败,具体取决于服务器的策略设置。
我运行了Wireshark,看到远程服务器发送了CertificateRequest
,它似乎有一个Distinguished Name
条目,其中指定了客户端证书的CN \ OU \ O值。之后,我的应用程序发送没有证书的证书响应。
似乎我错过了设置此证书以便在Windows 7中与.NET应用程序一起正常工作的东西。我最好的猜测是,与XP机器相比,我的新Windows 7机器上有一些不同的东西,即导致此失败现在。我目前无法访问Windows XP环境以确认这一点;我会在几天内但是真的想尽快解决这个问题。
任何想法都会非常感激。谢谢!
编辑如上所述,当连接到Chrome中的网址时,浏览器会要求我提供客户端证书,我可以提供正确的证书并成功连接。但是,我无法在Internet Explorer(9)中成功完成此操作。我只是得到了#34; Internet Explorer无法显示网页",没有其他提示或解释。我被告知这可能是相关的,因为WebRequest.Create
具有与IE类似的行为。我正在调查这可能意味着什么,但会重视对此的任何想法。
编辑另外,我应该注意远程服务器使用自签名SSL证书。我原本以为这可能是问题,所以我将证书添加为MMC中的受信任根,以便证书在我的计算机上显示为有效。这并没有解决问题。
答案 0 :(得分:8)
事实证明这是一个相当简单的问题,但很难发现。我的应用程序在其密钥库中具有我的客户端证书的远程服务器,但不是我的客户端证书信任链中的任何根证书。
我能够使用我的代码成功地将请求发送到需要客户端证书的其他服务器。我在发送此成功请求时在Wireshark中捕获,并在将失败的请求发送到其他服务器时进行捕获。在Wireshark捕获中,我找到了“Server Hello”并比较了从远程服务器发送的消息。 “好的”远程服务器正在发送我的客户端证书,也也在该消息的“证书请求”部分中发送其根证书。 “坏”远程服务器只发送了我的客户端证书。
这提醒我System.Net诊断跟踪读取“服务器已指定6个发行者...剩下0个客户端证书可供选择”。事实证明,“发行人”是这里的关键术语。最初很容易忽略,因为在我对TLS握手的初步分析中,服务器在证书请求中发送了我的客户端证书。事后看来,服务器应该发送根证书而不是客户端证书本身是有道理的。
答案 1 :(得分:7)
我想为问题添加另一个“解决方案”。
如果该列表太长,服务器可能无法发送正确的颁发者列表。 这似乎是微软的限制。 http://support.microsoft.com/kb/933430
来源: http://netsekure.org/2011/04/tls-client-authentication-and-trusted-issuers-list/
解决方案是要求服务器根本不发送列表。 (通过编辑注册表值)
HKEY_LOCAL_MACHINE \ SYSTEM \ CURRENTCONTROLSET \控制\ SecurityProviders \ SCHANNEL
值名称:SendTrustedIssuerList 值类型:REG_DWORD 值数据:0(假)
答案 2 :(得分:0)
对我来说,这些确切的症状是由使用服务器不允许的TLS 1.0引起的。这可以在跟踪日志中找到:
System.Net信息:0:ProcessAuthentication(协议= Tls , 密码= TripleDes 168位强度,Hash = Sha1 160位强度,密钥 交换= RsaKeyX 2048位强度)。
TLS 1.0是.NET 4.5的默认设置,但可以通过设置覆盖它:
ServicePointManager.SecurityProtocol =
SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
还有some registry flags允许在不更改现有代码的情况下进行设置。