Tips from the Trenches vol 3: openssl s_client and SNI

Need to check what TLS certificate is served by a server?

Having trouble with your certificates for your web service? Need to determine exactly what certificate is being served?

Use openssl s_client, and use it to dump the server-side certificate in a format that’s easy to read:

[agoossen@agoossen ~]$ openssl s_client -connect ajg.id.au:443 -showcerts | openssl x509 -noout -text                  
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
verify return:1
depth=1 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = RapidSSL RSA CA 2018
verify return:1
depth=0 CN = www.ajg.id.au
verify return:1
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            02:5c:fe:60:69:26:9d:f5:5c:86:c7:a8:ed:42:1f:e7
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = RapidSSL RSA CA 2018
        Validity
            Not Before: Dec  8 00:00:00 2018 GMT
            Not After : Feb  6 12:00:00 2020 GMT

Going through the commands here, we’re using the client available in openssl to connect to ajg.id.au on port 443. We’re then instructing it to show the certificate, and piping the result back into openssl. This time, we’re using the x509 function to dump the resulting certificate back to a text format that we can easily view.

A word of warning: if you are connecting to a host that uses virtual hosts, i.e. you serve multiple domain names from the same IP address, you may find you do not get the certificate you require. To solve this, add the -servername parameter with the domain name you are connecting to.

A good example of this gotcha is attempting to verify the certificate your TLS-passthrough OpenShift service is using. Without -servername, you will get back the certificate of the router pod – not what you want to see!

This is due to a TLS extension called Server Name Indication (SNI) – the focus of the next part of this post.

Server Name Indication – solving the TLS chicken and the egg problem

When your browser connects to a site it resolves the domain name and connects to the IP address of the server. Most web hosting providers, at least the cheap ones, carry multiple virtual hosts on a single server.

The problem is that all you have is an IP address to connect to, so the server does not know which virtual host you’re actually connecting to. The browser will pass along the Host HTTP header; this is what holds the domain you are connecting to. The server takes this, maps it to a virtual host, and serves up the content.

When you wrap that session in TLS, we now have a problem – each virtual host will, presumably, have a different TLS certificate that corresponds to its domain name. The server needs to know which virtual host you’re after so it can serve the correct certificate.

Which domain you are connecting to is contained in the HTTP Host header…except that isn’t provided to the server until the TLS session is established. So you can’t serve up the correct certificate until you get the Host header, which you won’t get until the connection is established…this is the chicken-and-the-egg problem.

To solve this problem Server Name Indication (SNI) was established as an extension to TLS. You pass along the detail of the server name you are connecting to, in the clear, as part of the TLS negotiation. The server can then use this detail to identify which virtual host you require and serve the correct certificate.

Taking our OpenShift example, the proxy service running on the router pods will need SNI when conducting TLS passthrough – otherwise, it doesn’t know which route you’re connecting to, and therefore doesn’t know which service to proxy your connection to.

You can specify the SNI hostname to openssl s_client by using the -servername parameter:

openssl s_client -connect ajg.id.au:443 -servername ajg.id.au | openssl x509 -text -noout


Leave a Reply

Your email address will not be published. Required fields are marked *