How to Determine which TLS Version an Application is Using

The TLS version used by your Java application depends on how your application and JDK are configured. The Java Security API offers options for which protocol versions to have TLS use. In the most recent JDK releases, TLSv1.3 and TLSv1.2 are offered by default (via the TLS ClientHello handshake initialization routine)

Check out How to Disable TLS Versions by Default to read more on what the JDK API has to offer in this area.

Determining exactly what TLS protocol version an application is using is easiest done via collection of runtime data. Various tools and logger options are available. This document will discuss some of those options.


Network Analysis

One option is to attach a network protocol analyzer tool to the network interface where the JDK is communicating and get information on all the network traffic on that interface. The "ClientHello" record will control details of the TLS versions offered to the TLS server for use, but ultimately it’s the TLS server that decides what TLS version will be used. Look for the "ServerHello" record and the accompanying version value to detemine what TLS version is in use on a particular socket.


JDK Debug Logs

The JDK offers verbose, standard error output if the javax.net.debug system property is enabled. A value of all produces verbose output. For the purpose of detemining a TLS version protocol value, a value of ssl:handshake (for example, -Djavax.net.debug=ssl:handshake) is sufficient. Here’s an example of a "ServerHello" capture in a recent JDK 17 release:

"ServerHello": {
  "server version"      : "TLSv1.2",
  "random"              : "3C84EF0BAB49FDD5D179780EEC6C0E8E950AAF5DBCBAE20C8B258D75F2646618",
  "session id"          : "",
  "cipher suite"        : "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(0xC02F)",
  "compression methods" : "00",
  "extensions"          : [
    "server_name (0)": {
      <empty extension_data field>
    },
    "renegotiation_info (65,281)": {
      "renegotiated connection": [<no renegotiated connection>]
    },
    "ec_point_formats (11)": {
      "formats": [uncompressed, ansiX962_compressed_prime, ansiX962_compressed_char2]
    },
    "session_ticket (35)": {
      <empty>
    }
  ]
}

From above output, we see that TLSv1.2 is in use for this particular connection.


Java Flight Recorder (JFR) Events

A less well-known but quite useful option for capturing such TLS information is the JFR profiler that ships with the JDK. The jdk.TLSHandshake event captures core information on every TLS handshake performed by the JDK. You can enable it via the JFR options or edit the default configuration file in the JDK itself. The default JFR configuration file is located at <JDK_HOME>/lib/jfr/default.jfc in JDK 11 and later. Simply switch the enabled options to true.

<event name="jdk.TLSHandshake">
  <setting name="enabled">true</setting>
  <setting name="stackTrace">true</setting>
</event>

This event is available since the release of Oracle JDK 8u231 (JDK-8148188). For JDK 8u, the event name is java/tls_handshake.

You can start your JFR recording via application start-up arguments or attach remotely via jcmd, Java Mission Control (JMC), or one of your other favourite tools that supports the JFR attach option. For example, add (in JDK 11 and later) -XX:StartFlightRecording=filename=TLSInfo.jfr to your application start-up arguments. Use JMC or the $JDK/bin/jfr tool (avaialable since JDK 12) to analyze the recording.

Here’s a example dump of the TLSHandshake event data from the jfr tool:

$JDK/bin/jfr print --events "TLS*" /tmp/TLS.jfr
jdk.TLSHandshake {
  startTime = 09:03:36.128
  peerHost = "myserver.oracle.com"
  peerPort = 443
  protocolVersion = "TLSv1.2"
  cipherSuite = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
  certificateId = -1377911018
  eventThread = "main" (javaThreadId = 1)
  stackTrace = [
    sun.security.ssl.Finished.recordEvent(SSLSessionImpl) line: 1164
    sun.security.ssl.Finished$T12FinishedConsumer.onConsumeFinished(ClientHandshakeContext, ByteBuffer) line: 567
    sun.security.ssl.Finished$T12FinishedConsumer.consume(ConnectionContext, ByteBuffer) line: 538
    sun.security.ssl.SSLHandshake.consume(ConnectionContext, ByteBuffer) line: 396
    sun.security.ssl.HandshakeContext.dispatch(byte, ByteBuffer) line: 480
    ...
  ]
}

As you can see, TLSv1.2 is in use for this particular example.


Last reviewed on Sat Feb 01 2025 00:00:00 GMT+0000 (Coordinated Universal Time)