Wednesday, November 19, 2008

Let Java SSL Trust All Certificates without Violating Security Manager

Java SSL by default does not trust self-signed certificate. Wikibooks:Programming reveals a way to allow connection to secure HTTP server using self-signed certificate. The magic looks like:

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

public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
// do nothing
}

public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
// do nothing
}
}
};

// Install the all-trusting trust manager
SSLContext sc = null;
try {
sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
} catch(GeneralSecurityException gse) {
throw new IllegalStateException(gse.getMessage());
}
HttpsURLConnection.setDefaultSSLSocketFactory(
sc.getSocketFactory());

However, HttpsURLConnection.setDefaultSSLSocketFactory(...) will throw a SecurityException (a RuntimeException) if a security manager exists and its checkSetFactory method does not allow a socket factory to be specified. The thrown SecurityException looks like

Exception in thread "main" java.security.AccessControlException: access denied (java.lang.RuntimePermission setFactory)
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:323)
at java.security.AccessController.checkPermission(AccessController.java:546)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
at java.lang.SecurityManager.checkSetFactory(SecurityManager.java:1612)
at javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(HttpsURLConnection.java:308)
at SecurityManagerTest.main(SecurityManagerTest.java:50)

A workaround to avoid such a SecurityException is as below:

URL url = new URL("https://engage.ac.uk");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(sc.getSocketFactory());
conn.getInputStream();

The trick is to use the instance method setSSLSocketFactory instead of the static method setDefaultSSLSocketFactory. The former does not throw a SecurityException.

Note: need to use conn.getInputStream() instead of url.openStream(), otherwise the customised SocketFactory won't be used.

Of course to allow to connect the secure web site, the following permission should be added in the Java security policy file:

permission java.net.SocketPermission "engage.ac.uk:443", "connect";

2 comments:

andrea chiu said...

There are certain point in our life that we encounter failure but it doesn't mean you will lose hope and give up everything but it only means that every failure there's an exchange and that is new beginning. Well, thank you for sharing your article and keep on posting. Visit my site too for more information.

triciajoy.com

www.triciajoy.com

Silvia Jacinto said...

Life is a battle, if you don't know how to defend yourself then you'll end up being a loser.
So, better take any challenges as your stepping stone to become a better person. Have fun,
explore and make a lot of memories.

n8fan.net

www.n8fan.net