URLConnection and SSL

Cas asked me to give a bit of brain dump on SSL as I use it in Danger Maze, so I thought the Networking topic seemed a more appropriate place for it.

I’m going to assume you know how to set up a web server using SSL. Read up on your apache docs if not, or whatever web server you happen to be using. Personally, I’m using Apache 2 on a RedHat Linux server, and found the book “Official RedHat Linux Administrator’s Guide” has an excellent section in the web server chapter about setting up SSL.

Now then, presuiming you want to make a connection from a java client to your SSL web server, here we go. We’re going to use a java.net.URLConnection object, just like you would to make a get/post request over regular http. But we’re going to override the Trust Manager for that connection. So first thing is, we need a TrustManager class.


import java.io.*;
import java.net.*;
import javax.net.ssl.*;

class MyTrustManager extends X509TrustManager {
  
  protected static X509Certificate     kCert;

  static {
    FileInputStream fis = new FileInputStream("mycert.cer");
    BufferedInputStream bis = new BufferedInputStream(fis);
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    kCert = (X509Certificate)cf.generateCertificate(bis);
  }

  public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException
  {
  }

  public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException
  {
    for (int i=0;i<certs.length;i++) {
      if (!kCert.equals(certs[i])) {
        throw new CertificateException("Certificate does not match!");
      }
    }
  }

  public X509Certificate[] getAcceptedIssuers() {
    return null;
  }
}

You can see I punted on checkClientTrusted and getAcceptedIssuers. I’m only interested in making a connection to a known server, so these methods aren’t really important to me.

Now, to make the request, it should look like this:


  SSLContext sc = SSLContext.getInstance("SSL");
  TrustManager[] trustManagers = new TrustManager[1];
  trustManager[0] = new MyTrustManager();
  sc.init(null, trustManagers, new java.security.SecureRandom());
  HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

  URL url = new URL("https://myserver.com/myservice");
  URLConnection conn = url.openConnection();

From there you just use the normal means of creating a post or get with the URLConnection object. One other trick I’ll mention that I’ve done is rather than loading the certificate from a file, you can embed it right into your java class. Simply make a String variable that has the PEM encoded version of your cert, then create an ByteInputStream around the string.getBytes(). Use this instead of the BufferedInputStream above, and you’re all set.

Hope this helps.

–Paul

One more point -

All this is assuming you’re using a self-signed certificate on your web server. If you paid the big-bucks for a real cert from Verisign or some such, you probably don’t need any of this. I think you could just create your URL object and go.

Paul

Er, actually I’ve got no idea at all how to set up SSL on my webserver :-/ I use IIS (sorry). Any tips on that?

Cas :slight_smile:

Tips? How about, stop using IIS. Seriously, like half the viruses out there today specifically target IIS servers to propagate themselves. There is a Windows version of Apache available, so you don’t have to go setting up a linux server just to make the switch.

Sorry, that’s probably not what you wanted to hear. Unfortunately I have no experience with IIS. You’ll have to consult your documentation, maybe get a book, or contact Microsoft for support.

To point you in the right direction, what you really want to do is create a self-signed certificate for your web server. Look for that in your research. Buying an official cert from Verisign is big bucks. (Last I checked it was around $400, no doubt it’s increased since then).

Paul

[quote]Tips?

To point you in the right direction, what you really want to do is create a self-signed certificate for your web server. Look for that in your research. Buying an official cert from Verisign is big bucks. (Last I checked it was around $400, no doubt it’s increased since then).
[/quote]
If anyone needs any help on setting up a Java-based SSL server, I’ve got it mostly licked now :).

A good starting point for java SSL is this link:

http://www.churchillobjects.com/c/11201.html

which includes details of how to use the java key-mgmt tools (distributed with the JDK) to sign certificates etc. IIRC I found one or two errors in it, minor things, and it’s not got enough detail to solve a lot of the common problems (note: their troubleshooting section is WRONG; it’s explanation of at least one of the Errors/Exceptions is wrong, or was the last time I got that one :().

But on the whole, it’s nice and simply explained…

I can’t get any of Sun’s client SSL examples to work on out-of-the-box 1.4.2 installation, due to “No trusted certificate found”.

EDIT: I misread the certificate. Now I simply don’t know why a standard JDK install is broken :(.

[quote]I can’t get any of Sun’s client SSL examples to work on out-of-the-box 1.4.2 installation, due to “No trusted certificate found”.

EDIT: I misread the certificate. Now I simply don’t know why a standard JDK install is broken :(.
[/quote]
Problem appears to have been that 1.4.2 on linux is/was missing several certificates that are/were present in the windows JDK (1.4.2.something, I think). Copying the windows cacerts over the linux one fixed everything.

(problem existed on several different linux 1.4.2 JVM’s at different sites)

Hey Blah,

If these are our Win and Linux VMs then pls post a bug that the certs are missing in the Linux distro. Thanks.

JK

[quote]Hey Blah,

If these are our Win and Linux VMs then pls post a bug that the certs are missing in the Linux distro. Thanks.

JK
[/quote]
:slight_smile: Sure. I’d like to find a clean windows and a clean linux install just to 100% confirm before I log a bug though.

If anyone would like to help…you can tell which cacerts you have by looking at the file size. The windows one, with all certs, is a little over 21,000b whereas the linux one, which is missing some certs, is a little over 17,000b.

The files are found at [jdk-driectory]/jre/lib/security/cacerts

Not sure, but looks like it’s a fixed bug already, it was fixed in a sort of “invisible” release (i.e. no increment to version number, incremented an underscore suffix instead); looks like the affected linux machines were merely a build (or whatever?) behind. I found this, eventually:

Confusingly, java -version on the affected linux JVM doesn’t document any “_”+anything signifier in the version - it just has “1.4.2-b28”.

My mistake. I thought “1.4.2” was a JVM version; at least I know for the future that “1.4.2” is not a version of the JVM, although I wish Sun would adopt “1.4.2.03”, and so make it completely clear :(.

Yeah, that gets to me, too. Either there’s no difference between the builds and the user doesn’t need to know, or there are real differences whereupon the user should be told. This is quite apart from the naming scheme screw up - a basic rule of release management is that if you’re releasing version 6.2b you’d better make damn sure any reference to the version number says “6.2b”. Any other policy will eventually cause confusion and misery, probably to the last person you want to annoy. ::slight_smile:

Aren’t we talking about somethign separate from the JVM itself? Namely: the certificates? I’m not sure if you would update the jvm version if supporting resources were updated, would you?

-Chris

[quote]Aren’t we talking about somethign separate from the JVM itself? Namely: the certificates? I’m not sure if you would update the jvm version if supporting resources were updated, would you?
[/quote]
To re-iterate my earlier post: if you do NOT update, ALL of Sun’s official sample code ceases to work. All your correctly-written existing java SSL applications that access standard HTTPS sites cease to work.

This is not a minor issue. I would imagine that over the next few months there will be many java developers who fail to get started with SSL at all because their local JVM appears to be the latest version but isn’t, and Sun’s own example code won’t work “out of the box”. For instance, in corporates and universities, where the installed JVM may not be updated very often.

Sun has compounded this problem by NOT including the text of the Exception that is thrown in the page described above - if you google for it today, you won’t find anything anywhere which tells you about this problem (I’ve spent many hours looking!). Unfortunately, I don’t have any machines left which throw the exception, or else I’d quote it in this thread :).

But the problem will slowly vanish as more people upgrade their JVM’s. It shouldn’t affect most windows devs because of the auto-update feature they have now (which I believe is the only reason why I had a later windows JVM)

Update: Sun has fixed this. Linux now reports “1.4.2_04-b05” as the version (in line with win builds IIRC). I guess that means it was a bug not a feature ;).