tags:

views:

10949

answers:

8

I am using Java 6 and am trying to create an HttpsURLConnection against a remote server, using a client certificate.
The server is using an selfsigned root certificate, and requires that a password-protected client certificate is presented. I've added the server root certificate and the client certificate to a default java keystore which I found in /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts (OSX 10.5). The name of the keystore file seems to suggest that the client certificate is not supposed to go in there?

Anyway, adding the root certificate to this store solved the infamous javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed' problem.
However, I'm now stuck on how to use the client certificate. I've tried two approaches and neither gets me anywhere.
First, and preferred, try:

    SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
 URL url = new URL("https://somehost.dk:3049");
 HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
 conn.setSSLSocketFactory(sslsocketfactory);
 InputStream inputstream = conn.getInputStream();
    // The last line fails, and gives:
    // javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

I've tried skipping the HttpsURLConnection class (not ideal since I want to talk HTTP with the server), and do this instead:

    SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
    SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("somehost.dk", 3049);
    InputStream inputstream = sslsocket.getInputStream();
    // do anything with the inputstream results in:
    // java.net.SocketTimeoutException: Read timed out

I am not even sure that the client certificate is the problem here?

+3  A: 

I use the Apache commons HTTP Client package to do this in my current project and it works fine with SSL and a self-signed cert (after installing it into cacerts like you mentioned). Please take a look at it here:

http://hc.apache.org/httpclient-3.x/tutorial.html

http://hc.apache.org/httpclient-3.x/sslguide.html

Taylor Leese
This seems like a pretty neat package, but the class that should make it all work 'AuthSSLProtocolSocketFactory' is apparantly not part of the official distribution, neither in 4.0beta (despite the release notes stating that it is), or in 3.1. I've hacked around with it a bit and now seem to be permanently stuck with a 5 minute hang before it just drops the connection. It's really odd - if I load the CA and the client cert into any browser, it just flies.
Jan
Apache HTTP Client 4 can take an `SSLContext` directly, so you can configure all this this way, instead of using `AuthSSLProtocolSocketFactory`.
Bruno
+3  A: 

Have you set the KeyStore and/or TrustStore System properties?

java -Djavax.net.ssl.keyStore=pathToKeystore -Djavax.net.ssl.keyStorePassword=123456

or from with the code

System.setProperty("javax.net.ssl.keyStore", pathToKeyStore);

Same with javax.net.ssl.trustStore

Gandalf
A: 

I think you have an issue with your server certificate, is not a valid certificate (I think this is what "handshake_failure" means in this case):

Import your server certificate into your trustcacerts keystore on client's JRE. This is easily done with keytool:

keytool
    -import
    -alias <provide_an_alias>
    -file <certificate_file>
    -keystore <your_path_to_jre>/lib/security/cacerts
SourceRebels
I tried cleaning up and starting over, and the handshake failure went away. Now I just get 5 minutes of dead silence before the connection is terminated :o
Jan
+4  A: 

While not recommended, you can also disable SSL cert validation alltogether:

public static void disableCertificateValidation() {
    // Create a trust manager that does not validate certificate chains
    TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public void checkClientTrusted(X509Certificate[] certs, String authType) {
            return;
        }

        public void checkServerTrusted(X509Certificate[] certs, String authType) {
            return;
        }
    }};

    // Install the all-trusting trust manager
    try {
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    } catch (Exception e) {
        return;
    }
}
neu242
This was precisely what I was looking for, and it works like a charm.
vy32
+7  A: 

Finally solved it ;). Got a strong hint here (Gandalfs answer touched a bit on it as well). The missing links was (mostly) the first of the parameters below, and to some extent that I overlooked the difference between keystores and truststores.

The self-signed server certificate must be imported into a truststore:

keytool -import -alias gridserver -file gridserver.crt -storepass $PASS -keystore gridserver.keystore

These properties need to be set (either on the commandline, or in code):

-Djavax.net.ssl.keyStoreType=pkcs12
-Djavax.net.ssl.trustStoreType=jks
-Djavax.net.ssl.keyStore=clientcertificate.p12
-Djavax.net.ssl.trustStore=gridserver.keystore
-Djavax.net.debug=ssl # very verbose debug
-Djavax.net.ssl.keyStorePassword=$PASS
-Djavax.net.ssl.trustStorePassword=$PASS

Working example code:

    SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
 URL url = new URL("https://gridserver:3049/cgi-bin/ls.py");
 HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
 conn.setSSLSocketFactory(sslsocketfactory);
 InputStream inputstream = conn.getInputStream();
    InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
    BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

    String string = null;
    while ((string = bufferedreader.readLine()) != null) {
        System.out.println("Received " + string);
    }
Jan
A: 

Hi, I have a question on the same topic. I am observing that some sites configured using HTTPS are providing the response back without requiring to import the certificate into the keystore. Does this depend on how the HTTPS is configured on the server?

Thanks

Without knowing the specifics of your situation, some servers are configured to letting client certificates be optional, while others actually require it. Jetty for example can either "needClientAuth" or "wantClientAuth".
Jan
Thanks. In our case we are using HTTPSURLConnection to make a connection to HTTPS sites. The application making HTTPSURLConnection is running on a WebLogic server. Some common sistes likes gmail on https, verisign on https are working fine and response is coming back. But when tried with some of the intranet sites, we were getting SSLHandShakeException. Then we took the server certificate from the browser, and imported into WebLogic's keystore. Then, it started working fine. Wondering if there is any setting on the HTTPS configuration to let client trust server certificate?
A poster above noted how to bypass validation altogether, which might solve your problem. The sites that you connect to, have you checked which root CA that signed their certs, and if that root CA is trusted by Java?
Jan
A: 

Hi, If we are using HTTPSURLConnection to connect to HTTPS sites, is it required to import server certificate to trust the server? Would we get SSLHandshakeException if we do not import the server's certificate. It looks like we are getting the exception for few HTTPS sites and not getting the exception for few other sites. Wondering if this is based on some configuration of HTTPS or based on certificate type?

Thanks

A: 

If you are dealing with a web service call using the Axis framework, there is a much simpler answer. If all want is for your client to be able to call the SSL web service and ignore SSL certificate errors, just put this statement before you invoke any web services:

System.setProperty("axis.socketSecureFactory", "org.apache.axis.components.net.SunFakeTrustSocketFactory");

The usual disclaimers about this being a Very Bad Thing to do in a production environment apply.

I found this at the Axis wiki.

Mark