使用Java建立与MySQL的安全连接Amazon RDS(SSL / TLS)

我正在尝试使用JDK 1.8和MySQL Connector / J版本5.1.23安全地连接到Amazon RDS,我对MySQL Connector / J文档中的说明感到困惑。

说明指出在当前目录中创建信任库并导入服务器的CA证书。 当我运行以下命令时,我被要求输入密码,我不知道输入什么:

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

另一方面,亚马逊的指示指出证书由证书颁发机构签名。 SSL证书包括数据库实例端点作为公共名称(CN)。 公钥也存储在http://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem中 。

虽然我理解SSL / TSL和私钥/公钥的概念,但我很困惑如何实现它?

当我运行SHOW VARIABLES LIKE 'have_ssl'; 命令我收到“是”作为值。 当我连接到数据库并运行SHOW SESSION STATUS LIKE 'Ssl_version'; ,我没有收到任何价值。

如何建立从Java到MySQL数据库的安全连接?

更新:我使用的是ubuntu,我使用keytool在JRE目录/usr/lib/jvm/java/jre/生成truststore.jks文件。 我还执行了以下命令: GRANT USAGE ON db_name.* To 'username'@'address' REQUIRE SSL;

以下代码是我连接数据库的方式:

 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); } } 

上面的代码产生Communications link failure消息,并且The last packet successfully received from the server was 24 milliseconds ago. The last packet sent successfully to the server was 24 milliseconds ago The last packet successfully received from the server was 24 milliseconds ago. The last packet sent successfully to the server was 24 milliseconds ago 。 我能够成功连接?verifyServerCertificate=true&useSSL=true&requireSSL=true从uri中删除, REQUIRE SSL从用户权限中删除。

更新2:我已将代码恢复到基础,当我运行以下代码时:

 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(); } } } 

我收到以下错误消息:

 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.(ConnectionImpl.java:794) at com.mysql.jdbc.JDBC4Connection.(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.(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.(PKIXParameters.java:120) at java.security.cert.PKIXBuilderParameters.(PKIXBuilderParameters.java:104) at sun.security.validator.PKIXValidator.(PKIXValidator.java:88) ... 32 more 

我还确认使用Workbench和用户能够使用SSL登录的.pem文件。

更新3:将javax.net.debug设置为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)wl..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) 

当您运行以下命令并要求输入密码时,您应键入“changeit”。 这是密钥库的默认密码

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

接下来,确保您使用的mysql用户配置为需要SSL,如下所示

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

接下来,确保从http://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem下载的rds软件包导入到尝试连接的应用服务器的默认java cacerts上到AWS RDS。

最后,您需要将以下三个属性值’true’附加到您的mysqlurl

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

例如:

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

希望这可以帮助!

Java keytool一次只导入一个证书,但RDS组合CA捆绑包有许多CA证书。 您需要单独导入捆绑包中的每个证书。 当您尝试导入捆绑包时,它只导入一个证书,该证书可能不是您需要信任RDS实例的根CA.

在linux上,您可以使用CSPLIT工具拆分pem:

 csplit -b %02d.pem -z rds-combined-ca-bundle.pem /-----BEGIN/ {*} 

然后,您必须单独将每个pem导入密钥库。

在Mac OS X和FreeBSD上,过程类似。 这将分解并导入rds-combined-cabundle.pem中的所有证书。 您需要更改OS X的路径,否则它将是相同的。

 split -p "-----BEGIN CERTIFICATE-----" rds-combined-ca-bundle.pem individual- find . -iname 'individual*' -exec keytool -import -file {} -alias {} -storepass changeit -keystore /usr/local/openjdk8/jre/lib/security/cacerts \;