5

I have a MongoDb instance running(single instance) with SSL enabled. I am able to connect to it with RoboMongo where on SSL tab I provide the following :

CA File : /path to my certificate/testCA.pem 
PEM certificate/key: /path to my key/testKey.pem

Which successfully connects. Now I'm trying to connect to the same mondodb from java app. I imported the testCA.pem into cacerts using the following command:

keytool -import -keystore cacerts -file testCA.pem -storepass changeit

and I can see a new entry added to the store. Tried to add the other key into it and it says invalid certificate. On the Java app I set system property as following:

System.setProperty ("javax.net.ssl.trustStore","C:\\Program Files\\Java\\jre1.8.0_91\\lib\\security\\cacerts");
System.setProperty ("javax.net.ssl.trustStorePassword","changeit");

and I'm getting the following error:

org.springframework.dao.DataAccessResourceFailureException: Timed out after 10000 ms while waiting to connect. Client view of cluster state is {type=Unknown, servers=[{address=test.mongo.com:27017, type=Unknown, state=Connecting, exception={com.mongodb.MongoException$Network: Exception opening the socket}, caused by {java.io.EOFException}}]; nested exception is com.mongodb.MongoTimeoutException: Timed out after 10000 ms while waiting to connect. Client view of cluster state is {type=Unknown, servers=[{address=test.mongo.com:27017, type=Unknown, state=Connecting, exception={com.mongodb.MongoException$Network: Exception opening the socket}, caused by {java.io.EOFException}}]
    at org.springframework.data.mongodb.core.MongoExceptionTranslator.translateExceptionIfPossible(MongoExceptionTranslator.java:75)
    at org.springframework.data.mongodb.core.MongoTemplate.potentiallyConvertRuntimeException(MongoTemplate.java:2075)
    at org.springframework.data.mongodb.core.MongoTemplate.executeFindMultiInternal(MongoTemplate.java:1918)

What am I missing here, thanks in advance!

8 Answers 8

6

In addition to importing the CAFile.pem with the command:

(navigate to your java_home/jre/lib/security to run the commands)

1. keytool -import -trustcacerts -file testCA.pem -keystore cacerts -storepass "changeit"

I also had to export the key.pem into a pkcs12 format(default password 'changeit')

2. openssl pkcs12 -export -out mongodb.pkcs12 -in testKey.pem

and in addition to setting system property trustStore/password, keyStore/password should also be set:

System.setProperty ("javax.net.ssl.trustStore",JAVA_HOME + "\\lib\\security\\cacerts");
System.setProperty ("javax.net.ssl.trustStorePassword","changeit");
System.setProperty ("javax.net.ssl.keyStore",JAVA_HOME + "\\lib\\security\\mongodb.pkcs12");
System.setProperty ("javax.net.ssl.keyStorePassword","changeit");
Sign up to request clarification or add additional context in comments.

8 Comments

I have exact same issue, but it is still not working for me, I am running windows so created xyz.pkcs12 from 'testKey.pem' in linux and copied it over to windows and imported the same in keystore, is there any thing else I should do ?
@Amit can you post your stacktrace?
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present at sun.security.ssl.Alerts.getSSLException(Unknown Source) at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source) at sun.security.ssl.Handshaker.fatalSE(Unknown Source) at sun.security.ssl.Handshaker.fatalSE(Unknown Source) at sun.security.ssl.ClientHandshaker.serverCertificate(Unknown Source) at sun.security.ssl.ClientHandshaker.processMessage(Unknown Source) at sun.security.ssl.Handshaker.processLoop(Unknown Source)
Caused by: java.security.cert.CertificateException: No subject alternative names present at sun.security.util.HostnameChecker.matchIP(Unknown Source) at sun.security.util.HostnameChecker.match(Unknown Source) at sun.security.ssl.X509TrustManagerImpl.checkIdentity(Unknown Source) at sun.security.ssl.X509TrustManagerImpl.checkIdentity(Unknown Source)
.sslInvalidHostNameAllowed(true).build(); above code allowed me to connect and get passed this issue atleast fr now.
|
5

Both the approaches mentioned below usually suggested on forums will 'work' but are not secure as they disable the host-name verification essentially negating the SSL. Hence they are not recommended especially if your code would be deployed on production:

// 1. For any HTTPS connection
    javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
        new javax.net.ssl.HostnameVerifier(){
            public boolean verify(String hostname,
                javax.net.ssl.SSLSession sslSession) {
                    if(hostname.equals("<hostname>")) {
                        return true; 
                    }
                }
            });

// 2. MongoDB SSL specific 
MongoClientOptions.builder().sslEnabled(true).sslInvalidHostNameAllowed(true).build();

Refer: https://wiki.openssl.org/index.php/Hostname_validation


