Trusting any “secure” host

My main concern is developing server-side JAVA-applications: J[2]EE applications.

When working with hobby-projects, I’m often faced with situations where I need to call other “third-party” applications over HTTP, in order to obtain some information important to me in specific situations.

It is not uncommon for local/public organizations to publish sensitive information over HTTPS where the SSL-certificates are not signed by some known issuer.

Also when working with SSL in test-setups, one usually don’t spend time and money on “real” certificates, but uses self-signed certificates.

This is generally not a problem as I as a user can select to accept “invalid” certificates, when contacting a host that I trust.
Contacting hosts with invalid certificates using a browser is not a problem: one just accepts the invalid certificate or import the unknown issuer into the trust-store.

When contacting those hosts using JAVA, one is faced with a SSLHandshakeException:

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 com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1611)
	at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:187)
	at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:181)
	at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1035)
	at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:124)
	at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:516)
	at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:454)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:884)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1112)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1139)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1123)
	at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:434)
	at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
	at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:133)


Searching the net for a solution, the advice one is given is to simply import the invalid certificate into the trust-store. This is of course a great idea when developing applications where security is a main concern.

But if the application just needs some “not very sensitive” information, hacking around with trust-stores and certificates can be cumbersome, especially if the certificates change…

In those situations, it would be nice with a mechanism to just accept whatever certificate one is presented with.

The simple TrustAllTrustManager presented here helps doing just that – it “trusts” any ssl certificate:

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
 
import javax.net.ssl.X509TrustManager;
 
public class TrustAllTrustManager implements X509TrustManager {
	public TrustAllTrustManager() {
	}
 
	public X509Certificate[] getAcceptedIssuers() {
		throw new UnsupportedOperationException();
	}
 
	public void checkClientTrusted(X509Certificate[] chain, String authType)
			throws CertificateException {
		throw new UnsupportedOperationException();
	}
 
	public void checkServerTrusted(X509Certificate[] chain, String authType)
			throws CertificateException {
	}
}

Shown below is an example how the TrustAllTrustManager can be used to accept any SSL certificate the host being contacted publishes:

import java.io.InputStream;
import java.net.URL;
 
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
 
public class SSLTest {
	/**
	 * @param args - args[0] is the host to contact
	 */
	public static void main(String[] args) {
		try {
			TrustAllTrustManager tm = new TrustAllTrustManager();
 
			SSLContext context = SSLContext.getInstance("TLS");
			context.init(null, new TrustManager[] { tm }, null);
 
			URL url = new URL(args[0]);
			HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
			con.setSSLSocketFactory(context.getSocketFactory());
			con.connect();
			InputStream is = con.getInputStream();
			int rd;
			while ((rd = is.read()) != -1) {
				System.out.write(rd);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

In line 17 a SSLContext is initialized with the TrustAllTrustManager and in line 21 the HttpsURLConnection is setup with a SSLSocketFactory that uses our TrustAllTrustManager. So the HttpsURLConnection accepts any certificate given.

This is OK as only this instance is affected, not any SSL connection made by the system.

Hope this little solution helps other 🙂

About Jesper Udby

I'm a freelance computer Geek living in Denmark with my wife and 3 kids. I've done professional software development since 1994 and JAVA development since 1998.
This entry was posted in Java and tagged , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *