You are utilizing Java 5 or Java 6 and you want to establish a network connection to a HTTPS server? This task can be tricky if the server side is using a certificate issued by an certificate authority (CA) that is unknown to your Java Virtual Machine (JVM).
This article shows you how to encourage your JVM to talk to server backend systems with custom SSL certificates.
Contents
Java supports to establish connections to servers by talking HTTP and HTTPS by defaut. However, the JVM has registered a set of CAs that are trustworthy. Connections the servers containing certificates signed from CAs that are unknown to your JVM are rejected by throwing a SSLHandshakeException and a SunCertPathBuilderException. You will get error messages like ‘PKIX path building failed‘ and ‘unable to find valid certification path to requested target‘.
Java is intended to be a secure environment for applications running in virtual machines. So not anyone is allowed to provide data to your applications by default. There are an number of opportunities to get around this problem. This article will show you an example to end up successfully with this task.
As usual there are a number of solutions to reach your aims. Here are a number of possible approaches.
From all these approaches the latter one is the solution that we will try to configure in this article!
This is a code example to open a HTTP(s) connection. Without any additional code or configurations this application will fail, when connection to an unsecure server.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061 |
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.net.MalformedURLException;import java.net.URL; import javax.net.ssl.HttpsURLConnection;import javax.net.ssl.SSLSocketFactory; /** Connect to a HTTP or HTTPS server (runs with Java 5 and Java 6). * @author marioscondo * * Copyright 2010, Linux-Support.com */public class HttpsTest { /** * @param args provide an optional url */ public static void main(String[] args) { // main method - this is the entry point for running this application HttpsTest app = new HttpsTest(); try { // fetch a url from command line or use a default one String url = "https://www.linux-support.com:443/"; if (args.length > 0) { url = args[0]; } System.out.println("connecting to: " + url); // fetch remote data app.doit(url); } catch (Exception e) { e.printStackTrace(); } } // connect to the server void doit(String url_str) throws Exception { SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); URL url = new URL(url_str); 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; System.out.println("connection was successful."); System.out.println("received data:"); while ((string = bufferedreader.readLine()) != null) { System.out.println("Received " + string); } }} |
This is a very simple connection tester. If available, it takes the first command line argument and tries to connect to this url. If no parameter is available a default server will be utilized (www.linux-support.com via HTTPS). The method doit(…) is opening the network connection and is printing out all the data that has been received. If an error occures (like there is an untrusted server at the other end of the network connection) an exception will be thrown.
Now we want to create a key store to be provided to your JVM. It may contain any number of certificates. In this example we will add the certificate of the CA, that did sign the certificate of our target HTTPS server. This will result in successful network connections for all requests to servers this CA did sign. So we do not need to import all the certificates of each and every HTTPS service we want to talk to.
# create a key store and import the certificate of a CA$ keytool -import -storepass passwd -keystore mykeystore -alias lsca -file cacert.crt
This single line is performing all the following steps at once:
If you are asking yourself, where to get the CA certificate from, we have got a reassuring answer to you. Each CA is publishing its certificate at its public website. If you are interested in connecting to the preconfigured default Url (see above) you may download the CA certificate at the following location: http://www.linux-support.com/cert/cacert-premium.crt
These are the parameters recognized by the JVM to start with an additional key store and/or trust store.
-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
Don’t worry. For our example we just need two parameters from that list.
Now we are ready to start the application. After compiling the sample application and creating the key store you might key-in the following command line to connect to the preconfigured default url. In the next example we assume that your application is packaged within the file test-https.jar.
# run the application$ java -Djavax.net.ssl.keyStorePassword=passwd -Djavax.net.ssl.trustStore=mykeystore -jar test-https.jar # run the application from an intranet by utilizing # a proxy server$ java -Djavax.net.ssl.keyStorePassword=passwd -Djavax.net.ssl.trustStore=mykeystore -Dhttp.proxyHost=wwwproxy.yournet.corp -Dhttp.proxyPort=8080 -Dhttps.proxyHost=wwwproxy.yournet.corp -Dhttps.proxyPort=8080 -jar test-https.jar
Have a lot of fun with your new extended SSL client!
Related articles: