Friday, July 13, 2007

Q: How do I get a remote server's SSL Cert?

A: Not 100% securely over the internet, but there are a few options

So you have to work with a web service over SSL? In order to get this to work you are going to need a certificate to import into your local key store using the keytool command.

The problem is that communication over the internet can be very easily intercepted so ideally you would receive the certificate in person from the provider preferably with the providers parents and partner there to verify the authenticity of the certificate.

Having said that if you are not entirely paranoid you can normally rely on a certificate that is signed by a root authority. Your web browser will do a reasonable job of checking this and just accepting the certificate that the server gives out is likely to be secure enough for most low key operations. You would probably want to be more paranoid if you are exchanging millions of pounds.

The question comes as to how do I get hold of this in a handy format that I can then pass into the keytool? Well the first and easiest way is to ask your web browser although sadly this trick only works for IE7. So connect to the endpoint you are going to work with or just request the WSDL over SSL. Then click on the padlock icon in the url status bar and view the certificate. Then travel to the "Details" page and voila there is a "Copy To File..." button which invokes a wizard to generate a .CER file that keytool with understand.

So how do you do the same trick from Java? Well it is easy enough to get a list of server side certificates using java and write the first one directly into the key store:

 public static void main(String[] args)
    throws Exception
  {
    assert args.length == 3 : "Should have three parameters keystore, password, site";
    File keystoreFile = new File(args[0]);
    assert keystoreFile.exists();
    char[] password = args[1].toCharArray();
    URL url = new URL(args[2]);

    // Load the keystore
    //
    
    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    keyStore.load(
      new BufferedInputStream(new FileInputStream(keystoreFile)),
      password);    
  
    // Create a connection
    //

    HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
    con.connect();
    Certificate certs[] = con.getServerCertificates();
    
    // Get the first certificate
    //
    
    Certificate firstCert = certs[0];
    
    
    keyStore.setCertificateEntry(
      url.getHost(), firstCert);
      
    // Store the keystore
    //
      
    keyStore.store(
      new BufferedOutputStream(
        new FileOutputStream(keystoreFile)), password);
    
  }

Again this is a way of getting a certificate is not 100% secure; but might well do for a bit of development. Also it is probably the only choice when dealing with entities such as google or amazon.

For more fun and games with HTTPS check out Tug's Blog on the topic of configuring HTTPS in oc4j and on the client side.

4 comments:

knottygal said...

Hey..I came across ur blog thru google..
I am trying to solve a similar problem.. My scenario is..I have a remote SSL server loaded with its certificate. how do I import the server's certificate into my client's keystore thru java code??

HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.connect();

Certificate certs[] = con.getServerCertificates();

connect() method is failing for me cos to establish the https connection I need the server's cert in my client's keystore..

now, I am questioning the purpose of your API. why wud u wanna fetch server certificates when u cant establish a conn without it in ur keystore??

Gerard Davison said...

Hi,

I suppose this code was for the degenerate case where the remote's servers certificate is signed by a trust root provider. (Such as versign)

In this case you can establish a connection because we kinda know this is going to be a valid certificate.

When you are using a web service though you want to make sure that the server is using a particular certificate to ensure you communication channel. So you need to import the cert into a keystore, at least in the case of the Oracle WS stack.

In your case it seems like you will need to access the cert in another way. If you have access to the actual server cert file then you can use keytool to export and then reimport the certificate as required.

Does this make sense?

Gerard

Unknown said...

Very helpful Gerard, thank you.
I just have a question. Establishing a connection can be done in JAVA by this code:

URL destinationURL = new URL("https://www.google.com");
HttpsURLConnection con = (HttpsURLConnection) destinationURL.openConnection();
con.connect();
Certificate[] certs = con.getServerCertificates();

My question is how con.getServerCertificates() really imports all the certificates chanining into Java from given a URL link, does con.getServerCertificates() always set a SSL connection to the webpage and import all the certificates chaining into an array OR does it just use (cacerts file) that comes with JKD ?

Gerard Davison said...

A,

It always lists the server certificates.

Gerard