An example of TLS 1.3 client and server on Java

Java 11 supports TLS 1.3 protocol which was published in August 2018. During implementing the new TLS protocol, Java security-libs team significantly re-worked Java Secure Sockets Extension (JSSE). I used to work on security-libs in Java for 6 years, so I can tell that was not an easy task for sure. But nevertheless, Java security-libs team delivered TLS 1.3 implementation in Java 11. Great job!

But TLS 1.3 implementation in Java 11 doesn’t not support all the features of the new TLS protocol. Here is what JSSE supports (see more details in JEP 332):

  • Protocol version negotiation
  • Full handshake for both client and server sides
  • Session resumption
  • Key and IV update
  • Updated OCSP stapling
  • Backward compatibility mode
  • Required extensions and algorithms
  • Two new cipher suites: TLS_AES_128_GCM_SHA256 and TLS_AES_256_GCM_SHA384
  • RSASSA-PSS signature algorithms
  • Both SSLSocket and SSLEngine

And here is what are not supported:

Java 11 doesn’t introduce new public classes and methods for TLS 1.3. It just adds a couple of new constants for the new protocol name, cipher suites, ets. And this is actually great because it makes it very easy to switch to the new TLS version. You just need to configure JSSE to use new protocol and ciphers, and the rest of the code should not change. Depending on an application, migration to the new version may not even require any modification of the application code. For example, if an application is configured by JSSE system properties such as https.protocols and jdk.tls.client.protocols. (well, if third-parties you’l like to talk to with TLS 1.3 don’t support it, then the migration may not be that easy).

Here is an example of TLS 1.3 client and server in Java. As you may notice, it’s just a regular example of SSLSocket-based client and server except it uses new constants “TLSv1.3” and “TLS_AES_128_GCM_SHA256”.

If you have found a spelling error, please, notify us by selecting that text and pressing Ctrl+Enter.

Share

