sharptooth
sharptooth

Reputation: 170499

Why do I have to add the server SSL certificate if my client program is in Java and not when it is in C#?

If I use HttpsURLConnection in a Java program and try to open an URL starting with https:// I'll get an error message:

unable to find valid certification path to requested target

and the solution I found is to add the server certificate to the client certificate storage. But if I write a C# program that uses HttpWebRequest then I don't have to add anything anywhere.

So for me it looks like a C# client "just works" and a Java client only works after being tweaked with a hammer.

Why is an extra step required for a Java client? Can I somehow skip saving the certificate to the client storage of JVM?

Upvotes: 2

Views: 571

Answers (4)

jmehrens
jmehrens

Reputation: 11045

A good source of information on this topic is the Leveraging Security in the Native Platform Using Java SE 6 Technology and Java Secure Socket Extension (JSSE) Reference Guide on the Oracle site.

If you want Java to use the Windows certificate store to validate certificates then you can specify the the following system properties on launch:

-Djavax.net.ssl.keyStoreType=Windows-MY -Djavax.net.ssl.trustStoreType=Windows-ROOT

If you want only one connection to use the Windows certificate store to validate certificates you can modify the following code to fit your needs:

    KeyStore ks = KeyStore.getInstance("Windows-MY");
    ks.load(null, null);

    KeyStore ts = KeyStore.getInstance("Windows-ROOT");
    ts.load(null, null);

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

    KeyManagerFactory kmf = KeyManagerFactory
                .getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kmf.init(ks, new char[0]);

    SSLContext ctx = SSLContext.getInstance("TLS");
    ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);    

    URL url = new URL("https://some.web.site.org");
    javax.net.ssl.HttpsURLConnection urlConnection = 
            (javax.net.ssl.HttpsURLConnection) url.openConnection();
    urlConnection.setSSLSocketFactory(ctx.getSocketFactory());
    urlConnection.connect();
    try (InputStream in = urlConnection.getInputStream();) {
        byte[] chunk = new byte[1024];
        for (int len; (len = in.read(chunk)) > -1;) {
            System.out.write(chunk, 0, len);
        }
    } finally {
        urlConnection.disconnect();
    }

Upvotes: 1

Bruno
Bruno

Reputation: 122649

By default, Java uses its own set of trust anchors (in the default truststore, see the JSSE Reference Guide).

If you want to use the Windows certificate store, you can use the Windows-ROOT keystore as a trust store.

Upvotes: 1

AlexR
AlexR

Reputation: 115328

I believe it is because C# uses the same HTTP client as MSIE, so it has a lot of pre-installed SSL certificates including one that your use. JVM has less certificates pre-installed.

Upvotes: 2

Rup
Rup

Reputation: 34408

HttpWebRequest will use Window's own certificate store to validate certificates, i.e. the same as IE. If your IE can validate the certificate correctly, either by having the certificate or a CA path back to a trusted root, then HttpWebRequest should accept the certificate OK.

In the Java case I suspect adding the server certificate itself is wrong, unless it's self-signed in which case you'll have no choice. You should add the CA path back to a trusted root instead - you can probably pull these certificates out of Windows's CA store or download them from the root CA's website if you need them.

Upvotes: 2

Related Questions