13

I am facing problems connecting with my DocumentDB cluster with plain Java over TLS/SSL

The procedure I followed as per the AWS docs is this:

I downloaded the .pem file from AWS and copied in the root of my java project, from where I executed:

keytool -importcert -trustcacerts -file rds-combined-ca-bundle.pem -alias certAlias -keystore rds-ca-certs -storepass keyStorePassword

This has created the rds-ca-certsat the root of my project which now looks like this:

enter image description here

And, the java code in Main.java is:

package com.example.documentdb;

import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.ServerAddress;
import com.mongodb.MongoException;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoCollection;
import org.bson.Document;


public final class Main {
    private Main() {
    }
    public static void main(String[] args) {

        String template = "mongodb://%s:%s@%s/test?ssl=true&replicaSet=rs0&readpreference=%s";
        String username = "myUsernameInCluster";
        String password = "myPasswordInCluster";
        String clusterEndpoint = "mycluster.node.us-east-1.docdb.amazonaws.com:27017";
        String readPreference = "secondaryPreferred";
        String connectionString = String.format(template, username, password, clusterEndpoint, readPreference);

        String keystore = "rds-ca-certs";
        String keystorePassword = "keyStorePassword";

        System.setProperty("javax.net.ssl.trustStore", keystore);
        System.setProperty("javax.net.ssl.trustStorePassword", keystorePassword);

        MongoClientURI clientURI = new MongoClientURI(connectionString);
        MongoClient mongoClient = new MongoClient(clientURI);

        MongoDatabase testDB = mongoClient.getDatabase("test");
        MongoCollection<Document> numbersCollection = testDB.getCollection("numbers");

        Document doc = new Document("name", "pi").append("value", 3.14159);
        numbersCollection.insertOne(doc);

        MongoCursor<Document> cursor = numbersCollection.find().iterator();
        try {
            while (cursor.hasNext()) {
                System.out.println(cursor.next().toJson());
            }
        } finally {
            cursor.close();
        }

    }
}

It gives me this ugly error:

com.mongodb.MongoSocketWriteException: Exception sending message

Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Interestingly, when I use the mongo CLI to connect over SSL/TLS, like this:

mongo --ssl --host mycluster.node.eu-central-1.docdb.amazonaws.com:27017 --sslCAFile rds-combined-ca-bundle.pem --username myUsernameInCluster --password myPasswordInCluster

It works perfectly, so I have discarded a networking issue and I think the problem is in the Java code itself or in the keytool command executed.

Additionally, with TLS disabled in the cluster this java code provided by AWS (that only differs in the keystore configuration) works.

PD: I know there are a bunch of other questions regarding SSL/TLS connection with AWS DocumentDB, but none of them address my issue specifically. Moreover, I have also checked plain mongodb TLS/SSL connection questions and the process differ so they are not valid for me.

Community
  • 1
  • 1
Arcones
  • 3,954
  • 4
  • 26
  • 46
  • I don't think you are actually loading the trust store .. try `-Djavax.net.ssl.trustStore=path/to/keystore.jks` Check out this question https://stackoverflow.com/questions/2138574/java-path-to-truststore-set-property-doesnt-work . https://stackoverflow.com/questions/48790981/load-java-trust-store-at-runtime-after-jvm-have-been-launched . https://stackoverflow.com/questions/5871279/java-ssl-and-cert-keystore/5871301 . You can also import the aws rds bundle into your java trust store – Yan Jan 15 '20 at 04:00
  • 1
    @Yan as the trust store is in the root of the project, i simply call it by its name. In any case, I have tested that with an absolute path also and the same problem happens. I am interested on what you said about importing the aws rds bundle into java trust store directly because that wouldn't required the keytool configuration and everything is done by code, right? can you share some details about this method? – Arcones Jan 15 '20 at 11:42

3 Answers3

13

The issue seems to be related to the process of importing the certificates within the bundle, under the same alias.

So I have had to stop using the bundled option (rds-combined-ca-bundle.pem) and start using this one rds-ca-2019-root.pem

After importing the keystore using the following command:

keytool -importcert -trustcacerts -file rds-ca-2019-root.pem -alias rds19 -keystore rds-ca-certs -storepass keyStorePassword

Connectivity with the database under TLS was achieved.

Arcones
  • 3,954
  • 4
  • 26
  • 46
  • 1
    That's great that you worked it out. That makes sense. Usually, you would import just the root ca certificate so this way the server would present the chain and you trust the root of it. – Yan Feb 12 '20 at 15:24
  • 1
    Thank you for this answer. I was having a hard time trying to connect my EMR Spark cluster to documentdb , and this solved it. – Fábio Junqueira Aug 31 '20 at 21:07
  • My pleasure @FábioJunqueira – Arcones Sep 01 '20 at 15:42
  • @Arcones Can you please share complete solution? steps, folder structure and code? I am facing same issue and not able to resolve – sidgate Jan 30 '21 at 05:25
  • @sidgate IIRC, I didn't change the folder structure from what it's reported in the question – Arcones Feb 01 '21 at 12:41
  • 1
    Thank you very much for this answer. I was having a hard time finding a fix. However I had confusions between these root certificate, intermediate certificate, chain of trust. If anyone has the same refer below link to grasp a basic idea on this. https://aboutssl.org/root-certificates-vs-intermediate-certificates/#:~:text=Root%20certificates%20are%20the%20Certificate,who%20offers%20an%20intermediate%20root. – Nilucshan Siva May 25 '21 at 18:29
  • Same issue for me, even with your solution, not able to get it working. I try to connect to my documentdb through ssh tunnel. Any help? – X6Entrepreneur Feb 12 '23 at 13:47
1

You can add the AWS provided certificate to the java's trust store then java will trust requests to AWS service by default.
You will have to find your java cacerts file. Depending on your OS and java version it should be in ..lib/security/cacerts.
I am using OSX and it was in
/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/security/cacerts

Then you cause keytool to import it the default java keystore password is change it:

keytool -import -trustcacerts -keystore /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/security/cacerts -storepass changeit -noprompt -alias aws-rds -file rds-combined-ca-bundle.pem

To check if the certificate was imported you can use:
keytool -list -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/security/cacerts | grep aws

Post back if that worked.

Yan
  • 3,533
  • 4
  • 24
  • 45
  • Unfortunately, this didn't solve the problem. I ended up opening a AWS support case, the problem was related with the process of importing the certificates within the bundle, under the same alias. I will post the procedure that helped me to solve it in a moment. Thanks for your help in any case – Arcones Feb 12 '20 at 09:41
0

https://github.com/hungbang/spring-boot-aws-docdb I have created a sample to connect to amazon docdb from spring boot with SSL enabled.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Hung Bang Quan
  • 151
  • 2
  • 9