Making Practical Use of OpenSSL’s s_client

I frequently troubleshoot SSL/TLS server configurations, X.509 certificates, and other SSL/TLS-related concerns. One of the most useful utilities in my toolbox is OpenSSL. I use it for a huge number of tasks: generating new X.509 certificate signing requests, generating random strings for encryption keys, retrieving server X.509 certificates, testing support SSL/TLS ciphers, etc.

In my experience, the s_client sub-command is particularly useful when interacting with servers via SSL/TLS. The s_client sub-command implements a generic SSL/TLS client, which connects to a remote server using SSL/TLS. This allows me to perform a number of useful activities.

Examining Server SSL/TLS Configuration

By default, just connecting with:

openssl s_client -connect hostname:port

… will show me basic information about the connection that OpenSSL is able to establish with the server:


openssl s_client -connect wikipedia.org:443
CONNECTED(00000003)
depth=2 OU = GlobalSign Root CA - R3, O = GlobalSign, CN = GlobalSign
verify return:1
depth=1 C = BE, O = GlobalSign nv-sa, CN = GlobalSign Organization Validation CA - SHA256 - G2
verify return:1
depth=0 C = US, ST = California, L = San Francisco, O = "Wikimedia Foundation, Inc.", CN = *.wikipedia.org
verify return:1
---
Certificate chain
 0 s:/C=US/ST=California/L=San Francisco/O=Wikimedia Foundation, Inc./CN=*.wikipedia.org
   i:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - SHA256 - G2
 1 s:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - SHA256 - G2
   i:/OU=GlobalSign Root CA - R3/O=GlobalSign/CN=GlobalSign
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIJVTCCCD2gAwIBAgIMCd5zS5F1TBh5wK5OMA0GCSqGSIb3DQEBCwUAMGYxCzAJ
BgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMTwwOgYDVQQDEzNH
bG9iYWxTaWduIE9yZ2FuaXphdGlvbiBWYWxpZGF0aW9uIENBIC0gU0hBMjU2IC0g
RzIwHhcNMTcxMTAzMDM0MjAyWhcNMTgxMTIyMDc1OTU5WjB5MQswCQYDVQQGEwJV
UzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEj
MCEGA1UEChMaV2lraW1lZGlhIEZvdW5kYXRpb24sIEluYy4xGDAWBgNVBAMMDyou
d2lraXBlZGlhLm9yZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABO9TTfhvt3Ky
RaINKYQcrt4E7haIi8E96LYjqXVycaTRH0YDMyL1clY/3x8rk9JBtzn5dE8gCh3p
wEeJgAtfaAOjgga5MIIGtTAOBgNVHQ8BAf8EBAMCA4gwgaAGCCsGAQUFBwEBBIGT
MIGQME0GCCsGAQUFBzAChkFodHRwOi8vc2VjdXJlLmdsb2JhbHNpZ24uY29tL2Nh
Y2VydC9nc29yZ2FuaXphdGlvbnZhbHNoYTJnMnIxLmNydDA/BggrBgEFBQcwAYYz
aHR0cDovL29jc3AyLmdsb2JhbHNpZ24uY29tL2dzb3JnYW5pemF0aW9udmFsc2hh
MmcyMFYGA1UdIARPME0wQQYJKwYBBAGgMgEUMDQwMgYIKwYBBQUHAgEWJmh0dHBz
Oi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMAgGBmeBDAECAjAJBgNV
HRMEAjAAMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5j
b20vZ3MvZ3Nvcmdhbml6YXRpb252YWxzaGEyZzIuY3JsMIIC+AYDVR0RBIIC7zCC
AuuCDyoud2lraXBlZGlhLm9yZ4IRKi5tLm1lZGlhd2lraS5vcmeCESoubS53aWtp
Ym9va3Mub3JnghAqLm0ud2lraWRhdGEub3JnghEqLm0ud2lraW1lZGlhLm9yZ4Ib
Ki5tLndpa2ltZWRpYWZvdW5kYXRpb24ub3JnghAqLm0ud2lraW5ld3Mub3JnghEq
Lm0ud2lraXBlZGlhLm9yZ4IRKi5tLndpa2lxdW90ZS5vcmeCEioubS53aWtpc291
cmNlLm9yZ4ITKi5tLndpa2l2ZXJzaXR5Lm9yZ4ISKi5tLndpa2l2b3lhZ2Uub3Jn
ghIqLm0ud2lrdGlvbmFyeS5vcmeCDyoubWVkaWF3aWtpLm9yZ4IWKi5wbGFuZXQu
d2lraW1lZGlhLm9yZ4IPKi53aWtpYm9va3Mub3Jngg4qLndpa2lkYXRhLm9yZ4IP
Ki53aWtpbWVkaWEub3JnghkqLndpa2ltZWRpYWZvdW5kYXRpb24ub3Jngg4qLndp
a2luZXdzLm9yZ4IPKi53aWtpcXVvdGUub3JnghAqLndpa2lzb3VyY2Uub3JnghEq
Lndpa2l2ZXJzaXR5Lm9yZ4IQKi53aWtpdm95YWdlLm9yZ4IQKi53aWt0aW9uYXJ5
Lm9yZ4IUKi53bWZ1c2VyY29udGVudC5vcmeCFCouemVyby53aWtpcGVkaWEub3Jn
gg1tZWRpYXdpa2kub3JnggZ3Lndpa2mCDXdpa2lib29rcy5vcmeCDHdpa2lkYXRh
Lm9yZ4INd2lraW1lZGlhLm9yZ4IXd2lraW1lZGlhZm91bmRhdGlvbi5vcmeCDHdp
a2luZXdzLm9yZ4INd2lraXF1b3RlLm9yZ4IOd2lraXNvdXJjZS5vcmeCD3dpa2l2
ZXJzaXR5Lm9yZ4IOd2lraXZveWFnZS5vcmeCDndpa3Rpb25hcnkub3JnghJ3bWZ1
c2VyY29udGVudC5vcmeCDXdpa2lwZWRpYS5vcmcwHQYDVR0lBBYwFAYIKwYBBQUH
AwEGCCsGAQUFBwMCMB0GA1UdDgQWBBS8k7eB+Y5zeehXd/+7LZYycjelhTAfBgNV
HSMEGDAWgBSW3mHxvRwWKVMcwMx9O4MAQOYafDCCAfUGCisGAQQB1nkCBAIEggHl
BIIB4QHfAHUA3esdK3oNT6Ygi4GtgWhwfi6OnQHVXIiNPRHEzbbsvswAAAFff/iq
ggAABAMARjBEAiA4FALX81XWMAH0ZP4ShF4ybuHVAQXD8VBdUdEgKX2m9wIgWMGN
RkrtuqHZBakLF4NR+B5prk6p2JfHaMJOYNN79eQAdgBWFAaaL9fC7NP14b1Esj7H
Rna5vJkRXMDvlJhV1onQ3QAAAV9/+KraAAAEAwBHMEUCIAzNCd5c6unJfnRk8x3F
chkmb5IonNAaZ0OgOHUTfsEjAiEAmwGfrchvB/HQPonfydZuveGu52gw0V80SFbV
UPN/gYsAdgCkuQmQtBhYFIe7E6LMZ3AKPDWYBPkb37jjd80OyA3cEAAAAV9/+K1K
AAAEAwBHMEUCID4nD7TtfMRugYv7YHxat/xn2lBbPlPtVBlPo7d1QMi8AiEAwg1N
0KAAapUKIRcy18u535qOIxm6cVmez8MojlNSDvgAdgDuS723dc5guuFCaR+r4Z5m
ow9+X7By2IMAxHuJeqj9ywAAAV9/+LAxAAAEAwBHMEUCIQCRJh4Z/2bGZ+VUV74R
fPoQOmj4OEIQbXcJyrTv4srmNwIgEcWj2wtYPc0txnRFY51oJp1zjDCWjDAfieFA
awhWAR8wDQYJKoZIhvcNAQELBQADggEBAFEehJMucjWXmy6JPFQHqZWvrrR4l6mD
5U1pSAszUm3vmDJbR5WTm/7vFpJ8gAeJnMbRq5vnY0AuxTkgZbXdQwuCSn3gPrVf
H8LXcQ5O3CyrhqymtXCeiNxr7l4ElZdv4MZZansbyaempOCggVXWbKrQa5mYjM8P
w0R3KvlDh8ZnZj8O454kxq37bUn2DZbm9HDcnapN1dL48eozbINJlmbFN4MSHdgI
AJel6OErMYZq1gsqoYljSOCQ+1jhMmoVrT5WxpXbbQTNPpRY9jhk1W9NoOOYMn54
UtX6wJV7pWLlHfRyk1koREbnGVrHEpBUBiYVehiKUfRsnrMYeZoqNQg=
-----END CERTIFICATE-----
subject=/C=US/ST=California/L=San Francisco/O=Wikimedia Foundation, Inc./CN=*.wikipedia.org
issuer=/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - SHA256 - G2
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 3843 bytes and written 434 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-ECDSA-AES256-GCM-SHA384
Server public key is 256 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-ECDSA-AES256-GCM-SHA384
    Session-ID: EE02AB3825BFF18761A65636191FAAF5B89E4005E96E31DBA9137D629493AEFB
    Session-ID-ctx:
    Master-Key: FD15C60D1B808E3F1A8EAC87D24F812EF577AB7B59322C3899617ABE0AC9B6B6013F5EE2C95C8A0E7B4C0AB8275D0361
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1530568092
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---