To fix this, you'll need a certificate containing the DNS of the server as a Subject Alternative Name entry, which you can import to your JDK cacerts

Alternatively, if you want establish SSL at the application level, I'd recommend creating a SSLContext for that particular connection instead of using System.setProperty() for setting key-stores/trust-stores. This would help in avoiding conflicts if your application connects to different external services who have different SSL implementations.


Specifically for MongoDB, you would just need to append ?ssl=true at the end of the MongoDBURI after the above mentioned step. If it still doesn't work, I'd recommend updating your JDK version as per https://jira.mongodb.org/browse/JAVA-2184

Hope this helps

Comments

0

You need to configure the monog db driver to use SSL. You can do this with configuring it manually in a @Configuration class.

    public @Bean MongoClient mongo()  {
        MongoClientOptions.Builder options = MongoClientOptions.builder().sslEnabled(true);
        // add more options to the builder with your config
        MongoClient mongoClient = new MongoClient("localhost", options.build());
        return mongoClient;
    }

5 Comments

I enabled SSL via URI: test.mongo.com:27017/TestDb?ssl=true
afiak Mongo database URI. Cannot be set with host, port and credentials. It uses internal ServerAddress class that accepts only host and port.
ah yes, I was looking at the MongoClient that takes a string. Not the MongoClientURI
the general format for URI is mongodb://username:[email protected]:portNum/DatabaseName?W=1&ssl=true. I just need to provide a way to look for the certificates in the keystore.
0

If you are using RAD with WAS local servers, you have to add the pem file to the java VM for that server.

So if you have WAS installed to X:\IBM\WASx, X:\IBM\WASx\java_17\jre is the directory that you would navigate and then execute the keytool import there. Hope this helps others.

Comments

0

Please check below code for MongoDB Java Driver 4.x and MongoDB 3.x

Where you have to configure spring.data.mongodb.xxxx properties as per your MongoDB.

import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;

@Configuration
public class MongoClientConfig {

    @Value("${spring.data.mongodb.username}")
    private String user;
    
    @Value("${spring.data.mongodb.password}")
    private char[] password;
    
    @Value("${spring.data.mongodb.host}")
    private String host;
    
    @Value("${spring.data.mongodb.port}")
    private int port;
    
    @Value("${spring.data.mongodb.database}")
    private String database;
    
    @Bean
    public MongoClient mongo() {
        List<ServerAddress> hosts = Arrays.asList(new ServerAddress(host, port));
        MongoCredential credential = MongoCredential.createCredential(user, database, password);

        MongoClientSettings settings = MongoClientSettings.builder().credential(credential)
                .applyToSslSettings(builder -> builder.enabled(true))
                .applyToClusterSettings(builder -> builder.hosts(hosts)).build();
        MongoClient mongoClient = MongoClients.create(settings);
        System.out.println("====================================================================");
        System.out.println("========= MongoClientConfig: " + mongoClient.toString() + "=========");
        System.out.println("====================================================================");
        return mongoClient;
    }
}

Comments

0
  1. I download a openssl program from here and install it
  2. i open a cmd in the foler of my Pem file certiface.pem
    and i run a command :

DIRECTION\OpenSSL\bin\openssl pkcs12 -export -out keystore.pkcs12 -in certiface.pem

on password you put what you want example: blabla should create a file name keystore.pkcs12

  1. run the command on cmd

keytool -importkeystore -srckeystore keystore.pkcs12 -srcstoretype PKCS12 -destkeystore keystore.jks -deststoretype JKS

on passwords i put blabla check that created file keystore.jks

  1. now my code

          System.setProperty("javax.net.ssl.keyStore", keystore.jks);
          System.setProperty("javax.net.ssl.keyStorePassword", "blabla");
          MongoClient mongoClient = MongoClients.create("mongodb+srv://YOURLINK.mongodb.net/?authSource=%24external&authMechanism=MONGODB-X509&retryWrites=true&w=majority");
          database = mongoClient.getDatabase("DatabaseName");
    

Comments

0

Here is a complete implementation of how to connect to MongoDB/DocDB over ssl using mongoUri and sslCertificates. In this implementation, the application itself creates the keystore file and imports the certificate into it. So, there is no need to do any server side changes (in most cases). https://github.com/BarathArivazhagan/spring-boot-aws-documentdb

Comments

-1

As Redlab mentioned in the code, it can be disabled for restricting ssl validation. Specially this case comes on local setup. No extra effort required to do.

public @Bean MongoClient mongo()  {
    MongoClientOptions.Builder options = MongoClientOptions.builder().sslEnabled(false);
    // add more options to the builder with your config
    MongoClient mongoClient = new MongoClient("localhost", options.build());
    return mongoClient;
}

just I modified-- sslEnabled(true) ===> sslEnabled(false).

1 Comment

I was facing same problem, i resolved it by above way.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.