1. Obtaining the OpenSSL development branch for Draft 21 Testing
NetScaler supports Draft 21 of the TLS 1.3 specification. To connect to the NetScaler TLS 1.3 server with s_client, checkout the OpenSSL main development branch from github (draft 21 is only supported in the OpenSSL main development branch):
git clone https://github.com/openssl/openssl.git openssl-tls1.3-draft-21 cd openssl-tls1.3-draft-21
Build a version of OpenSSL with TLS 1.3 support suitable for debugging and tracing (very handy for testing):
./config no-asm no-dso no-hw no-engine no-shared enable-tls1_3 enable-ssl-trace --prefix=$HOME/tools/openssl-tls1.3-draft-21-install --openssldir=$HOME/tools/openssl-tls1.3-draft-21-install -O0 -fno-omit-frame-pointer -fno-inline-functions -g3 make && make install
Or build with default production options and TLS 1.3 support:
./config enable-tls1_3 --prefix=$HOME/tools/openssl-tls1.3-draft-21-install --openssldir=$HOME/tools/openssl-tls1.3-draft-21-install make && make install
Note: This code is not bug-free - it is in active development and changes frequently. Sometimes the trace functionality activated with the -trace argument prints things that aren't true. Proceed with caution!
2. Basics
Completing a TLS 1.3 handshake with NetScaler requires an SSL vserver, HTTP(S) services on the backend, and a server certificate and private key bound to the vserver. OpenSSL s_client looks for the entire server certificate chain will be sent in the server's Certificate handshake message, so be sure to link the server certificate to its issuing CA certificates (all the way up to the self-signed root CA) using the link ssl certKey command in NetScaler so that the vserver will automatically send the full certificate chain during each handshake. Alternatively (among many other options), the -CA argument can be used to specify the CA certs from a local file during each openssl s_client invocation.
To complete a TLS 1.3 handshake with NetScaler (assuming TLS 1.3 support is compiled into OpenSSL per instructions above):
$ openssl s_client <vip>:<port>
The remaining sections show how to control various aspects of the handshake and exercise additional features of the NetScaler TLS 1.3 server.
3. Cipher Suite Negotiation
NetScaler supports the following TLS 1.3 cipher suites (all are enabled in the DEFAULT cipher group):
Hex Code |
OpenSSL Name |
0x13,0x01 |
TLS13-AES-128-GCM-SHA256 |
0x13,0x02 |
TLS13-AES-256-GCM-SHA384 |
0x13,0x03 |
TLS13-CHACHA20-POLY1305-SHA256 |
s_client also supports these ciphers by default, so under a default NetScaler SSL vserver configuration, forcing selection of a cipher that is not NetScaler's most preferred cipher requires restricting the cipher suites offered by the client using the -ciphers argument. For example, to cause the server to select the ChaCha-20-Poly-1305 AEAD cipher, offer support for just that cipher suite:
openssl s_client -connect 192.168.5.22:443 -cipher 'TLS13-CHACHA20-POLY1305-SHA256'
4. (EC)DHE Key Exchange
Both full/initial and abbreviated/resumption handshakes support an ephemeral Diffie-Hellman key exchange. For initial handshakes, this is required, and for resumption handshakes this is optional and is only skipped if both the client and server opt out.
openssl ecparam -list_curves
The ECDHE groups (curves) supported by NetScaler are:
OpenSSL Name |
NetScaler Name |
prime256v1 |
P_256 |
secp384r1 |
P_384 |
secp521r1 |
P_521 |
By default, all three of these curves are enabled in an SSL vserver. The P_224 curve is also enabled in an SSL vserver by default, but TLS 1.3 servers are forbidden from negotiating this curve, so it will never be used in a TLS 1.3 handshake regardless of its enabled/disabled state.
The ClientHello messages contains two extensions key_share and supported_groups that are used for the DHE key exchange. The -groups argument to openssl s_client controls the contents of these extensions, and the parameter value is a colon-separated string of group (curve) names.
s_client sends a key_share extension containing a public key created using the first group in the -groups argument. It sends a supported_groups extension containing all groups specified in the colon-separated argument value.
4.1. (EC)DHE Group Negotiation
To negotiate a commonly supported group, NetScaler finds its most-preferred group that is present in the client's supproted_groups extension. One of three situations results:
-
If there is no overlap between supported groups lists, the handshake fails with a fatal handshake_failure alert.
-
If the selected group has a corresponding entry in the key_share extension, the handshake proceeds immediately with a key exchange using the selected group.
-
Otherwise, a key_share for the selected group is requested by the server via a HelloRetryRequest message. The client responds with a suitable key_share for the selected group in the second ClientHello and the key exchange proceeds using the selected group.
4.2. Examples
-
Offer a P_256 key_share, which the default NetScaler frontend SSL profile will accept without the need for a HelloRetryRequest. Since no fallback groups are offered, the handshake will fail if P_256 is removed from the supported groups in the server’s configuration.
-
$ openssl s_client -connect 192.168.5.22:443 -groups prime256v1
-
-
Offer a P_256 key_share and advertise willingness to provide a P_384 or P_521 key_share in response to a HelloRetryRequest. The default NetScaler frontend SSL profile will accept the P_256 key_share without sending a HelloRetryRequest since P_256 is configured to be the preferred group.
-
$ openssl s_client -connect 192.168.5.22:443 -groups prime256v1:secp384r1:secp521r1
-
-
Offer a P_384 key_share and advertise willingness to provide a P_256 or P_521 key_share in response to a HelloRetryRequest. The default NetScaler frontend SSL profile prefers P_256, and the client is advertising willingness to use that group, so NetScaler will send a HelloRetryRequest for a P_256 key_share, and client will offer one in the second ClientHello.
-
$ openssl s_client -connect 192.168.5.22:443 -groups secp384r1:prime256v1:secp521r1
-
-
Offer a P_384 key_share and don't offer any fallback groups. Even though the P_384 group is not the most preferred group in the default frontend SSL profile, the key exchange will proceed with P_384 since the groups with higher preference in the server configuration (P_256) are not supported by the client.
-
> $ openssl s_client -connect 192.168.5.22:443 -groups secp384r1
-
-
Offer an unsupported group in the initial key_share and don't offer any supported fallback groups. The server will respond with a fatal handshake_failure alert.
-
$ openssl s_client -connect 192.168.5.22:443 -groups secp224k1
-
4.3. (EC)DHE Key Exchange with PSK
When a client has a PSK established during a previous handshake it can offer the PSK and decide whether to also require a (EC)DHE key exchange. By default, s_client requires an (EC)DHE key exchange when offering a PSK. To inform the server that the (EC)DHE key exchange can be skipped, just use the -allow_no_dhe_kex argument when offering a PSK.
If an (EC)DHE key exchange is required, the -groups argument is used just as it is during a full handshake to control the key_share and supported_groups extensions.
NetScaler has an option to force an (EC)DHE key exchange to take place when a PSK is accepted, even if the client does not require it. This guarantees PFS for all application traffic sent after a completed handshake, even if session ticket keys are compromised, and even if the client did not request an (EC)DHE key exchange.
# NetScaler CLI > set ssl profile prof1 -dheKeyExchangeWithPsk YES
When this option is set to YES (by default it is set to NO), the handshake will fail if the client does not offer the psk_dhe_ke(1) mode in its psk_key_exchange_modes extension, since PFS cannot be guaranteed in case of a ticket encryption key compromise.
5. Abbreviated Handshakes with Ticket-Based Session Resumption
NetScaler supports session resumption in TLS 1.3 via a session ticket mechanism. The abbreviated handshake is described here.
As a prerequisite, enable support for session tickets in the SSL profile:
# NetScaler CLI > set ssl profile prof1 -sessionTicket ENABLED
5.1. Examples
-
Obtain and save a session ticket. The argument for echo should be entered on the command line with the sequence <ctrl>-v <ctrl>-d. This input, combined with the -ign_eof argument forces s_client to wait until the server closes the connection before exiting. Since the NewSessionTicket message is sent by the server after the handshake completes, the client might miss capturing the ticket if it were to close immediately after the input EOF is processed (as it does by default).
-
echo "^D" | openssl s_client -connect 192.168.5.22:443 -ign_eof -sess_out sess
-
-
Now that we have session state stored locally in the sess file, offer it back to the server in a pre_shared_key extension. During this handshake, no server certificate is transferred.
-
openssl s_client -connect 192.168.5.22:443 -sess_in sess
-
-
By default, s_client requires that an (EC)DHE key exchange takes place, even if the PSK is accepted. To skip the (EC)DHE key exchange (thereby avoiding any possibility of a HelloRetryRequest due to group re-negotiation), use the -allow_no_dhe_kex argument:
-
openssl s_client -connect 192.168.5.22:443 -sess_in sess -allow_no_dhe_kex
-
Note: s_client respects ticket lifetime hints conveyed by the server in the NewSessionTicket message, so the PSK will not be offered to the server in this command if s_client believes the session state it read from the sess file is expired.
6. Sending "0-RTT" Early Application Data
To send early application data using s_client we first need to write an application message to a file. A bogus HTTP GET request should be enough to at least get us an HTTP 400 (we just want to see the TLS layer in action). We'll use httpget.bin with the following contents:
$ hexdump -C httpreq.bin 00000000 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a |GET / HTTP/1.1..| 00000010 0d 0a |..| 00000012
As a prerequisite, enable processing of early application data in the SSL vserver. This is a crude all-or-nothing interface in place just for the Beta release of TLS 1.3 early application data support.
# NetScaler CLI > set ssl profile prof1 -zeroRttEarlyData ENABLED
6.1. Examples
After obtaining a ticket from the server in an initial handshake, we can start offering early application data along with our ClientHello.
In order for early data to be accepted and processed, the handshake must not require a HelloRetryRequest. One simple way to ensure this is to skip the (EC)DHE key exchange altogether with the -allow_no_dhe_kex argument.
We can still perform an (EC)DHE key exchange while sending early data, but we have to ensure that a HelloRetryRequest will not take place. To do this, the key_share extension in our initial ClientHello must offer a group that the server will accept.
-
Resume using a PSK and send early data without performing an (EC)DHE key exchange. This command will send our HTTP GET request in encrypted early application data records. The server's application response comes back in the server's first flight, immediately following the records containing Finished and NewSessionTicket handshake messages.
-
$ openssl s_client -connect 192.168.5.22:443 -sess_in sess -early_data httpreq.bin -allow_no_dhe_kex
-
-
Resume using a PSK and send early data while also performing an ECDHE handshake using the server's preferred P_256 curve.
-
$ openssl s_client -connect 192.168.5.22:443 -sess_in sess -early_data httpreq.bin -groups prime256v1
-
Note: When requesting an (EC)DHE key exchange and offering early data, as in example 2 above, we used a priori knowledge of NetScaler's preferred DH groups to ensure that we sent a key_share extension in the initial ClientHello that NetScaler would accept without the need for a HelloRetryRequest (and thus automatic rejection of early data). Since, in general, clients will have no knowledge of the server's preferred groups, NetScaler will send a supported_groups extension in it's EncryptedExtensions handshake message during any handshake where an (EC)DHE key exchange is taking place. During the initial handshake when the ticket is issued, the server's preferred groups can be remembered by the client and used to ensure that the key_share extension offered along with early data will be accepted, thus allowing early data processing to proceed. In the Beta release, NetScaler does not yet send the supported_groups extension.
7. Updating Traffic Keys
The s_client utility can send KeyUpdate handshake messages after a handshake is completed to force the client write keys and (optionally) the server write keys to rotate.
Initiate a key update for client write keys (open a connection and then press 'k'):
openssl s_client -connect 192.168.5.22:443 > k
Initiate a key update for client and server write keys (open a connection and then press 'K'):
openssl s_client -connect 192.168.5.21:443 > K
8. Viewing TLS 1.3 Packet Traces in Wireshark
The latest Wireshark releases (version 2.3 and up) support analysis and decryption of TLS 1.3 Draft 21. s_client can export traffic secrets in a convenient log format that Wireshark recognizes. Wireshark later finds the correct secrets for a given trace by using the ClientHello.random field to lookup the correct secrets in the key log file. To use this convenient functionality:
-
Supply the -keylogfile <file> to any s_client commands and all traffic secrets (early, handshake, and application) will be appended to the named file.
-
Specify the name of this file in the Wireshark "SSL" protocol preferences in the "(Pre-)Master-Secret log filename..." field. Wireshark will automatically decrypt protected records.