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:
- 0-RTT data
- Post-handshake authentication
- Signed certificate timestamps (SCT)
- ChaCha20/Poly1305 cipher suites (targeted to Java 12)
- x25519/x448 elliptic curve algorithms (targeted to Java 12)
- edDSA signature algorithms (targeted to Java 12)
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”.
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
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
andjavax.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.
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
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.
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)
What Java version did you use? TLS 1.3 has been supported since Java 11.
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?
The TLS 1.3 specification requires to use “0x0303” value in the TLSPlaintext.legacy_record_version field. This value means “TLS 1.2”. See the SSLSocketInputRecord class, it prints the TLSPlaintext.legacy_record_version field:
http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java#l215
I think that’s why JSSE prints “TLSv1.2” while it’s reading messages on the record layer:
javax.net.ssl|DEBUG|0D|Thread-0|2020-01-22 17:11:37.091 IST|SSLSocketInputRecord.java:213|READ: TLSv1.2 handshake, length = 237
On the other hand, the SSLSocketOutputRecord class behaves differently. It prints the protocol version from the “protocolVersion” which has been set before:
http://hg.openjdk.java.net/jdk/jdk11/file/tip/src/java.base/share/classes/sun/security/ssl/SSLSocketOutputRecord.java#l70
It appears to be the protocol version which has been negotiated. I think that’s why JSSE prints “TLSv1.2” while it’s writing messages:
javax.net.ssl|DEBUG|01|main|2020-01-22 17:11:37.090 IST|SSLSocketOutputRecord.java:258|WRITE: TLS13 handshake, length = 237
In your case, I think TLS 1.3 is actually used (I hope so). But JSSE log messages are confusing. It looks like a minor bug to me.