Using Java to establish a secure connection to MySQL Amazon RDS (SSL/TLS)

Greg picture Greg · Aug 22, 2015 · Viewed 18.6k times · Source

I am trying to securely connect to Amazon RDS using JDK 1.8 and MySQL Connector/J version 5.1.23 and I am confused about the instructions in MySQL Connector/J documentation.

The instructions say to create a truststore in the current directory and import the server's CA certificate. When I run the below command I am asked for a password and I do not know what to enter:

keytool -import -alias mysqlServerCACert -file file_location.pem -keystore truststore

The instructions from Amazon on the other hand point out that certificates are signed by a certificate authority. The SSL certificate includes the DB instance endpoint as the Common Name (CN). The public key is also stored at http://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem.

Whilst I understand the concept of SSL/TSL and private/public keys, I am confused how to implement this?

When I run SHOW VARIABLES LIKE 'have_ssl'; command I receive "yes" as a value. When I am connected to the database and run SHOW SESSION STATUS LIKE 'Ssl_version';, I receive no value back.

How do you establish a secure connection from Java to a MySQL database?

Update: I am using ubuntu and I used the keytool to generate a truststore.jks file in the JRE directory /usr/lib/jvm/java/jre/. I also executed the following command: GRANT USAGE ON db_name.* To 'username'@'address' REQUIRE SSL;

The below code is how I am connecting to the database:

import com.mysql.jdbc.Connection;

public class mainClass{

    public static void main(String[] args){

        System.setProperty("javax.net.ssl.trustStore", "/usr/lib/jvm/java/jre/truststore.jks");
        System.setProperty("javax.net.ssl.trustStorePassword", "truststore_password");

        final String url = "jdbc:mysql://mysql_rds_enpoint:port/db_name?verifyServerCertificate=true&useSSL=true&requireSSL=true";
        final String username = "username";
        final String password = "password";

        Connection mysqlConnection = null;
        mysqlConnection = (Connection) mysqlMethods.connectToMysql(url, username, password);
    }
}

The above code produces a Communications link failure message and The last packet successfully received from the server was 24 milliseconds ago. The last packet sent successfully to the server was 24 milliseconds ago. I am able to successfully connect when ?verifyServerCertificate=true&useSSL=true&requireSSL=true is removed from the uri and REQUIRE SSL is removed from the user permissions.

Update 2: I have taken the code back to basics and when I run the below code:

import java.sql.DriverManager;
import java.sql.SQLException;

class testClass{

    public static void main(String[] args){
        System.setProperty("javax.net.ssl.trustStore", "/usr/lib/jvm/java/jre/truststore.jks");
        System.setProperty("javax.net.ssl.trustStorePassword", "truststore_password");

        final String url = "jdbc:mysql://mysql_rds_enpoint:port/db_name?verifyServerCertificate=true&useSSL=true&requireSSL=true";
        final String username = "username";
        final String password = "password";

        try {
            Object connection = DriverManager.getConnection(url, username, password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

I get the following error message:

com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet successfully received from the server was 206 milliseconds ago.  The last packet sent successfully to the server was 203 milliseconds ago.
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:389)
    at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1038)
    at com.mysql.jdbc.ExportControlled.transformSocketToSSLSocket(ExportControlled.java:110)
    at com.mysql.jdbc.MysqlIO.negotiateSSLConnection(MysqlIO.java:4760)
    at com.mysql.jdbc.MysqlIO.proceedHandshakeWithPluggableAuthentication(MysqlIO.java:1629)
    at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1206)
    at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2239)
    at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2270)
    at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2069)
    at com.mysql.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:794)
    at com.mysql.jdbc.JDBC4Connection.<init>(JDBC4Connection.java:44)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:389)
    at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:399)
    at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:325)
    at java.sql.DriverManager.getConnection(DriverManager.java:664)
    at java.sql.DriverManager.getConnection(DriverManager.java:247)
    at apples.main(mainClass.java:18)

Caused by: javax.net.ssl.SSLException: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)
    at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
    at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1906)
    at sun.security.ssl.SSLSocketImpl.handleException(SSLSocketImpl.java:1889)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1410)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
    at com.mysql.jdbc.ExportControlled.transformSocketToSSLSocket(ExportControlled.java:95)
    ... 18 more

