I am trying to write a Java application (using Java 17) that connects to an AWS RDS MySQL instance using IAM authentication. Amazon has a document describing how to do this, along with a complete Java example.
The Java example uses SSL to connect, so I tried the following certificate bundles listed on the Amazon document that describes using SSL to connect to RDS:
- global-bundle.pem
- us-west-2-bundle.pem (because our RDS instance is located in us-west-2)
- rds-ca-2019-us-west-2.pem - used in the example code, but is not listed in the above SSL document for some reason
Here is the example code in simplified form:
X509Certificate x509Cert;
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
URL url = new File("c:\\users\\me\\documents\\global-bundle.pem").toURI().toURL();
try (InputStream certInputStream = url.openStream()) {
x509Cert = (X509Certificate) certFactory.generateCertificate(certInputStream);
}
File keyStoreFile = File.createTempFile("sys-connect-via-ssl-test-cacerts", ".jks");
try (FileOutputStream fos = new FileOutputStream(keyStoreFile.getPath())) {
KeyStore ks = KeyStore.getInstance("JKS", "SUN");
ks.load(null);
ks.setCertificateEntry("rootCaCertificate", x509Cert);
ks.store(fos, "changeit".toCharArray());
}
System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getPath());
System.setProperty("javax.net.ssl.trustStoreType", "JKS");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
BasicAWSCredentials awsCredentials = new BasicAWSCredentials(AWS_ACCESS_KEY, AWS_SECRET_KEY);
RdsIamAuthTokenGenerator generator = RdsIamAuthTokenGenerator.builder()
.credentials(new AWSStaticCredentialsProvider(awsCredentials)).region("us-west-2").build();
String authToken = generator.getAuthToken(GetIamAuthTokenRequest.builder()
.hostname(RDS_INSTANCE_HOSTNAME).port(3306).userName(DB_USER).build());
Properties mysqlConnectionProperties = new Properties();
mysqlConnectionProperties.setProperty("verifyServerCertificate","true");
mysqlConnectionProperties.setProperty("useSSL", "true");
mysqlConnectionProperties.setProperty("user",DB_USER);
mysqlConnectionProperties.setProperty("password",authToken);
DriverManager.getConnection("jdbc:mysql://" + RDS_INSTANCE_HOSTNAME + ":3306", mysqlConnectionProperties);
The code fails on the call to DriverManager.getConnection(...)
with the following exception:
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:174)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64)
at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:824)
at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:444)
at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:237)
at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:198)
at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:681)
at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:190)
at IAMDatabaseAuthenticationTester.run(IAMDatabaseAuthenticationTester.java:92)
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:759)
... 5 common frames omitted
Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure
[...]
Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors
The same exception happens if I replace global-bundle.pem
with us-west-2-bundle.pem
or rds-ca-2019-us-west-2.pem
.
I looked up the "Path does not chain with any of the trust anchors" error and saw tons of questions on StackOverflow - most of them do not have answers. One of the few with answers says that you need both the root certificate as well as the intermediate certificate for your region. But according to the Amazon SSL document, the global-bundle.pem
and us-west-2-bundle.pem
files contain both the root certificates and the intermediate certificates for the AWS region(s). I also loaded the global-bundle.pem
and us-west-2-bundle.pem
files in KeyStore Explorer, and confirmed that they have both root CAs and non-root CAs, so I don't think that is the problem.
Any idea why the code is not working? It almost seems like Amazon did not test their code at all.