我们设置了一台 2008R2 IIS 服务器,其中的站点配置为需要客户端证书。我们的测试客户端无法正常工作,我们正在尝试调试原因。
在此过程中,我们设置了一个新的 Server 2008 R2 盒(是的,我知道它很旧,但这就是运行软件的东西)来尝试复制或确定排除故障的方法。
我们正在调查的一种途径是 TLS 握手。测试应用程序是用 .NET 编写的,在System.Diagnostics
启用了适当的调试后,它会将以下条目放入日志文件中:
System.Net 信息:0:[22724] SecureChannel#48979325 - 我们有用户提供的证书。服务器已指定 10 个颁发者。正在查找与任何颁发者匹配的证书。
我们无法看到此发行者列表,因此我们打开了 OpenSSL。运行以下命令:
openssl s_client -connect win2k8r2-1.hsl10690.test:443 -state -no_ticket -servername win2k8r2-1.hsl10690.test
输出结果如下:
[...]
-----END CERTIFICATE-----
subject=/CN=testcert.hsl10690.test
issuer=/CN=Internal Dev CA 1
---
No client certificate CA names sent
---
SSL handshake has read 1013 bytes and written 329 bytes
[...]
因此,我们遇到了不匹配的情况,Microsoft 堆栈声明服务器指定了 10 个颁发者,但 OpenSSL 报告服务器未发送 CA 名称。
在实时系统的情况下,System.Diagnostics 日志报告服务器指定的 130 多个发行者,但 OpenSSL 仍然返回零。
我们认为问题在于我们提供的客户端证书与其中一个颁发者不匹配(但我们已经验证了根证书位于服务器的信任存储中,并且我们已经在服务器之外验证了证书)。在实时服务器上,我们在日志中看到“服务器已指定...“ 信息:
System.Net Information: 0 : [36484] SecureChannel#33675143 - We have user-provided certificates. The server has specified 133 issuer(s). Looking for certificates that match any of the issuers.
ProcessId=20372
DateTime=2018-12-20T13:33:39.9042036Z
System.Net Information: 0 : [36484] SecureChannel#33675143 - Left with 0 client certificates to choose from.
ProcessId=20372
DateTime=2018-12-20T13:33:39.9052036Z
在测试中,一切正常,它显示:
System.Net Information: 0 : [22724] SecureChannel#48979325 - We have user-provided certificates. The server has specified 10 issuer(s). Looking for certificates that match any of the issuers.
ProcessId=22100
DateTime=2018-12-21T13:52:23.3718249Z
System.Net Information: 0 : [22724] SecureChannel#48979325 - Selected certificate: [Version]
V3
[Subject]
我们如何才能找出服务器返回了哪些证书?如果我们发现列表中缺少颁发者,那么是什么原因导致根证书无法被包含进去?我不排除我们错过了一些显而易见的东西,但我们还没有发现。
答案1
我们终于找到了答案:问题出在 KB931125。博客文章描述该 KB 仅用于客户端目标,但已发送到服务器,导致 IIS 发送的 CA 列表被截断。它并没有真正解释为什么我们无法使用 OpenSSL 查看传输的 CA 列表,但最终它确实让我们找到了根本原因。
博客文章指出进一步的 MSDN 文章更详细地描述了该问题:
如果 TLS/SSL 服务器在受信任的根认证列表中包含许多条目,则可能会出现这些问题。如果满足以下条件,服务器将向客户端发送受信任的证书颁发机构列表:
- 服务器使用传输层安全性 (TLS)/SSL 协议来加密网络流量。
- 在认证握手过程中,需要客户端证书来进行认证。
此受信任证书颁发机构列表表示服务器可以接受客户端证书的颁发机构。要通过服务器的身份验证,客户端必须拥有证书链中存在的证书,该证书链指向来自服务器列表的根证书。这是因为客户端证书始终是链末端的最终实体证书。客户端证书不是链的一部分。
目前,Schannel 安全包支持的受信任证书颁发机构列表的最大大小在 Windows Server 2008、Windows Server 2008 R2 和 Windows Server 2012 中为 16 KB。
还会记录一个事件日志条目,内容如下(我们第一次查看时错过了):
当要求客户端身份验证时,此服务器会向客户端发送受信任的证书颁发机构列表。客户端使用此列表选择服务器信任的客户端证书。目前,此服务器信任的证书颁发机构太多,列表变得太长。因此,此列表已被截断。此计算机的管理员应检查客户端身份验证所信任的证书颁发机构,并删除那些实际上不需要信任的证书颁发机构。
MSDN 文章确实列出了修复方法:
删除以下注册表项:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SystemCertificates\AuthRoot\证书
为此,请按照下列步骤操作:
- 启动注册表编辑器
- 找到以下注册表子项: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SystemCertificates\AuthRoot
- 右键单击然后删除名为“证书”的项