As this example demonstrates, it will include the presented X.509 certificate, negotiated cipher suite, and other characteristics of the SSL/TLS session.

Passing the -showcerts flag will return all X.509 certificates (the certificate chain, if it exists), allowing me to manually inspect and evaluate the certificates that the server is returning. This can be very useful for troubleshooting a server configuration which is missing or mis-ordering certificates.

Passing the -servername flag will send the server hostname in the TLS ClientHello, making use of the server name indication (SNI) feature of TLS. It’s helpful for troubleshooting server configuration issues, particularly those relating to multiple virtual servers on a shared network interface.

Passing the -debug flag will return a full hexdump of the communications between the client and server. You can use it to dig into the nitty-gritty details of what the client and server are sending each other.

Forcing Specific SSL/TLS Verions and Ciphers

By default, s_client will try to auto-negotiate an SSL/TLS protocol version and cipher suite. However, it is possible to specify parameters so you can ensure that certain protocols and ciphers are disabled (or enabled).

The following flags will set the SSL/TLS protocol version:

  • -ssl2 use SSL v2
  • -ssl3 use SSL v3
  • -tls1 use TLS v1
  • -tls1_1 use TLS v1.1
  • -tls1_2 use TLS v1.2

Prepending no_ to all of the above will disable the corresponding action. For example, -no_tls_1_1 will disable using TLS 1.1.

