Using TLS/SSL with MariaDB java connector

You are viewing an old version of this article. View the current version here.

Overview

This document goal is to explain how to configure MariaDB java driver to support TLS/SSL.

Data can be encrypted during transfer using the Transport Layer Security (TLS) protocol. TLS/SSL permit transfert encryption, and optionally server and client identity validation.

The term SSL (Secure Sockets Layer) is often used interchangeably with TLS, although strictly-speaking the SSL protocol is TLS predecessor, and is not implemented (now considered insecure).


Server configuration

Goal of this document is not to indicate server configuration, to ensure that SSL is well configured on server, the query "SELECT @@have_ssl;" must return YES. If not, please refer to the server documentation.

Protocol

MariaDB servers can be build with different cryptographic tools, that support TLS differently.

ToolsSupported TLS protocol
openSSLTLSv1, TLSv1.1 and TLSv1.2
YaSSLTLSv1, TLSv1.1

OpenSSL are used by default on Unix, YaSSL on windows. MySQL community server use YaSSL, MySQL entreprise openSSL.

During TLS/SSL negotiation, client indicate normally the maximum level supported, but YaSSL doesn't recognize the TLSv1.2 flag and then doesn't fallback to TLSv1.1 protocol. If trying to connect with TLSv1.2 with a server using YaSSL, connection then fail with the following exception :

  • with MariaDB server : Caused by: javax.net.ssl.SSLException: Unsupported record version Unknown-0.0 at sun.security.ssl.InputRecord.checkRecordVersion(InputRecord.java:552) at sun.security.ssl.InputRecord.readV3Record(InputRecord.java:565) at sun.security.ssl.InputRecord.read(InputRecord.java:529) at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387) at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.handleConnectionPhases(AbstractConnectProtocol.java:675) ... 38 more
  • with MySQL server : Caused by: javax.net.ssl.SSLHandshakeException: Remote host terminated the handshake at sun.security.ssl.SSLSocketImpl.readRecord(java.base@9-ea/SSLSocketImpl.java:1053) at sun.security.ssl.SSLSocketImpl.readRecord(java.base@9-ea/SSLSocketImpl.java:968) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(java.base@9-ea/SSLSocketImpl.java:1395) at sun.security.ssl.SSLSocketImpl.startHandshake(java.base@9-ea/SSLSocketImpl.java:1422) at sun.security.ssl.SSLSocketImpl.startHandshake(java.base@9-ea/SSLSocketImpl.java:1406) at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.handleConnectionPhases(AbstractConnectProtocol.java:675) ... 44 more Caused by: java.io.EOFException: SSL peer shut down incorrectly at sun.security.ssl.SSLSocketInputRecord.decodeInputRecord(java.base@9-ea/SSLSocketInputRecord.java:223) at sun.security.ssl.SSLSocketInputRecord.decode(java.base@9-ea/SSLSocketInputRecord.java:178) at sun.security.ssl.SSLSocketImpl.readRecord(java.base@9-ea/SSLSocketImpl.java:1026)

MariaDB java driver use by default only TLSv1, TLSv1.1 protocol. If servers are MariaDB on unix, consider adding TLSv1.2 protocol. This can be set using option "enabledSslProtocolSuites" (example: enabledSslProtocolSuites=TLSv1,TLSv1.1,TLSv1.2).

Additionally to protocol, Driver relay on java default cipher list. java default enabled cypher can are list here. JAVA allows cipher suites to be removed/excluded from use in the security policy using java system property "jdk.tls.disabledAlgorithms". Specific list of cipher to used can be set using driver option "enabledSslCipherSuites" (example : "enabledSSLCipherSuites=TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,...")



Oracle Java cryptographic limitation

Due to import regulations in some countries, the java Oracle implementation provides a default cryptographic jurisdiction policy file that limits the strength of cryptographic algorithms.

Here are the maximum key sizes allowed by this "strong" version of the jurisdiction policy files:

AlgorithmMaximum Keysize
DES64
DESede*
RC2128
RC4128
RC5128
RSA*

If stronger algorithms are needed (for example, AES with 256-bit keys), the JCE Unlimited Strength Jurisdiction Policy Files must be obtained and installed in the JDK/JRE to remove those limitation.

It is strongly recommended, if permissible under local regulations, to have JCE installed.



One way SSL authentication

