Using TLS/SSL with MariaDB Connector/J

Overview

This document explains how to configure the MariaDB Java driver to support TLS/SSL.

Data can be encrypted during transfer using the Transport Layer Security (TLS) protocol. TLS/SSL permits transfer 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 the predecessor of TLS, and is not implemented as it is now considered insecure.

Server configuration

To ensure that SSL is correctly configured on the server, the query "SELECT @@have_ssl;" must return YES. If not, please refer to the server documentation.

TLS Protocol Version Support

MariaDB Server can be built with different TLS and cryptography libraries that support different TLS protocol versions.

TLS LibrarySupported TLS Protocol Versions
openSSLTLSv1, TLSv1.1, TLSv1.2, TLSv1.3
wolfSSLTLSv1, TLSv1.1, TLSv1.2, TLSv1.3
yaSSLTLSv1, TLSv1.1

See TLS and Cryptography Libraries Used by MariaDB for information about which TLS libraries are used in each package.

See Secure Connections Overview: TLS Protocol Version Support for more information about which TLS protocol versions are supported by MariaDB Server.

TLS Protocol Version Selection

During TLS/SSL negotiation, the client normally indicates the maximum level supported, but before 10.2, YaSSL doesn't recognize the TLSv1.2 flag and then doesn't fallback to theTLSv1.1 protocol.

If trying to connect with TLSv1.2 with a server before 10.2 using YaSSL, the connection will fail with the following exception :

  • with MariaDB server : "Caused by: javax.net.ssl.SSLException: Unsupported record version Unknown-0.0"
  • with MySQL server : "Caused by: javax.net.ssl.SSLHandshakeException: Remote host terminated the handshake"

The MariaDB Java driver by default uses java default supported protocols . If the servers are MariaDB on Unix or version >= 10.2 , consider adding TLSv1.2 protocol. This can be set using the "enabledSslProtocolSuites" option (example: enabledSslProtocolSuites=TLSv1,TLSv1.1,TLSv1.2).

In addition to the protocol, the driver relies on the Java default cipher list. The Java default enabled ciphers are listed here. JAVA allows cipher suites to be removed/excluded from use in the security policy using the Java system property "jdk.tls.disabledAlgorithms". The specific list of ciphers to be used can be set using the "enabledSslCipherSuites" driver option (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, the driver can be configured to use SSL, even if the user used for authentication is not set to use SSL, but the recommendation is to use a user created with "REQUIRE SSL". See CREATE USER for more details.

Example;

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

The "useSSL" option must be enabled to indicate to the driver to connect using SSL.

Testing can be done with the trustServerCertificate=true option 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");
	}
}

The connection will then be encrypted.

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

The Java system property "-Djavax.net.debug=all" permits logging all TLS exchanges.

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

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

Java has a default truststore that contains well-known CAs including Let's Encrypt (since java 8u101), VeriSign, Entrust, and GTE CyberTrust.trusted Certificate Authorities (CA).

If the server certificate is signed using a certificate chain using a root CA known in java default truststore, no additional step is required.

Using self signed certificates 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.

The Java search order for locating the trust store is:

  1. system property "javax.net.ssl.trustStore"
  2. $JAVA_HOME/lib/security/jssecacerts
  3. $JAVA_HOME/lib/security/cacerts (shipped by default)

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, the "trustServerCertificate" option 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 are 2 choices:

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

The dedicated trustore can be generated 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

The "serverSslCert" option permits setting the certificate location. The location can be used in one of 3 forms:

  • serverSslCert=/path/to/cert.pem (full path to certificate)
  • serverSslCert=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&serverSslCert=/path/to/cert.pem)) {
	try (Statement stmt = con.createStatement()) {
		stmt.execute("select 1");
	}
}

Handling self-signed certificates

Using self-signed certificates, you will not only have to register the root and intermediates certificates, but each individual certificate of each server! 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, the user must be created with "REQUIRE X509" so the server asks the driver for client certificates. See CREATE USER for more details.

Example:

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

If the user is not set with REQUIRE X509, only one way authentication will be done

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

Java stores this client certificate and private key in a keyStore file. A keystore file is similar to trustore, in fact trustore and keystore 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, the Java default keystore can be used, then no additional option is needed, or a dedicated keystore by using the "keyStore" option to indicate location and the "keyStorePassword" option to indicate the keystore password. In JKS keystore, an additional password for a specific key may have been set. The "keyPassword" option permits setting this password.

Troubleshooting

Diffie-Hellman size error

If using Java 7, the Diffie-Hellman (DH) prime size is limited to 1024 (JDK-6521495). Java has some artificial restriction that restricts 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 the following error occurs: "java.sql.SQLException: Trying to connect with ssl, but ssl not enabled in the server", SSL is not enabled on the server-side. Since the "useSSL=true" option is set, the connection failed. Execute "show variables like '%ssl%';" on the server-side to identify the SSL issue.

Server certificate is not provided to client

When the driver tries to connect using SSL, but no certificate is provided, or the "trustServerCertificate=true" option is not set, the driver will fail with the following 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"

Solution: - not recommended: set the "trustServerCertificate=true" option. - add the server certificate to the driver (see documentation above).

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

This can occur for a number of reasons:

  • The user / password is incorrect.
  • Some SSL options have been set on the user (can be checked using "select SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT FROM mysql.user u where u.User = '<myUser>';) and the connection attempt doesn't meet those requirements.

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.