7 thoughts on “An example of TLS 1.3 client and server on Java

  1. AvatarAshu Phaugat

    I ran this program but got error:

    server started on port 50308
    accepted
    Exception in thread “main” exception: No available authentication scheme
    server stopped
    javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
    at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:128)
    at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:308)
    at java.base/sun.security.ssl.Alert$AlertConsumer.consume(Alert.java:279)
    at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:181)
    at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:164)
    at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1152)
    at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1063)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:402)
    at java.base/sun.security.ssl.SSLSocketImpl.ensureNegotiated(SSLSocketImpl.java:716)
    at java.base/sun.security.ssl.SSLSocketImpl$AppOutputStream.write(SSLSocketImpl.java:970)
    at java.base/java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:81)
    at java.base/java.io.BufferedOutputStream.flush(BufferedOutputStream.java:142)
    at TLSv13Test.main(TLSv13Test.java:25)

    Process finished with exit code 1

    Reply
    1. ArtemArtem Post author

      Hi Ashu,

      I forgot to mention that the program needs a keystore and a truststore. Otherwise, the server can’t find a certificate to authenticate itself, and as a result a SSLHandshakeException is thrown.

      To fix the problem, you need to set javax.net.ssl.keyStore, javax.net.ssl.keyStorePassword, javax.net.ssl.trustStore and javax.net.ssl.trustStorePassword system properties. More details can be found in JSSE docs.

      For example:

      java -cp classes \
      -Djavax.net.ssl.keyStore=keystore \
      -Djavax.net.ssl.keyStorePassword=passphrase \
      -Djavax.net.ssl.trustStore=keystore \
      -Djavax.net.ssl.trustStorePassword=passphrase \
      com.gypsyengineer.tlsbunny.jsse.TLSv13Test

      For testing purposes, you can download the keystore file from https://github.com/openjdk/jdk/tree/master/test/jdk/javax/net/ssl/etc

      Thanks for letting me know about the problem, I’ll update the post.

      Reply
  2. AvatarRafa

    Hello, Artem,

    first of all, this is a very good example for JSSE.
    Would it be possible for you to create a step by step explanation for keystore and trustore creation for cipher suite TLS_AES_256_GCM_SHA384

    greeting Rafa

    Reply
    1. ArtemArtem Post author

      Thanks for the feedback Rafa!

      Server certificate and key don’t depend on a cipher suite in TLS 1.3, so you can use any certificate and key supported by your JDK. For example, RSA or EC certificates. Make sure that the keys meet JSSE security requirements such as key length, etc. I’ll try to write a post about creating such a key pair and keystores.

      Reply
  3. Avatarraj

    Exception in thread “main” java.lang.IllegalArgumentException: TLSv1.3
    at sun.security.ssl.ProtocolVersion.valueOf(Unknown Source)
    at sun.security.ssl.ProtocolList.convert(Unknown Source)
    at sun.security.ssl.ProtocolList.(Unknown Source)
    at sun.security.ssl.SSLServerSocketImpl.setEnabledProtocols(Unknown Source)
    at TLSv13Test$EchoServer.create(TLSv13Test.java:119)
    at TLSv13Test$EchoServer.create(TLSv13Test.java:113)
    at TLSv13Test.main(TLSv13Test.java:39)

    Reply
    1. ArtemArtem Post author

      What Java version did you use? TLS 1.3 has been supported since Java 11.

      Reply
  4. AvatarAnurag

    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:35.358 IST|SSLCipher.java:465|jdk.tls.keyLimits: entry = AES/GCM/NoPadding KeyUpdate 2^37. AES/GCM/NOPADDING:KEYUPDATE = 137438953472
    server started on port 64765
    accepted
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.090 IST|SSLSocketOutputRecord.java:258|WRITE: TLS13 handshake, length = 237
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.091 IST|SSLSocketInputRecord.java:213|READ: TLSv1.2 handshake, length = 237
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.091 IST|SSLSocketInputRecord.java:249|READ: TLSv1.2 handshake, length = 237
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.100 IST|SSLSocketOutputRecord.java:258|WRITE: TLS13 handshake, length = 122
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.100 IST|SSLSocketInputRecord.java:213|READ: TLSv1.2 handshake, length = 122
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.100 IST|SSLSocketInputRecord.java:249|READ: TLSv1.2 handshake, length = 122
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.116 IST|SSLCipher.java:1867|KeyLimit read side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
    countdown value = 137438953472
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.116 IST|SSLCipher.java:1867|KeyLimit read side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
    countdown value = 137438953472
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.117 IST|SSLCipher.java:2021|KeyLimit write side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
    countdown value = 137438953472
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.117 IST|SSLCipher.java:2021|KeyLimit write side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
    countdown value = 137438953472
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.118 IST|SSLSocketInputRecord.java:213|READ: TLSv1.2 change_cipher_spec, length = 1
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.118 IST|SSLSocketInputRecord.java:249|READ: TLSv1.2 change_cipher_spec, length = 1
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.118 IST|SSLSocketOutputRecord.java:258|WRITE: TLS13 handshake, length = 32
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.130 IST|SSLSocketInputRecord.java:213|READ: TLSv1.2 application_data, length = 65
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.130 IST|SSLSocketInputRecord.java:249|READ: TLSv1.2 application_data, length = 65
    javax.net.ssl|ALL|0D|Thread-0|2020-01-22 17:11:37.131 IST|X509Authentication.java:295|No X.509 cert selected for EC
    javax.net.ssl|ALL|0D|Thread-0|2020-01-22 17:11:37.131 IST|X509Authentication.java:295|No X.509 cert selected for EC
    javax.net.ssl|ALL|0D|Thread-0|2020-01-22 17:11:37.132 IST|X509Authentication.java:295|No X.509 cert selected for EC
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.133 IST|SSLSocketOutputRecord.java:258|WRITE: TLS13 handshake, length = 878
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.135 IST|SSLSocketInputRecord.java:213|READ: TLSv1.2 application_data, length = 911
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.135 IST|SSLSocketInputRecord.java:249|READ: TLSv1.2 application_data, length = 911
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.170 IST|SSLSocketOutputRecord.java:258|WRITE: TLS13 handshake, length = 264
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.171 IST|SSLSocketInputRecord.java:213|READ: TLSv1.2 application_data, length = 297
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.171 IST|SSLSocketInputRecord.java:249|READ: TLSv1.2 application_data, length = 297
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.174 IST|SSLSocketOutputRecord.java:258|WRITE: TLS13 handshake, length = 36
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.174 IST|SSLSocketInputRecord.java:213|READ: TLSv1.2 application_data, length = 69
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.174 IST|SSLSocketInputRecord.java:249|READ: TLSv1.2 application_data, length = 69
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.175 IST|SSLCipher.java:2021|KeyLimit write side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
    countdown value = 137438953472
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.176 IST|SSLSocketInputRecord.java:213|READ: TLSv1.2 change_cipher_spec, length = 1
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.176 IST|SSLSocketInputRecord.java:249|READ: TLSv1.2 change_cipher_spec, length = 1
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.176 IST|SSLCipher.java:1867|KeyLimit read side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
    countdown value = 137438953472
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.177 IST|SSLSocketOutputRecord.java:258|WRITE: TLS13 handshake, length = 36
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.177 IST|SSLSocketInputRecord.java:213|READ: TLSv1.2 application_data, length = 69
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.178 IST|SSLSocketInputRecord.java:249|READ: TLSv1.2 application_data, length = 69
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.178 IST|SSLCipher.java:2021|KeyLimit write side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
    countdown value = 137438953472
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.179 IST|SSLSocketOutputRecord.java:331|WRITE: TLS13 application_data, length = 18
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.179 IST|SSLCipher.java:1867|KeyLimit read side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
    countdown value = 137438953472
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.180 IST|SSLSocketOutputRecord.java:258|WRITE: TLS13 handshake, length = 50
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.181 IST|SSLSocketInputRecord.java:213|READ: TLSv1.2 application_data, length = 83
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.181 IST|SSLSocketInputRecord.java:213|READ: TLSv1.2 application_data, length = 51
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.181 IST|SSLSocketInputRecord.java:249|READ: TLSv1.2 application_data, length = 83
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.181 IST|SSLSocketInputRecord.java:249|READ: TLSv1.2 application_data, length = 51
    server received 18 bytes: TLSv1.3 is working
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.182 IST|SSLSocketOutputRecord.java:331|WRITE: TLS13 application_data, length = 18
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.183 IST|SSLSocketInputRecord.java:213|READ: TLSv1.2 application_data, length = 51
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.183 IST|SSLSocketImpl.java:550|duplex close of SSLSocket
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.183 IST|SSLSocketInputRecord.java:249|READ: TLSv1.2 application_data, length = 51
    client received 18 bytes: TLSv1.3 is working
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.184 IST|SSLSocketImpl.java:550|duplex close of SSLSocket
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.186 IST|SSLSocketOutputRecord.java:72|WRITE: TLS13 alert(user_canceled), length = 2
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.186 IST|SSLSocketOutputRecord.java:72|WRITE: TLS13 alert(user_canceled), length = 2
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.186 IST|SSLSocketOutputRecord.java:72|WRITE: TLS13 alert(close_notify), length = 2
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.186 IST|SSLSocketOutputRecord.java:72|WRITE: TLS13 alert(close_notify), length = 2
    javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.187 IST|SSLSocketImpl.java:1718|close the SSL connection (passive)
    javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.187 IST|SSLSocketImpl.java:1718|close the SSL connection (passive)
    server stopped
    what is the tls13 and why tlsv 1.2 is still working also can you please elaborate this more?

    Reply

Leave a Reply

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