By default, driver can be configured to use SSL, even if the user used for authentication is not set to use SSL, but recommendation is to use a user created with "REQUIRE SSL". More documentation can be found on Create user server documentation.

Example; CREATE USER 'myUser'@'%' IDENTIFIED BY 'MyPwd'; GRANT ALL ON db_name.* TO 'myUser'@'%' REQUIRE SSL;

Option "useSSL" must be enabled to indicate to driver to connect using SSL.

Test can be done with option trustServerCertificate=true to validate encryption (NOT TO BE USED IN PRODUCTION !)

	try (Connection con = DriverManager.getConnection("jdbc:mariadb://localhost/myDb?user=myUser&password=MyPwd&useSSL=true&trustServerCertificate=true")) {
		try (Statement stmt = con.createStatement()) {
			stmt.execute("select 1");
		}
	}

connection will then be encrypted.

If trustServerCertificate option is not set, an exception "unable to find valid certification path to requested target" will be thrown. If server doesn't support SSL, an exception "Trying to connect with ssl, but ssl not enabled in the server" will be thrown.

Java system property "-Djavax.net.debug=all" permit to log all TLS exchanges.

To validate server identity, server root and intermediate certificates must be provided to driver. There are several ways to configure this :

  • use java default truststore in JKS format (or PKCS12 using java 9)
  • use a dedicated truststore in JKS format (or PKCS12 using java 9)
  • provide certificate

Using self signed certificated has some specificities.

Java default truststore

Java trustStore is a file that contains certificates of trusted SSL servers, or of Certificate Authorities trusted to identify servers. Truststore can be protected by a password.

Java search order for the locating the trust store is: 1) $JAVA_HOME/lib/security/jssecacerts, then 2) $JAVA_HOME/lib/security/cacerts

To add a certificate from a CA not included in the truststore, locate the default truststore on your system. The default truststore is located in the $JAVA_HOME/jre/lib/security/cacerts.

//copy the java truststore to jssecacerts
cp $JAVA_HOME/jre/lib/security/cacerts $JAVA_HOME/jre/lib/security/jssecacerts
//add your certificat to truststore
keytool -importcert -file myCA-root.cer -alias myCA -keystore /usr/java/default/jre/lib/security/jssecacerts -storepass changeit

with our previous example, option "trustServerCertificate" can then be removed

	try (Connection con = DriverManager.getConnection("jdbc:mariadb://localhost/myDb?user=myUser&password=MyPwd&useSSL=true")) {
		try (Statement stmt = con.createStatement()) {
			stmt.execute("select 1");
		}
	}



Use a dedicated truststore

if not using the alternative jssecacerts trustStore, there is 2 choices :

  • use java system properties "javax.net.ssl.trustStore" to defined your specific trustStore. Be careful, because then, this trustore will replace totally the default cacerts/jssecacerts, and only the certificates imported will be trusted !
  • indicate to driver the location of dedicated trustore using option "trustStore" and if needed "trustStorePassword"

dedicated trustore can be generate using this command :

   
   //Import the root certificate and create the truststore
   //You will be prompted to confirm that the root certificate is trustworthy. Be sure to verify that the certificate is genuine before you import it.
   keytool -importcert -keystore myTrustStore.jks -alias myCA-root -storepass myTrustPwd -file myCA-root.cer
   
   //Import the intermediate certificate into the truststore created in Step 1:   
   keytool -importcert -keystore myTrustStore.jks -alias myCA-intermediate -storepass myTrustPwd -file myCA-intermediate.cer   

with our previous example, trustServerCertificate can then be removed

	try (Connection con = DriverManager.getConnection("jdbc:mariadb://localhost/myDb?user=myUser&password=MyPwd&trustStore=/pathToTrustStore/myTrustStore.jks&trustStorePassword=mypwd")) {
		try (Statement stmt = con.createStatement()) {
			stmt.execute("select 1");
		}
	}



Provide certificate directly

Option "serverSslCert" permit to indicate certificate location. Location can be used in one of 3 forms :

  • sslServerCert=/path/to/cert.pem (full path to certificate)
  • sslServerCert=classpath:relative/cert.pem (relative to current classpath)
  • or as verbatim DER-encoded certificate string "------BEGING CERTIFICATE-----..." .