Caused by: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
    at sun.security.validator.PKIXValidator.<init>(PKIXValidator.java:90)
    at sun.security.validator.Validator.getInstance(Validator.java:179)
    at sun.security.ssl.X509TrustManagerImpl.getValidator(X509TrustManagerImpl.java:312)
    at sun.security.ssl.X509TrustManagerImpl.checkTrustedInit(X509TrustManagerImpl.java:171)
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:184)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1488)
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
    at sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
    ... 20 more

Caused by: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
    at java.security.cert.PKIXParameters.setTrustAnchors(PKIXParameters.java:200)
    at java.security.cert.PKIXParameters.<init>(PKIXParameters.java:120)
    at java.security.cert.PKIXBuilderParameters.<init>(PKIXBuilderParameters.java:104)
    at sun.security.validator.PKIXValidator.<init>(PKIXValidator.java:88)
    ... 32 more

I have also confirmed using Workbench and the .pem file that the user is able to login using SSL.

Update 3: Debug info after setting javax.net.debug to ssl:

keyStore is : 
keyStore type is : jks
keyStore provider is : 
init keystore
init keymanager of type SunX509
trustStore is: /usr/lib/jvm/java/jre/truststore.jks
trustStore type is : jks
trustStore provider is : 
init truststore
adding as trusted cert:
  Subject: CN=aws.amazon.com/rds/, OU=RDS, O=Amazon.com, L=Seattle, ST=Washington, C=US
  Issuer:  CN=aws.amazon.com/rds/, OU=RDS, O=Amazon.com, L=Seattle, ST=Washington, C=US
  Algorithm: RSA; Serial number: 0xe775b657e21a8128
  Valid from Tue Apr 06 08:44:31 AEST 2010 until Sun Apr 05 08:44:31 AEST 2015

trigger seeding of SecureRandom
done seeding SecureRandom
Ignoring unavailable cipher suite: TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
Ignoring unavailable cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
Ignoring unavailable cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
Ignoring unavailable cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256
Ignoring unavailable cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
Ignoring unavailable cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
Ignoring unavailable cipher suite: TLS_RSA_WITH_AES_256_GCM_SHA384
Ignoring unavailable cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Ignoring unavailable cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
Ignoring unavailable cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unavailable cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
Ignoring unavailable cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
Ignoring unavailable cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
%% No cached client session
*** ClientHello, TLSv1
RandomCookie:  GMT: 1425012760 bytes = { 250, 182, 202, 168, 139, 248, 125, 121, 251, 188, 182, 110, 1, 234, 29, 105, 158, 219, 212, 122, 105, 57, 87, 80, 44, 141, 121, 154 }
Session ID:  {}
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
Compression Methods:  { 0 }
Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1, sect283r1, secp384r1, sect409k1, sect409r1, secp521r1, sect571k1, sect571r1, secp160k1, secp160r1, secp160r2, sect163r1, secp192k1, sect193r1, sect193r2, secp224k1, sect239k1, secp256k1}
Extension ec_point_formats, formats: [uncompressed]
Extension server_name, server_name: [type=host_name (0), value=abcd.abcd123.ap-southeast-1.rds.amazonaws.com]
***
main, WRITE: TLSv1 Handshake, length = 202
main, READ: TLSv1 Handshake, length = 74
*** ServerHello, TLSv1
RandomCookie:  GMT: -1571637097 bytes = { 78, 228, 80, 30, 162, 233, 208, 59, 196, 166, 88, 129, 155, 99, 123, 252, 123, 153, 129, 25, 144, 235, 185, 112, 68, 79, 2, 79 }
Session ID:  {104, 7, 155, 31, 238, 102, 222, 218, 193, 219, 108, 206, 57, 235, 53, 253, 1, 200, 64, 46, 92, 60, 63, 251, 142, 106, 233, 54, 27, 110, 79, 128}
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA
Compression Method: 0
***
Warning: No renegotiation indication extension in ServerHello
%% Initialized:  [Session-1, TLS_RSA_WITH_AES_128_CBC_SHA]
** TLS_RSA_WITH_AES_128_CBC_SHA
main, READ: TLSv1 Handshake, length = 1057
*** Certificate chain
chain [0] = [
[
  Version: V3
  Subject: C=US, ST=Washington, L=Seattle, O=Amazon.com, OU=RDS, CN=sifrmedium.cove4uttgoa2.ap-southeast-2.rds.amazonaws.com
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 1024 bits
  modulus: 7182371456073335432842458631460148573771628715802835656528548198324749485547005738326606223242830258829871966755842342342342344059076665280539770508892924695580383343581127930380098999434723444448883595421930164673020286553920728427
  public exponent: 65537
  Validity: [From: Sun Jun 21 11:55:57 AEST 2015,
               To: Fri Mar 06 09:03:24 AEDT 2020]
  Issuer: CN=Amazon RDS ap-southeast-2 CA, OU=Amazon RDS, O="Amazon Web Services, Inc.", L=Seattle, ST=Washington, C=US
  SerialNumber: [    810c7b2c 621bcf07 366cad3b ab405b]

Certificate Extensions: 2
[1]: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 89 50 9D 8D 0A 1D 42 A7   D6 36 2E 99 27 44 61 FC  .P.......6..'Ia.
0010: 23 FC D7 F0                                        #...
]
[CN=Amazon RDS Root CA, OU=Amazon RDS, O="Amazon Web Services, Inc.", L=Seattle, ST=Washington, C=US]
SerialNumber: [    46]
]

