3

Problem: My demo code client unable to connect to the Azure Cosmos emulator on Windows 10.

Steps:

  1. I installed the Cosmosdb emulator on Windows - looks fine

  2. As per documentation, I started the Windows cert manage.msc. I selected private cert with friendly name "DocumentDBEmulatorCertificate" as base64 encoded x.509.cer file to local disk

  3. I started the cmd console in Windows as administrator and cd to local JAVA_HOME/lib/security directory (I'm using Java 8.0.131)

  4. I ran keytool with this

    keytool -import -trustcacerts -keystore cacerts -storepass changeit -noprompt -alias azureCosmossDBEmulator -file "D:\exported certificates\cosmossDB-emulator-cert.cer"

  5. I listed out the revised keystore to dump.txt file. I can see my entry in the dump

    azurecosmossdbemulator, 30-Aug-2017, trustedCertEntry, Certificate fingerprint (SHA1): 5B:F4:14:BE:9F:2B:7F:6A:2B:C0:87:A4:3E:4D:9A:52:45:FA:2F:EA

    and this matches the thumbprint value in the x.509 cert.

  6. I restarted Intellij on my build, and checked that Java 8.0.1.3.1 was the only jdk in the project.

  7. I fired up Groovy test script in debug and stepped through code. I can create DocumentClient ok.

  8. This is just a rough script to test connection code looks like this

     final String key = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
    
     DocumentClient client = new DocumentClient("https://localhost:8081", key
                 , new ConnectionPolicy(), ConsistencyLevel.Session)
    
     String dbname = "familyDB"
         String dblink = "/dbs/$dbname"
    
     //create db if not exists
     try {
         client.readDatabase(dblink,null)
         println "found db $dbname"
     } catch (DocumentClientException de) {
         if (de.getStatusCode() == 404) {
             Database db = new Database()
             db.id = dbname
             client.createDatabase(db, null)
             println "created new DB $dbname"
     } else {
         throw de
     }
     }
    

When I get to the client.readDatabase line I get an exception like this:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Caught: java.lang.IllegalStateException: Http client execution failed.
java.lang.IllegalStateException: Http client execution failed.
    at com.microsoft.azure.documentdb.internal.GatewayProxy.performGetRequest(GatewayProxy.java:234)
    at com.microsoft.azure.documentdb.internal.GatewayProxy.doRead(GatewayProxy.java:89)
    at com.microsoft.azure.documentdb.internal.GatewayProxy.processMessage(GatewayProxy.java:336)
    at com.microsoft.azure.documentdb.DocumentClient$8.apply(DocumentClient.java:2985)
    at com.microsoft.azure.documentdb.internal.RetryUtility.executeDocumentClientRequest(RetryUtility.java:58)
    at com.microsoft.azure.documentdb.DocumentClient.doRead(DocumentClient.java:2991)
    at com.microsoft.azure.documentdb.DocumentClient.readDatabase(DocumentClient.java:491)
    at com.microsoft.azure.documentdb.DocumentClient$readDatabase.call(Unknown Source)
    at com.softwood.azure.client.cosmossDBClientScript.run(cosmossDBClientScript.groovy:29)
Caused by: 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
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:394)
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:353)
    at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:141)
    at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:353)
    at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:380)
    at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
    at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:107)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:55)
    at com.microsoft.azure.documentdb.internal.GatewayProxy.performGetRequest(GatewayProxy.java:231)
    ... 8 more
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    ... 20 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    ... 20 more

which basically states it can't find my cacert entry and refuses to connect.

What's gone wrong? (I haven't restarted Windows yet). The cert looks ok, the import seemed to work via keytool into cacerts, I'm using single jdk ref, but code won't connect.

How can I unravel what I have not done correctly, and must now do to have the code connect from Java into Azure DB emulator running locally in my Windows 10 machine?

halfer
  • 19,824
  • 17
  • 99
  • 186
WILLIAM WOODMAN
  • 1,185
  • 5
  • 19
  • 36
  • I ran into a similar problem like this a few weeks ago, but I was dealing with an LDAP servers certificate (that was given to me). I've never worked with the Azure stuff your dealing with but it sounds like your running some sort of local server on your machine right? If yes, have a look at this page. https://github.com/escline/InstallCert/blob/master/InstallCert.java. Copy / paste the java code and run it, passing "host:port passphrase". it should generate a certificate for you (i believe in the same location as the java class you run).copy/paste the file into the jre location you mentioned. – Jason Aug 31 '17 at 02:17
  • I solidly answered your question. See my reply below. – djangofan Mar 25 '22 at 16:20

2 Answers2

10

According to your description, I also wrote a snippet of code using Document DB JavaSDK to connect to the Cosmos DB emulator, and as a result, I ran into the same issue as you.

package emulator;

import com.microsoft.azure.documentdb.ConnectionPolicy;
import com.microsoft.azure.documentdb.ConsistencyLevel;
import com.microsoft.azure.documentdb.Database;
import com.microsoft.azure.documentdb.DocumentClient;
import com.microsoft.azure.documentdb.DocumentClientException;

public class TestEmlulator {

    // Replace with your DocumentDB end point and master key.
    private static final String END_POINT = "https://localhost:8081/";
    private static final String MASTER_KEY = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";

