2012-03-25 188 views
16

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

下面的代码是一个简单的复制,只有一个基本的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运行ASP.NET MVC 3/.NET 4的应用问题。在本地IIS和.NET控制台应用程序/ .NET中运行的.NET WebForms/.NET 3.5应用程序最近安装了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(在缓冲器计数= 2,输出缓冲器长度= 0,返回代码= CertUnknown)。

我启用了全SCHANNEL logging,看到这样的警告:

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

我跑Wireshark的,看见远程服务器发送一个CertificateRequest,它似乎有一个Distinguished Name条目以准确说明我的客户端证书的CN \ OU \ O值。之后,我的应用程序发送一个证书响应,没有证书。

似乎我错过了设置此证书以便在Windows 7中与.NET应用程序一起正常工作的事情。我最好猜测的是,与XP机器相比,我的新Windows 7机器上有不同的东西现在造成这个失败。我目前无法访问Windows XP环境来确认这一点;我会在几天内,但真的想尽快解决这个问题。

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

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

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

+1

证书是否包含“客户端验证(1.3.6.1.5.5.7.3.2)”增强型密钥用法扩展?如果没有,Chrome可能会被淘汰,因此被选中。 – 2012-03-26 13:45:47

+0

事实证明,客户端证书没有这个增强型密钥用法扩展。我很确定这段代码在过去指向不同的远程URL和不同的客户端证书时正常工作,即使该客户端证书没有客户端验证增强密钥用法。这给了我一个调查的途径,但谢谢。 – 2012-03-26 13:58:11

+0

其实达米安,我错了 - 所有这些证书都包括客户端身份验证增强密钥使用扩展。 – 2012-03-26 15:43:32

回答

8

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

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

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

+5

你能帮助澄清你做了什么来解决这个问题吗? 我正在使用自签名证书,并使用以下来验证证书: ServicePointManager.ServerCertificateValidationCallback = delegate(object s,X509Certificate certificate,X509Chain chain,SslPolicyErrors sslPolicyErrors){return true; }; 我的主机已更新为.net 4.5,现在我的服务返回以下错误“无法创建SSL/TLS安全通道” – Watson 2012-10-09 18:31:04

+0

这也解决了我的问题。我花了整整两周才弄明白这一点。远程服务器仅发送客户端证书,不包括RSA CA根证书。一旦我阅读这个答案,并建议在“证书请求”握手中包含RSA CA根证书的解决方案,它就解决了这个问题。谢谢!!! – vampire203 2016-10-13 19:51:10

7

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

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

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

的解决方案是让服务器完全不发送列表。 (通过编辑注册表值)

HKEY_LOCAL_MACHINE \系统\ CurrentControlSet \控制\ SecurityProviders \ SCHANNEL

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

0

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

System.Net信息:0:ProcessAuthentication(协议= TLS, 密码= TripleDes的168位的强度,散列= SHA1 160位的强度,密钥交换 = RsaKeyX 2048位强度)。

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

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

还有some registry flags允许设置该在不改变现有的代码。