To test that TLS 1.0 is properly disabled on a server, I can attempt to connect with:

openssl s_client -connect sandbox.braintreegateway.com:443 -tls1

…and receive a failed negotiation:


CONNECTED(00000003)
140736833831944:error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version:s3_pkt.c:1500:SSL alert number 70
140736833831944:error:1409E0E5:SSL routines:ssl3_write_bytes:ssl handshake failure:s3_pkt.c:659:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 0 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1
    Cipher    : 0000
    Session-ID:
    Session-ID-ctx:
    Master-Key:
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1530568664
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
---

The combination of flags allows a number of different tests, along with tuning a specific protocol.

Similar to the SSL/TLS protocol versions, the -cipher flag will allow you to specify the exact cipher suite to use on the client side.

For example, I could use something like the example below to force our client to try and use that cipher to communicate with the server:

openssl s_client -connect sandbox.braintreegateway.com:443 -cipher ECDHE-RSA-AES128-GCM-SHA256

(As might be expected, this will only work if the server will actually accept that cipher suite.)

The cipher suites available to s_client can be enumerated with openssl ciphers.

Communicating Directly with the Server

After making a connection to a server with s_client, I can also directly communicate using whatever protocol that is running over the SSL/TLS connection.

For example, I can send an HTTP request:

echo -en "HEAD / HTTP/1.1\r\nHost: www.wikipedia.org\r\n\r\n" | openssl s_client -connect wikipedia.org:443 -quiet

