获取包括根证书在内的完整证书链

时间:2014-12-02 08:18:22

标签: ssl openssl

如何获得服务器的完整证书链?虽然有些人声称one should be able to do just thatopenssl s_client -showcerts,但情况并非总是如此。

echo | openssl s_client -CApath /etc/ssl/certs -connect www.ssllabs.com:443 \
                        -showcerts | grep -B2 BEGIN
depth=3 C = SE, O = AddTrust AB, OU = AddTrust External TTP Network, CN = AddTrust External CA Root
verify return:1
depth=2 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO RSA Certification Authority
verify return:1
depth=1 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO RSA Domain Validation Secure Server CA
verify return:1
depth=0 OU = Domain Control Validated, OU = PositiveSSL, CN = www.ssllabs.com
verify return:1
 0 s:/OU=Domain Control Validated/OU=PositiveSSL/CN=www.ssllabs.com
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
-----BEGIN CERTIFICATE-----
--
 1 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
-----BEGIN CERTIFICATE-----
--
 2 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
-----BEGIN CERTIFICATE-----
DONE

这里我们有四个证书。除AddTrust External CA Root证书之外的所有证书。 (可能是因为它没有被包含在证书包中。并且不是这样的必需。是的,我可以在/etc/ssl/certs找到丢失的那个)

如何以全自动方式获取服务器的所有证书?

2 个答案:

答案 0 :(得分:3)

Meta:我试图在超级用户中回答这个问题,但你删除了它。幸运的是,当我发现这个副本时,我的大部分工作仍然是在一个划痕记事本中,我还没有关闭,否则我不愿意两次做研究工作。

s_client -showcerts显示服务器的已发送证书;根据RFC,此是向上的有效链之外,可以省略根MAY(在RFC2119定义中,即允许但不特别推荐)。但是,并非所有服务器都配置正确,有些服务器可能会发送额外的,丢失的和/或无序的证书。此外,根据使用的CA,可能有多个有效链,但服务器只能发送一个。 openssl目前只会使用已发送的链,但这会在1.0.2中很快发生变化,而其他服务器有时会找到与发送链不同的链。

openssl:如果收到的链完成并且可能包括使用的信任库中的根(其默认位置取决于系统或构建,并且任何情况都可以被覆盖)然后openssl客户端将验证它是好的 - 除非它已过期,或被撤销,并且该信息可用,通常它不是。在这种情况下,您可以编写一个客户端程序,该程序在设置 cert-verify回调函数后连接,该函数输出验证循环处理的完整证书,或者您想要的其他信息,而不是{ {1}}使用一个回调记录(仅)s_client行中的主题名称,您可以在示例中看到,这里包含链中的所有4个证书。 openssl是opensource,因此一个主要像s_client一样的客户端程序可能是s_client的修改副本(在本例中特别是s_cb.c)。

Java 也可以这样做,并且编写起来要短一些,但需要安装Java。如果收到的链验证使用的信任库中的 anchor (默认为一组公共根,但可以修改或覆盖,并且可以具有非root锚),您同样可以编写程序(可能使用 HandshakeCompletedListener 进行连接,以便从depth=n转储信息。但是,如果链没有验证,Java会以异常中止握手,并且根本没有证书,这与openssl情况不同,在这种情况下,您可能会在错误发生之前获得部分信息 - 加上openssl的检查,至少在默认情况下,无论如何都不是那么严格。

更新:为了完整性,在Java 7+中,命令行event.getPeerCertificates()以相当杂乱的格式显示发送的链。

浏览器中,我可以轻松查看,Windows上的Firefox和Chrome(至少)可以写出他们找到并验证的链。 ISTR但不能轻易地重新测试Firefox错误/异常对话框也可以为无法验证且可能不完整的链执行此操作。这些并非按原样自动生成,但我已经看到了大量模拟GUI用户输入的广告"显然可以根据需要驱动它们的工具。

答案 1 :(得分:2)

您可以在verify_callback中获得包含内置受信任根证书的链(请参阅SSL_CTX_set_verify。使用小型Perl程序,您可以像这样转储链:

#!/usr/bin/perl
use strict;
use warnings;
use IO::Socket::SSL;

IO::Socket::SSL->new(
    PeerHost => 'www.google.com:443',
    SSL_verify_callback => sub {
        my $cert = $_[4];
        my $subject = Net::SSLeay::X509_NAME_oneline(Net::SSLeay::X509_get_subject_name($cert));
        my $issuer  = Net::SSLeay::X509_NAME_oneline(Net::SSLeay::X509_get_issuer_name($cert));
        print "# $subject (issuer=$issuer)\n";
        print Net::SSLeay::PEM_get_string_X509($cert),"\n";
        return 1;
    }
) or die $SSL_ERROR||$!;