    public static void main(String[] args) throws DocumentClientException {
        // Connect to the Azure Cosmos DB Emulator running locally
        DocumentClient client = new DocumentClient(END_POINT, MASTER_KEY, ConnectionPolicy.GetDefault(),
                ConsistencyLevel.Session);

        Database database = new Database();
        database.setId("testEmulator");
        database = client.createDatabase(database, null).getResource();

        System.out.println(database.toJson());
    }

}

So I exported the certificate of cosmosDB, and named it as documentdbemulatorcert.cer and stored it in my D disk, as provided by the official tutorial. It seems that you did the same thing.

enter image description here

Then,I tried to import the certificate of cosmosdb into the JVM trust certificate list. You could also refer to the steps I did below:

Step1: Open the CMD command window and enter the %JAVA_HOME%/jre/lib/security directory. Note that it is necessary to open the command window with administrator privileges

For me, the directory looks like C:\Program Files\Java\jdk1.8.0_131\jre\lib\security

Step2 : In the open command window, enter the following command:

keytool -import -alias cacerts -keystore cacerts -file d:\documentdbemulatorcert.cer

enter the default password: changeit and enter Y or y against the Trust this certificate? [no]:

Step 3: You could see Certificate was added to keystore if the certificate has been successfully imported.

You can refer to the screenshot below for the whole process:

enter image description here

Finally, my code works!

enter image description here

You can also refer to the SO threads below:

1.Unable to find valid certification path to requested target - error even after cert imported

2."PKIX path building failed" and "unable to find valid certification path to requested target"

Hope it helps you.

Gandhi
  • 11,875
  • 4
  • 39
  • 63
Jay Gong
  • 23,163
  • 2
  • 27
  • 32
  • 1
    Jay, thanks for that. I looked at the starting folder and i think i was in the wrong place. I had imported it into "C:\Program Files\Java\jre1.8.0_131\lib\security" - the JRE copy. when i read your again you had imported in into %java_home%/jre/security. Not sure how i ballsed that up - too tired i think. I followed what you did got into the right JDK directory this time and ran it again. Bingo we are on. Thanks for that again else id have been tearing my hair out for another day in java config directories. Much appreciated – WILLIAM WOODMAN Sep 01 '17 at 16:35
  • @WILLIAMWOODMAN I'm glad to be able to help you, if you don't mind ,you could mark my answer for reference with someone have the same issue. – Jay Gong Sep 01 '17 at 17:30
  • Jay - upped the answer my question number - thanks again – WILLIAM WOODMAN Sep 04 '17 at 08:51
  • Faced a same issues, followed the steps in the answer. Worked. Thanks. – Praveen Kumar K S Oct 23 '19 at 04:03
  • @PraveenKumarKS Cheers! you're so kind. – Jay Gong Oct 23 '19 at 04:45
  • @JayGong Hi Jay, i could make it working following the steps mentioned above. But it stopped working from today. Nothing changed in cacerts. Any pointers? – Gandhi Jan 21 '20 at 12:30
  • @Gandhi - yeah, see my answer below. you probably hosed yourself using that `-cacerts` keytool option. – djangofan Mar 25 '22 at 16:26
0

I am on a Mac, but the scripts below can be adjusted to work on Windows 10.

DO NOT use the keytool -cacerts option. You will hose yourself. If you do that, and you are not careful, you could accidentally import the cert to the wrong JDK on your system.

I define CosmosDB emulator in a docker-compose.yml . Then I start it like so:

#!/usr/bin/env bash
## starts Mongo locally and imports certificates into your JDK

export EXTERNAL_IP=$(ifconfig | grep "inet " | grep -Fv 127.0.0.1 | awk '{print $2}' | head -n 1)
exec docker-compose up -d

sleep 20s

curl -k https://localhost:8081/_explorer/emulator.pem > emulatorcert.crt

if [ ! -f $JAVA_HOME/lib/security/jssecacerts ]
then
    sudo cp $JAVA_HOME/lib/security/cacerts $JAVA_HOME/lib/security/jssecacerts
else
    echo "jssecacerts file exists"
fi

sudo keytool -delete -alias "cosmosdb" -keystore $JAVA_HOME/lib/security/jssecacerts -storepass changeit -noprompt
sudo keytool -importcert -file ./emulatorcert.crt -keystore $JAVA_HOME/lib/security/jssecacerts -alias "cosmosdb" --storepass changeit -noprompt
sudo keytool -list -keystore $JAVA_HOME/lib/security/jssecacerts -alias "cosmosdb" --storepass changeit

And here is my docker-compose.yml :

version: '2.4'

services:

  cosmosdb:
    container_name: cosmosdb
    hostname: cosmosdb
    image: mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator
    platform: linux
    tty: true
    restart: always
    mem_limit: 3GB
    ports:
        - '8081:8081'
        - '8900:8900'
        - '8901:8901'
        - '8902:8902'
        - '10250:10250'
        - '10251:10251'
        - '10252:10252'
        - '10253:10253'
        - '10254:10254'
        - '10255:10255'
        - '10256:10256'
        - '10350:10350'
    environment:
      AZURE_COSMOS_EMULATOR_PARTITION_COUNT: 2
      AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE: true
      AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE: ${EXTERNAL_IP}
    volumes:
      -  vol_cosmos:/Volumes/docked/cosmosdb  # volume must allow write permissions

volumes:
  vol_cosmos:

Finally, import the cert into your Keychain Access app (a.k.a. system cert store), and then mark the cert as trusted.

Then, finally you can manage your CosmosDB by visiting https://localhost:8081/_explorer/index.html .

djangofan
  • 28,471
  • 61
  • 196
  • 289