[2]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 0C 5C 64 D5 CE C4 27 13   34 01 A0 0D 64 8C B1 C9  .\d...'.4...d<..
0010: 4D 6C 3D 16                                        M\=.
]
]

]
  Algorithm: [SHA1withRSA]
  Signature:
0000: 16 53 AA 7A 55 49 84 87   5C EC 49 B9 BD 51 98 83  .S.zUI..\.I..Q..
0010: BA 91 84 72 29 77 19 6C   F7 CA 0A 36 09 AC 70 F4  ...r)w.l...6..p.
0020: 8A 2C 90 FD 36 3D 3D CD   E9 BA 2C 5C 95 C3 79 78  .,..6==...,\..yx
0030: CE FA 46 A0 3D 77 31 11   E3 5B 9C 84 AF 4D 60 51  ..F.=w1..[...M`Q
0040: 80 52 04 1E 65 4D 6E 5B   92 E1 69 79 12 8E 56 0F  .R..eMn[..iy..V.
0050: 83 E4 F8 31 4E A7 65 4E   C2 BB 97 A8 B4 67 79 F4  ...1N.eN.....gy.
0060: CD 37 F7 00 A5 42 B9 E0   CB D9 CA 81 EC E3 A3 E3  .7...B..........
0070: FB 05 40 2F 9A 4B 5D AE   7C 30 F4 BA ED DC 74 AC  ..@/.K]..0....t.
0080: 89 30 AD 13 26 F7 88 09   66 01 E1 4B 3A 8C B1 3D  .0..&...f..K:..=
0090: 28 6F 74 1D B0 00 25 FB   9B 1E 00 8D F1 1F 83 84  (ot...%.........
00A0: CE D7 92 39 78 C4 70 26   8E 3C 05 1C 10 3C A3 E0  ...9x.p&.<...<..
00B0: CC 30 F0 A0 68 B1 26 C4   23 AC C0 C6 8A 27 DB 3C  .0..h.&.#....'.<
00C0: 7F 38 DD EE 92 1B A3 A4   99 13 9F 80 62 66 C8 F0  .8..........bf..
00D0: 46 25 38 C3 99 0A A2 A7   04 E5 FF 4D 31 2B E4 82  F%8........M1+..
00E0: BE D9 D5 07 28 96 17 07   30 B9 EC BA 26 F3 7B C3  ....(...0...&...
00F0: 1A CF 13 73 58 96 F2 87   3F FD 86 09 35 FA A3 7B  ...sX...?...5...

]
***
%% Invalidated:  [Session-1, TLS_RSA_WITH_AES_128_CBC_SHA]
main, SEND TLSv1 ALERT:  fatal, description = certificate_unknown
main, WRITE: TLSv1 Alert, length = 2
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
main, called close()
main, called closeInternal(true)

Answer

virag picture virag · Oct 8, 2015

When you run the below command and are asked for the password you should type "changeit". That is the default password for the keystore

keytool -import -alias mysqlServerCACert -file file_location.pem -keystore truststore

Next, make sure the mysql user you are using is configured to require SSL as below

GRANT ALL PRIVILEGES ON test.* TO 'root'@'localhost' REQUIRE SSL;

Next, make sure the rds bundle which you downloaded from https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem is imported on the default java cacerts of the app server which is trying to connect to the AWS RDS.

Lastly, you need append the below three properties with value 'true' to your mysql url

?verifyServerCertificate=true&useSSL=true&requireSSL=true

Eg:

final String url = "jdbc:mysql://mysql_rds_enpoint:port/db_name?verifyServerCertificate=true&useSSL=true&requireSSL=true";

Hope this helps!