example :

	try (Connection con = DriverManager.getConnection("jdbc:mariadb://localhost/myDb?user=myUser&password=MyPwd&sslServerCert=/path/to/cert.pem)) {
		try (Statement stmt = con.createStatement()) {
			stmt.execute("select 1");
		}
	}



handling self signed certificat

Using self-signed certificates, you will not have just to register the root and intermediates certificates, but each individual certificates of each servers ! So if using more than one server, each self-signed certificate must be registered.

Import the test certificate into the truststore:

    keytool -importcert -keystore my.truststore -alias server1 -storepass trustChangeMe -file server1.cer



Mutual (2-way) authentication

Mutual SSL authentication or certificate based mutual authentication refers to two parties authenticating each other through verifying the provided digital certificate so that both parties are assured of the others' identity.

To enable mutual authentication, user must be created with "REQUIRE X509" so the server ask driver for client certificates. More documentation can be found on https:mariadb.com/kb/en/mariadb/create-user/.

example; CREATE USER 'myUser'@'%' IDENTIFIED BY 'MyPwd'; GRANT ALL ON db_name.* TO 'myUser'@'%' REQUIRE X509;

Client (driver) must then have his own certificat too (and related private key). If driver doesn't provide certificate, and user used to connect is defined with "REQUIRE X509", server will then return a basic "Access denied for user". Check how user is defined with "select SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT FROM mysql.user u where u.User = '<myUser>'".

Java stored this client certificate and private key in a keyStore file. A keystore file is similar to trustore, in fact trustore and keystore are they are often the same file.

Example of generating a keystore in JKS format :

  # generate a keystore with the client cert & key
  openssl pkcs12 \
    -export \
    -in "${clientCertFile}" \
    -inkey "${clientKeyFile}" \
    -out "${tmpKeystoreFile}" \
    -name "mariadbAlias" \
    -passout pass:kspass

  # convert PKSC12 to JKS
  keytool \
    -importkeystore \
    -deststorepass kspass \
    -destkeypass kspass \
    -destkeystore "${clientKeystoreFile}" \
    -srckeystore ${tmpKeystoreFile} \
    -srcstoretype PKCS12 \
    -srcstorepass kspass \
    -alias "mariadbAlias"

like truststore, java default keystore can be used, then no additional option is needed, or a dedicated keystore by using option "keyStore" to indicate location and option "keyStorePassword" to indicate keystore password. in JKS keystore, an additional password for a specific key can have been set, option "keyPassword" permit to indicate this password.



Troubleshooting


diffie-hellman size error

If using java 7, diffie-hellman (DH) prime size is limited to 1024 (JDK-6521495). Java has some artificial restriction that restrict size : Java 7 max size is 1024, java 8 is 2048, java 9 will be 8192. java 7 with extended support has a 2048 limit.

SSL not enabled on server.

if having the following error :

java.sql.SQLException: Trying to connect with ssl, but ssl not enabled in the server
	at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.handleConnectionPhases(AbstractConnectProtocol.java:682)
	at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.connect(AbstractConnectProtocol.java:397)
	at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.connectWithoutProxy(AbstractConnectProtocol.java:1013)
	at org.mariadb.jdbc.internal.util.Utils.retrieveProxy(Utils.java:481)

SSL is not enabled server size. Since option useSSL=true is set, connection failed. execute "show variables like '%ssl%';" on server-side to identify the SSL issue.

server certificat is not provided to client

When driver try to connect using SSL, but no certificate is provided / option trustServerCertificate=true is not set, driver will fail with the folowing exception :

Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387)
	at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
	at sun.security.validator.Validator.validate(Validator.java:260)
	at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
	at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
	at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
	at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1491)
	... 30 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
	at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
	at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
	at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
	... 36 more

solution : - not recommended : set option trustServerCertificate=true - add server certificate to driver (check documentation above)

java.sql.SQLInvalidAuthorizationSpecException: Could not connect: Access denied for user

This can occur because of lot of reason. user / password is incorrect

If some SSL option have been set on user ( can be checked using query "select SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT FROM mysql.user u where u.User = '<myUser>';) and connection attempt doesn't meet those requirement.

Comments

Comments loading...
Content reproduced on this site is the property of its respective owners, and this content is not reviewed in advance by MariaDB. The views, information and opinions expressed by this content do not necessarily represent those of MariaDB or any other party.