… and receive the response:


depth=2 OU = GlobalSign Root CA - R3, O = GlobalSign, CN = GlobalSign
verify return:1
depth=1 C = BE, O = GlobalSign nv-sa, CN = GlobalSign Organization Validation CA - SHA256 - G2
verify return:1
depth=0 C = US, ST = California, L = San Francisco, O = "Wikimedia Foundation, Inc.", CN = *.wikipedia.org
verify return:1
HTTP/1.1 200 OK
Date: Mon, 02 Jul 2018 22:23:26 GMT
Content-Type: text/html
Connection: keep-alive
Server: mw1258.eqiad.wmnet
Last-Modified: Mon, 02 Jul 2018 10:02:39 GMT
Backend-Timing: D=202 t=1530525991668434
Cache-Control: s-maxage=86400, must-revalidate, max-age=3600
ETag: W/"1273f-570014c3bdd88"
Vary: Accept-Encoding
X-Varnish: 1058517920 1052234082, 141753078 881532609
Via: 1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1)
Age: 44214
X-Cache: cp1065 hit/9, cp1054 hit/288698
X-Cache-Status: hit-front
Strict-Transport-Security: max-age=106384710; includeSubDomains; preload
Set-Cookie: WMF-Last-Access=02-Jul-2018;Path=/;HttpOnly;secure;Expires=Fri, 03 Aug 2018 12:00:00 GMT
Set-Cookie: WMF-Last-Access-Global=02-Jul-2018;Path=/;Domain=.wikipedia.org;HttpOnly;secure;Expires=Fri, 03 Aug 2018 12:00:00 GMT
X-Analytics: https=1;nocookies=1
X-Client-IP: 68.37.91.110
Set-Cookie: GeoIP=US:MI:Grand_Rapids:42.95:-85.66:v4; Path=/; secure; Domain=.wikipedia.org
Accept-Ranges: bytes

I can also interact manually as I would using telnet or nc to send HTTP requests:

openssl s_client -quiet -connect wikipedia.org:443

I can even use s_client for protocols that use STARTTLS (upgrading an insecure connection) such as SMTP and FTP:

openssl s_client -connect smtp.gmail.com:587 -starttls smtp

depth=2 OU = GlobalSign Root CA - R2, O = GlobalSign, CN = GlobalSign
verify return:1
depth=1 C = US, O = Google Trust Services, CN = Google Internet Authority G3
verify return:1
depth=0 C = US, ST = California, L = Mountain View, O = Google LLC, CN = smtp.gmail.com
verify return:1
250 SMTPUTF8
EHLO atomicobject.com
250-smtp.gmail.com at your service, [68.37.91.110]
250-SIZE 35882577
250-8BITMIME
250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8

Conclusion

While there are a variety of individual tools suited for the activities I’ve demonstrated above, I think I would be hard-pressed to find a single utility that packs the power of the s_client sub-command. Further, openssl is often already installed on many *NIX systems (such as remote servers), which provides this functionality without needing to install many dependencies. This can be a life-saver when SSH-tunneled across a couple systems in an environment where I may not have the ability to install new packages.

Unfortunately, much of the advanced functionality of s_client is only available with newer versions of OpenSSL (> 1.0.1, generally), and older *NIX systems may not have the support for all of the TLS extensions and options presented above.

Note: I used OpenSSL 1.0.1o for this post.