You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Ruby与gnutls-cli获取的证书信任链存在差异?

证书固定测试中gnutls-cli与Ruby获取证书链数量差异的原因分析

我在测试网站证书固定(cert pinning)时发现,gnutls-cli命令和Ruby代码获取的证书信任链数量有时不一致:

  • 针对github-cloud.s3.amazonaws.com,gnutls-cli返回4条证书,Ruby代码仅返回3条
  • 针对www.netflix.com,gnutls-cli返回2条证书,Ruby代码返回3条

测试结果示例

1. gnutls-cli 针对 github-cloud.s3.amazonaws.com 的输出

Certificate[0] info:
subject `CN=*.s3.amazonaws.com'
pin-sha256="hK1awhGE7onU0O+/0pwyTCX1ngEBhLhdNNtD8P11+xY="

Certificate[1] info:
subject `CN=Amazon,OU=Server CA 1B,O=Amazon,C=US'
pin-sha256="JSMzqOOrtyOT1kmau6zKhgT676hGgczD5VMdRMyJZFA="

Certificate[2] info:
subject `CN=Amazon Root CA 1,O=Amazon,C=US'
pin-sha256="++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI="

Certificate[3] info:
subject `CN=Starfield Services Root Certificate Authority - G2,O=Starfield Technologies'
pin-sha256="KwccWaCgrnaw6tsrrSO61FgLacNgG2MMLq8GE6+oP5I="

2. Ruby代码针对 github-cloud.s3.amazonaws.com 的输出

/CN=*.s3.amazonaws.com
hK1awhGE7onU0O+/0pwyTCX1ngEBhLhdNNtD8P11+xY=
/C=US/O=Amazon/OU=Server CA 1B/CN=Amazon
JSMzqOOrtyOT1kmau6zKhgT676hGgczD5VMdRMyJZFA=
/C=US/O=Amazon/CN=Amazon Root CA 1
++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=

3. gnutls-cli 针对 www.netflix.com 的输出

Certificate[0] info:
subject `CN=www.netflix.com,O=Netflix\, Inc.'
pin-sha256:3TGagkVvINvo827M04z0YZlg5kctebcod1Qwb83pA0s=

Certificate[1] info:
subject `CN=DigiCert TLS RSA SHA256 2020 CA1,O=DigiCert Inc'
pin-sha256="RQeZkB42znUfsDIIFWIRiYEcKl7nHwNFwWCrnMMJbVc="

4. Ruby代码针对 www.netflix.com 的输出

/C=US/ST=California/L=Los Gatos/O=Netflix, Inc./CN=www.netflix.com
3TGagkVvINvo827M04z0YZlg5kctebcod1Qwb83pA0s=
/C=US/O=DigiCert Inc/CN=DigiCert TLS RSA SHA256 2020 CA1
RQeZkB42znUfsDIIFWIRiYEcKl7nHwNFwWCrnMMJbVc=
/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA
r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=

测试所用Ruby代码

#!/usr/bin/env ruby
require 'colorize'
require 'net/http'
require 'openssl'
require 'base64'

domain = "www.netflix.com"

http = Net::HTTP.new(domain, 443)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.verify_callback = lambda do | preverify_ok, cert_store |
    return false unless preverify_ok
    end_cert = cert_store.chain[0]
    return true unless end_cert.to_der == cert_store.current_cert.to_der
    cert_store.chain.each do |i|
    sha256 = OpenSSL::Digest::SHA256.new
    digest = sha256.digest(i.public_key.to_der)
    spki = Base64.strict_encode64(digest)
    puts i.subject.to_s, spki
    end
    true
end
res = http.get '/'

差异原因解析

1. 证书链来源不同

  • gnutls-cli展示的是服务器主动发送的完整证书链,包含服务器证书、中间CA证书,甚至可能包含根证书(如果服务器配置了发送根证书)。比如s3的例子中,服务器额外发送了Starfield根证书,所以gnutls-cli输出4条。
  • Ruby依赖的OpenSSL库返回的是验证完成后的信任链:它会先接收服务器发送的证书,再结合本地信任存储补全缺失的根证书,同时过滤掉本地已信任的根证书(因为根证书是信任锚,不需要服务器传递)。

2. 本地信任存储的作用差异

  • OpenSSL会自动从系统或指定的信任存储中查找缺失的根证书,完成信任链的构建。比如Netflix的例子,服务器只发送了服务器证书和中间CA证书,OpenSSL从本地信任存储中找到了DigiCert根证书,所以Ruby输出的链包含3条。
  • gnutls-cli默认不会从本地信任存储补充证书,只展示服务器发送的内容,所以Netflix的例子里只输出2条。

3. 根证书的处理逻辑

  • 对于本地已信任的根证书:如果服务器发送了它,gnutls-cli会直接展示;但OpenSSL会认为根证书是信任起点,不会将其加入返回的验证链中,所以s3的例子里Ruby输出少了Starfield根证书。
  • 如果服务器没发送根证书,但OpenSSL能从本地找到,就会把它加入链中,导致Ruby输出的链比gnutls-cli多一条(比如Netflix的例子)。

内容的提问来源于stack exchange,提问作者Jay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 05:05:20