Easier Java connections to MongoDB at Compose

Published

Having trouble getting Java to connect to Compose's MongoDB with SSL enabled? Try this one library trick to make everything work from the environment.

Java has been one of the trickier languages when it comes to connecting with MongoDB. Unencrypted connections are no problem, but as soon as we want to use Compose's self-signed certificates to enable verification of the server, there's a problem. Java's SSL/TLS support is a maze of twisty trust stores and confusion and you end up having to either create some store files and use them or add certificates, or override to the system trust stores. It's not a pretty sight.

So when I found Joe Kutner's "Creating Java Truststores and KeyStores from Environment Variables" it seemed to be just the thing to make life easier for all. As described in the article, the library used env-keystore has moved on in various ways, but here I'm going to talk about how it applies specifically to connecting to Compose.

Before building

The idea with the library is that you can set an environment variable to the certificate, or certificates, that you want to encapsulate in a Java crypto consumable KeyStore or TrustStore. With one of these created you can create a socket factory that'll do the connection work and validate things using the certificates in the store. So... the first thing we need is an environment variable with a certificate in it. I'm just going to make one like so:

export TRUSTED_CERT="-----BEGIN CERTIFICATE-----                        ⏎  
MIIDZzCCAk+gAwIBAgIEWH6noDANBgkqhkiG9w0BAQ0FADA1MTMwMQYDVQQDDCpj  
b21wb3N1cmUtMDlhNjgzZThlMjIwOGNhOGYzZTg4Njc1NmMyYTUxZDMwHhcNMTcw  
MTE3MjMyNDE2WhcNMzcwMTE3MjMwMDAwWjA1MTMwMQYDVQQDDCpjb21wb3N1cmUt  
...
77+NBWQRKebBuvIKGj+/nwpu7q3BuwJO08AYSnJt5unUGSo27FcCn6GMMXzb6zuT  
P9lmdxoukBpXLvg79FHw/pQL2zOchl4EjEC6PdMHo4MkR2i076SfcguM45v9pFO3  
zJpYwXp1BQEnBNU=  
-----END CERTIFICATE-----"
$

Now, let's start up our Java development environments, make a project and get coding:

Adding the library

The first step is adding the Maven dependencies to bring in the libraries we need. Add this to your pom.xml file.

        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongodb-driver</artifactId>
            <version>3.4.2</version>
        </dependency>
        <dependency>
            <groupId>com.github.jkutner</groupId>
            <artifactId>env-keystore</artifactId>
            <version>0.1.2</version>
        </dependency>

Other routes to installing the code env-keystore and MongoDB libraries from the Github repository are, of course, available but this is quick enough.

Stepping through the code

As with all Java code, there's the import preamble which I'll skip - you can find the code in our examples repositiory for a longer read. We'll start just inside the static main method:

            KeyStore ts = EnvKeyStore.createWithRandomPassword("TRUSTED_CERT").keyStore();

This is where the magic happens. It calls on EnvKeyStore to create a keystore using the environment variable TRUSTED_CERT. There are a few instances of creating EnvKeyStore which allow you to handle certificates with their own keys and passwords, but we just need it to associate a random password with our new store. Now comes the dance of factories. This is where we make a TrustManagerFactory and then initialise it with our new EnvKeyStore.

            String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
            tmf.init(ts);

With the TrustManagerFactory initialized, the next step is to create an SSLContext... don't worry, it's really TLS and the name is just a hangover from the old days. We get a "TLSv1.2" instance of an SSLContext and initialise it with the TrustManagers from our TrustManagerFactory:

            SSLContext sc = SSLContext.getInstance("TLSv1.2");
            sc.init(null, tmf.getTrustManagers(), new SecureRandom());

So far, so generic Java TLS/SSL handling. Now comes the MongoDB specific bit. We're assuming at this point that you are using full connection strings, like the Compose UI gives you. We need to use that connection string, but there's one thing we can't express in the connection string. That's which SSLContext to use as we just made our own. We can pass that in using MongoClientOptions.

            MongoClientOptions.Builder mco=MongoClientOptions.builder().socketFactory(sc.getSocketFactory());

This creates a Builder instance and sets the SocketFactory for creating MongoDB connections to the SocketFactory created by our SSLContext. We can pass that into the new MongoClientURI constructor along with the full connection string like so:

            MongoClientURI connectionString = new MongoClientURI("mongodb://<user>:<pass>@host1-portal.0.dblayer.com:10001,host2-portal.2.dblayer.com:10002/javatester?ssl=true",mco);

This MongoClientURI is now our special version loaded with SSLContext. From here on out, it's standard MongoDB Java Client all the way...

            MongoClient mongoClient = new MongoClient(connectionString);
            MongoDatabase mongoDatabase = mongoClient.getDatabase("javatester");
            MongoCollection mongoCollection = mongoDatabase.getCollection("javatester");
            ArrayList<Document> arrayList = new ArrayList<>();
            for (int i = 0; i < 100; ++i) {
                arrayList.add(new Document("i", (Object) i));
            }
            mongoCollection.insertMany(arrayList);
            System.out.println(mongoCollection.count());

What's not shown is the try...catch surrounding that code.

    try {
    ...
    } catch (KeyManagementException | IOException | KeyStoreException | NoSuchAlgorithmException | CertificateException ex) {
            Logger.getLogger(ComposeToMongoDB.class.getName()).log(Level.SEVERE, null, ex);
        }

There's a host of exceptions which can be thrown, mostly from the key management code. I'll leave your it to you to convert it to your preferred idiom of exception handling.


If you have any feedback about this or any other Compose article, drop the Compose Articles team a line at articles@compose.com. We're happy to hear from you.

attribution Crew

Dj Walker-Morgan
Dj Walker-Morgan is Compose's resident Content Curator, and has been both a developer and writer since Apples came in II flavors and Commodores had Pets. Love this article? Head over to Dj Walker-Morgan’s author page and keep reading.