I am running curl against a sni-enabled server with the following command
curl --cacert CustomCA.crt -H "Host: example.com" However, I am not getting the right certificate where common name (CN) set as example.com (hence the certificate verification failed). I know the SNI config is done correctly because I see the right certificate is delivered when accessing it through web browser. Just in case, my current environment is
> 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: trustyEDIT: I forgot to mention that there is no dns entry to map example.com to 1.2.3.4. This is actually a part of my work, which I did it in ruby as follows:
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.closeI am just wondering how to re-create something like this by just using curl.
And... by editing my own copy of /etc/hosts, the command would work properly
1.2.3.4 example.comwhy is that?
EDIT 2: doing it via openssl
openssl s_client -connect 1.2.3.4:443 -servername example.com 2 Answers
The host header has nothing to do with SNI. To use SNI you have to use the hostname in the URL, i.e. use instead of (and of course use https:// not http://).
More details: SNI sends the hostname inside the TLS handshake (ClientHello). The server then chooses the correct certificate based on this information. Only after the TLS connection is successfully established it will send the HTTP-Request, which contains the Host header you specified.
Regarding your edit/comments:
- I wonder how you've accessed the site successfully with the browser if there is no DNS entry for the host. If the browser does not know the hostname it cannot use SNI either.
- If you add the host/ip mapping to
/etc/hostsit will work when accessing with curl because you know you have the hostname for SNI and the matching IP for the TCP connection. - Your ruby code sets ssl_client.hostname. This is not the same as the HTTP host header you've set with curl.
- I don't see an option for curl where you could set hostname for TLS SNI and IP for TCP connection independently.
Finally found the answer after referring to this.
curl --cacert CustomCA.crt --resolve example.com:443:1.2.3.4 1