.NET应用程序无法发送客户端证书 - Win 7和Win XP的区别?

20

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

下面的代码是一个简单的重现,只包含基本的GET请求。

var r = WebRequest.Create(url) as HttpWebRequest;
r.ClientCertificates = new X509CertificateCollection { myX509Cert };
using (var resp = r.GetResponse() as HttpWebResponse) {
    ...
}
我遇到了我们最常见的异常“Could not create SSL/TLS secure channel”。通常,这些问题指向证书私钥权限的问题。我已经尝试了一切我能想到的来确保证书配置正确,但可能我漏掉了什么。简而言之,远程服务器正在发送带有列表的TLS CertificateRequest,但我的应用程序无法响应任何客户端证书。
这是我的设置:
· Windows 7 Professional 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 - 我们提供了用户证书。服务器已指定6个颁发者。寻找与任何发行人匹配的证书。 SecureChannel#26717201 - 没有客户端证书可供选择。
我启用了完整的SCHANNEL日志记录,看到了这个警告:
远程服务器请求SSL客户端身份验证,但找不到合适的客户端证书。将尝试匿名连接。此SSL连接请求可能会成功或失败,这取决于服务器的策略设置。
我运

1
证书是否包含“客户端身份验证(1.3.6.1.5.5.7.3.2)”的增强密钥用途扩展?如果没有,那么Chrome可能会在允许其被选择时有些调皮。 - Damien_The_Unbeliever
事实证明,客户端证书没有这个增强的密钥用途扩展。我非常确定在过去指向不同远程URL和不同客户端证书时,这段代码是正确工作的,即使该客户端证书没有客户端身份验证增强密钥用途。这给了我一个调查的方向,谢谢。 - Mike Mertsock
实际上,Damien,我错了 - 所有这些证书都包括客户端身份验证增强密钥使用扩展。 - Mike Mertsock
完全没有必要去查维基百科。你已经很好了。 - user1228
3个回答

8

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

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

这让我想起System.Net诊断跟踪读取“服务器已指定6个发行人…剩下0个客户端证书可供选择”。因此,“发行人”是关键术语。最初很容易忽略,因为在我对TLS握手的初始分析中,服务器正在发送我的客户端证书。回想起来,服务器应该发送您的根证书,而不是您的客户端证书本身,这是有道理的。


7
你能帮忙澄清一下你是如何解决这个问题的吗?我正在使用自签名证书,并使用以下内容来验证证书:ServicePointManager.ServerCertificateValidationCallback = delegate(object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; }; 我的主机已经升级到 .net 4.5,现在我的服务返回以下错误信息 "Could not create SSL/TLS secure channel"。 - Watson
这也解决了我的问题。我花了整整两个星期才弄清楚。远程服务器只发送客户端证书,没有包括RSA CA根证书。一旦我阅读了这个答案并建议在“证书请求”握手中包含RSA CA根证书的解决方案,问题就得到了解决。谢谢!!! - vampire203

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

如果发行人列表过长,服务器可能无法发送正确的列表。这似乎是微软的限制。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(False)


0

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

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

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

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

还有一些注册表标志可以在不更改现有代码的情况下设置此项。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接