启用 curl 和 sni 的服务器

Jef*_*y04 7 ssl curl tls

我正在使用以下命令对启用了 sni 的服务器运行 curl

curl --cacert CustomCA.crt -H "Host: example.com" https://1.2.3.4/foo
Run Code Online (Sandbox Code Playgroud)

但是,我没有得到正确的证书,其中通用名称 (CN) 设置为 example.com(因此证书验证失败)。我知道 SNI 配置是正确完成的,因为我看到在通过 Web 浏览器访问时提供了正确的证书。以防万一,我目前的环境是

> curl --version
curl 7.35.0 (x86_64-pc-linux-gnu) libcurl/7.35.0 OpenSSL/1.0.1f zlib/1.2.8 libidn/1.28 librtmp/2.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smtp smtps telnet tftp 
Features: AsynchDNS GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP 
> lsb_release -a
LSB Version:    core-2.0-amd64:core-2.0-noarch:core-3.0-amd64:core-3.0-noarch:core-3.1-amd64:core-3.1-noarch:core-3.2-amd64:core-3.2-noarch:core-4.0-amd64:core-4.0-noarch:core-4.1-amd64:core-4.1-noarch:security-4.0-amd64:security-4.0-noarch:security-4.1-amd64:security-4.1-noarch
Distributor ID: Ubuntu
Description:    Ubuntu 14.04.1 LTS
Release:        14.04
Codename:       trusty
Run Code Online (Sandbox Code Playgroud)

编辑:我忘了提到没有将 example.com 映射到 1.2.3.4 的 dns 条目。这实际上是我工作的一部分,我在 ruby​​ 中做了如下:

  context = OpenSSL::SSL::SSLContext.new
  context.ca_file = 'CustomCA.crt'
  context.verify_mode = OpenSSL::SSL::VERIFY_PEER

  tcp_client = TCPSocket.new('1.2.3.4', 443)
  ssl_client = OpenSSL::SSL::SSLSocket.new(tcp_client, context)
  ssl_client.hostname = 'example.com'
  ssl_client.connect
  cert = OpenSSL::X509::Certificate.new(ssl_client.peer_cert)
  cert_ca = OpenSSL::X509::Certificate.new(File.read(ca_path))
  ssl_client.sysclose
  tcp_client.close
Run Code Online (Sandbox Code Playgroud)

我只是想知道如何通过使用 curl 来重新创建这样的东西。

而且...通过编辑我自己的 /etc/hosts 副本,该命令将正常工作

1.2.3.4 example.com
Run Code Online (Sandbox Code Playgroud)

这是为什么?

编辑 2:通过 openssl 进行

openssl s_client -connect 1.2.3.4:443 -servername example.com
Run Code Online (Sandbox Code Playgroud)

Jef*_*y04 24

参考this后终于找到了答案。

curl --cacert CustomCA.crt --resolve example.com:443:1.2.3.4 https://example.com/foo
Run Code Online (Sandbox Code Playgroud)


Ste*_*ich 7

主机头与 SNI 无关。要使用 SNI,您必须在 URL 中使用主机名,即使用https://example.com/而不是http://1.2.3.4/(当然使用 https:// 而不是 http://)。

更多细节:SNI 在 TLS 握手 (ClientHello) 中发送主机名。然后服务器根据此信息选择正确的证书。只有在 TLS 连接成功建立后,它才会发送 HTTP-Request,其中包含您指定的 Host 标头。

关于您的编辑/评论:

  • 如果主机没有 DNS 条目,我想知道您是如何使用浏览器成功访问该站点的。如果浏览器不知道主机名,它也不能使用 SNI。
  • 如果您将主机/IP 映射添加到/etc/hosts它,则在使用https://example.comcurl访问时将起作用,因为您知道您拥有 SNI 的主机名和 TCP 连接的匹配 IP。
  • 您的 ruby​​ 代码设置了 ssl_client.hostname。这与您使用 curl 设置的 HTTP 主机标头不同。
  • 我没有看到 curl 的选项,您可以在其中独立设置 TLS SNI 的主机名和 TCP 连接的 IP。