Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can Java not connect to MySQL 5.7 after the latest JDK update and how should it be fixed? (ssl.SSLHandshakeException: No appropriate protocol)

Tags:

java

mysql

tls1.2

In the latest update to the JDK in April 2021 (11.0.11+9-0ubuntu2~18.04) support for TLSv1 and TLSv1.1 was dropped, presumably because since March 2021 those versions are no longer supported. This is evident by the diff in the java.security file:

Before:

jdk.tls.disabledAlgorithms=SSLv3, RC4, DES, MD5withRSA, DH keySize < 1024, \
    EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \
    include jdk.disabled.namedCurves

After:

jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, RC4, DES, MD5withRSA, \
    DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \
    include jdk.disabled.namedCurves

which is also discussed in this SO post: SSLHandShakeException No Appropriate Protocol . In that thread there are also more answers popping up the last few days since the updated JDK version.

After this update of the JDK, we received the error

java.sql.SQLException: An attempt by a client to checkout a Connection has timed out.

with c3p0.

After switching to hikari we got a more meaningful error:

ERROR [2021-04-29 16:21:16,426] com.zaxxer.hikari.pool.HikariPool: HikariPool-1 - Exception during pool initialization.
! javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)

We're running on MySQL 5.7.33-0ubuntu0.18.04.1. Now in my understanding as described here, MySQL 5.7 supports TLSv1.2. Also when running SHOW VARIABLES LIKE 'tls_version'; we get TLSv1,TLSv1.1,TLSv1.2 which suggest that TLSv1.2 is supported.

So then the question is, why don't the JDK and MySQL just agree on using TLSv1.2 and what can we do about it, to make them communicate with TLSv1.2?

Note: I don't think changing the java.security file as suggested in the other thread is a good long term solution to this problem!

like image 330
bersling Avatar asked Apr 30 '21 11:04

bersling


People also ask

What is the latest MySQL Connector Java?

Connector/J 8.0. 30 MySQL Connector/J 8.0 is compatible with all MySQL versions starting with MySQL 5.6. Additionally, MySQL Connector/J 8.0 supports the new X DevAPI for development with MySQL Server 8.0.

What software driver can be used to connect Java application to the MySQL database is?

To connect to MySQL from Java, you have to use the JDBC driver from MySQL. The MySQL JDBC driver is called MySQL Connector/J.

Why don't the JDK and MySQL just agree on TLS?

Why don't the JDK and MySQL just agree on using TLSv1.2? Although both parties do actually support TLSv1.2, the problem you were experiencing is introduced by the default behavior of Connector/J.

Do I need to enable TLS on connector/J by default?

Therefore, one has to enable it explicitly. For Connector/J 8.0.18 and earlier when connecting to MySQL Community Server 5.6 and 5.7 using the JDBC API: Due to compatibility issues with MySQL Server compiled with yaSSL, Connector/J does not enable connections with TLSv1.2 and higher by default.

What should I do if MySQL JDBC driver error occurs?

5 In case someone found that because of MySQL JDBC Driver error, you need to only exclude TLSv1 and TLSv1.1 from that list and it will work. – Oleg Kurbatov

What version of TLS does MySQL support?

Now in my understanding as described here, MySQL 5.7 supports TLSv1.2. Also when running SHOW VARIABLES LIKE 'tls_version'; we get TLSv1,TLSv1.1,TLSv1.2 which suggest that TLSv1.2 is supported. So then the question is, why don't the JDK and MySQL just agree on using TLSv1.2 and what can we do about it, to make them communicate with TLSv1.2?


2 Answers

As @skelwa already commented you will need to add the enabledTLSProtocols=TLSv1.2 configuration property in the connection string to resolve your issue.

A complete connection string for Connector/J could look like this:

jdbc:mysql://<host>:<port>/<dbname>?enabledTLSProtocols=TLSv1.2

For r2dbc you will need to use tlsVersion=TLSv1.2 instead.

For Connector/J v8.0.28 enabledTLSProtocols was renamed to tlsVersions (see note). However, the original name remains as an alias.


The question that remains is:

Why don't the JDK and MySQL just agree on using TLSv1.2?

Although both parties do actually support TLSv1.2, the problem you were experiencing is introduced by the default behavior of Connector/J. For compatibility reasons Connector/J does not enable TLSv1.2 and higher by default. Therefore, one has to enable it explicitly.

See the following note:

For Connector/J 8.0.18 and earlier when connecting to MySQL Community Server 5.6 and 5.7 using the JDBC API: Due to compatibility issues with MySQL Server compiled with yaSSL, Connector/J does not enable connections with TLSv1.2 and higher by default. When connecting to servers that restrict connections to use those higher TLS versions, enable them explicitly by setting the Connector/J connection property enabledTLSProtocols (e.g., set enabledTLSProtocols=TLSv1,TLSv1.1,TLSv1.2).


WARNING: please be aware that solutions suggesting editing jdk.tls.disabledAlgorithms inside of jre/lib/security pose a security risk to your application and changing anything there might have severe implications! There are reasons why those protocols were disabled and one should not simply remove everything or even just parts from that list.


Note: if you want to get more low level insights from the JDK to debug your problem you can enable ssl debug logs by passing the following configuration to the java comand:

-Djavax.net.debug=ssl,handshake or even -Djavax.net.debug=all

In your case you will see something like:

...(HANDSHAKE_FAILURE): Couldn't kickstart handshaking (
"throwable" : {
  javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
    at java.base/sun.security.ssl.HandshakeContext.<init>(HandshakeContext.java:170)
    at java.base/sun.security.ssl.ClientHandshakeContext.<init>(ClientHandshakeContext.java:98)
    ...
like image 76
Kristianmitk Avatar answered Oct 20 '22 02:10

Kristianmitk


I just added useSSL=false and it worked for me.

jdbc:mysql://<host>:<port>/<dbname>?useSSL=false

I have JDK 8, MySQL 5.7 and mysql-connector-java lib with version 5.1.38

This is useful when you want to execute quickly in local environment only (not staging/test/prod)

like image 14
Ankur Raiyani Avatar answered Oct 20 '22 03:10

Ankur Raiyani