OpenSSL comes with a client tool that you can use to connect to a secure server. The tool is similar to
nc in the sense that it handles the encryption aspect but allows you to fully control the layer that comes next.
To connect to a server, you need to supply a hostname and a port. For example:
$ openssl s_client -crlf \ -connect www.feistyduck.com:443 \ -servername www.feistyduck.com
Notice that you had to supply the hostname twice. The
-connect switch is used to establish the TCP connection, but
-servername is used to specify the hostname sent at the TLS level. Starting with OpenSSL 1.1.1, the
s_client tool automatically configures the latter. You’ll still need to use the
-servername switch if (1) you’re using an earlier version of OpenSSL, (2) you’re connecting to an IP address, or (3) the TLS host needs to be different. Use the
-noservername switch to avoid sending hostname information in the TLS handshake.
Once you type the command, you’re going to see a lot of diagnostic output (more about that in a moment) followed by an opportunity to type whatever you want. Because we’re talking to an HTTP server, the most sensible thing to do is to submit an HTTP request. In the following example, I use a
HEAD request because it instructs the server not to send the response body:
HEAD / HTTP/1.0 Host: www.feistyduck.com HTTP/1.1 200 OK Date: Mon, 24 Aug 2020 16:38:02 GMT Server: Apache Strict-Transport-Security: max-age=31536000; includeSubDomains; preload Cache-control: no-cache, must-revalidate Content-Type: text/html;charset=UTF-8 Transfer-Encoding: chunked Set-Cookie: JSESSIONID=882D48C8842EA82E3F3AFACC4425A695; Path=/; Secure; HttpOnly Connection: close read:errno=0
If, when connecting to a remote server in this way, the TLS handshake completes but you’re getting disconnected after the first HTTP request line, check that you’ve specified the
-crlf switch on the command line. This switch ensures that the newlines you type are translated to a carriage return plus line feed combo to ensure string HTTP compliance.
Now we know that the TLS communication layer is working: we got through to the HTTP server, submitted a request, and received a response back. Let’s go back to the diagnostic output. The first couple of lines will show the information about the server certificate:
CONNECTED(00000003) 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.feistyduck.com verify return:1
The next section in the output lists all the certificates presented by the server in the order in which they were delivered:
Certificate chain 0 s:OU = Domain Control Validated, OU = PositiveSSL, CN = www.feistyduck.com i:C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO RSA Domain Validation Secure Server CA 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
For each certificate, the first line shows the subject and the second line shows the issuer information.
This part is very useful when you need to see exactly what certificates are sent; browser certificate viewers typically display reconstructed certificate chains that can be almost completely different from the presented ones. To determine if the chain is nominally correct, you might wish to verify that the subjects and issuers match. You start with the leaf (web server) certificate at the top, and then you go down the list, matching the issuer of the current certificate to the subject of the next. The last issuer you see can point to some root certificate that is not in the chain, or—if the self-signed root is included—it can point to itself.
The next item in the output is the server certificate; it’s a lot of text, but I’m going to remove most of it for brevity:
Server certificate -----BEGIN CERTIFICATE----- MIIFUzCCBDugAwIBAgIRAPR/CbWZEksfCIRqxNcesPIwDQYJKoZIhvcNAQELBQAw gZAxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMTYwNAYD [...] L1MPjFiB5pyvf9jDBxv8TmG4Q6TnDDhw2t2Qil6lhsPAMZ9odP22W3uaLE1y7aB6 zbQXjVsc3E1THfFZWRzDPsU4fN/1iGlbrcAWa2sFfhJXrCDfAowFJ8A1n9jMiNEG WfQfGgA2ar2xUtsqA7Re6XlXOlwBPuQ= -----END CERTIFICATE----- subject=OU = Domain Control Validated, OU = PositiveSSL, CN = www.feistyduck.com issuer=C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO RSA Domain Validation Secure Server CA
By default, the
s_client tool shows just the leaf certificate. If you wish to obtain the entire chain, use the
If you want to have a better look at the certificate, you’ll first need to copy it from the output and store it in a separate file. I’ll discuss how to do that in the next section.
The following is a lot of information about the TLS connection, most of which is self-explanatory:
--- No client certificate CA names sent Peer signing digest: SHA512 Peer signature type: RSA Server Temp Key: ECDH, P-256, 256 bits --- SSL handshake has read 3624 bytes and written 446 bytes Verification: OK --- New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256 Server public key is 2048 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE No ALPN negotiated SSL-Session: Protocol : TLSv1.2 Cipher : ECDHE-RSA-AES128-GCM-SHA256 Session-ID: 73FC4831AF053C46291C2D8CC90BF7F1D5B12178E488FBB4DC49A302B870E8DE Session-ID-ctx: Master-Key: E60DA9C6669C2C7DFFD8A3AD2CD17405CC0B9B69C4184469D779A9BA19A6FD4B3D602A023BD8B23F8D9A9FF2CBB5DDF7 PSK identity: None PSK identity hint: None SRP username: None TLS session ticket lifetime hint: 300 (seconds) TLS session ticket: 0000 - 31 95 ff d0 4c 42 dd d0-24 64 03 5c fc 55 1d 17 1...LB..$d.\.U.. 0010 - 05 c4 61 1f b8 ba fd fe-f7 6c 6c e9 ae a2 49 3f ..a......ll...I? 0020 - c5 19 d4 e9 69 a5 79 d5-af 13 26 c8 2c e7 f0 01 ....i.y...&.,... 0030 - 3b 42 d8 c0 29 4c fa 7e-88 aa 8d c8 0b 30 96 ce ;B..)L.~.....0.. 0040 - 43 40 2c 09 0b aa 2e d5-61 e3 34 7a a3 78 2f 93 C@,.....a.4z.x/. 0050 - 67 5a b9 96 78 f5 e7 69-b7 b6 2d 8c 00 8f 04 ab gZ..x..i..-..... 0060 - 42 1d 26 db 92 ec 2d 2f-ba 1c c6 61 87 64 0e d5 B.&...-/...a.d.. 0070 - f2 ce 20 d0 07 a5 e2 6d-c6 45 50 c2 45 14 a8 ee .. ....m.EP.E... 0080 - 59 7c 63 e1 d7 d8 b0 b6-76 21 d2 13 97 eb bd 97 Y|c.....v!...... 0090 - a1 d3 e8 5c 61 da da 2d-85 80 db ae de 56 97 e1 ...\a..-.....V.. 00a0 - e8 7a 25 f9 bf cf b6 18-48 5b b0 03 a5 e6 ec 0a .z%.....H[...... 00b0 - bf 2f 0d 1a 6b ae 79 10-80 9c cf 4d 66 8f 90 43 ./..k.y....Mf..C 00c0 - 69 54 32 be 0c 89 57 e8-6d 81 b5 3e 5b cb 5e 8e iT2...W.m..>[.^. Start Time: 1598288068 Timeout : 7200 (sec) Verify return code: 0 (ok) Extended master secret: no
The most important information here is the protocol version (TLS 1.2) and cipher suite used (
ECDHE-RSA-AES128-GCM-SHA256). Do note that protocol information appears in two locations, which is potentially confusing when different versions are shown. The first location describes the minimum protocol requirement with the negotiated cipher suite, while the second location points to the actual protocol version currently being negotiated. You will see a difference in protocol versions with some older cipher suites—for example:
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES128-SHA
The selected suite could be used with SSL 3.0, but it’s used with TLS 1.2 on this connection:
Protocol : TLSv1.2 Cipher : DHE-RSA-AES128-SHAs
You can also determine that the server has issued to you a session ID and a TLS session ticket (a way of resuming sessions without having the server maintain state) and that secure renegotiation is supported.
If you’re connecting to a TLS 1.3 server, the output may be different. Sometimes you will observe less information initially, with additional information arriving later in bursts. This behavior depends on the implementation and reflects the changes in TLS 1.3, which transmits session tickets as separate protocol messages that are sent only after the handshake is complete. Additionally, multiple session tickets are usually sent on the same connection.