3 Replies Latest reply on Jul 9, 2010 4:10 AM by robert.geisler

    Retrieve Server's Certificate

    Jimmy Hui Newbie

      Hi,

       

      I currently have EJB3 via SSL working with a self signed certificate for the server. The client specifies a truststore which includes the server's certificate and all works well in this scenario.

       

      However if I don't define the truststore for the client, it obviously fails but my question is can I programmatically retrieve the server's certificate and prompt the user asking them if they trust this certificate? Which I can then programmatically add to a truststore (just like how a browser does it) ?

       

      Cheers,

      Jimmy

        • 1. Re: Retrieve Server's Certificate
          Jo Vandermeeren Newbie

          Hi Jimmy,

           

          You could call the keytool with System.exec(), bt the keytool is OS-specific, so I do not know if this is the way you want to go..

           

          By the way, I am struggling with EJB over SSL/HTTPS in JBoss 5.1.0GA, are you on the same version?

          Would you mind explaining how you got it to work? (cfr. https://community.jboss.org/thread/152957)

           

          Thanks a lot!

          Jo

          • 2. Re: Retrieve Server's Certificate
            Ron Sigal Master

            Hi Jimmy,

             

            Remoting itself doesn't have the facility you're asking for.  Perhaps it will be available in Remoting 3.

             

            For now, all I can think of is to try to use the RMI remote classloading facility built into the Application Server, though I really can't promise that it will work.  I can tell you that the AS starts a special case web server to service RMI, and it can also return files other than class files.  See Section 10.6. "RMI  Dynamic Class Loading" in the AS Administration And Development Guide at http://docs.jboss.org/jbossas/docs/Server_Configuration_Guide/4/html/index.html for a very brief introduction.  I guess you would have to send the appropriate HTTP message with the path to the truststore.  Also, you would have to set the parameter "DownloadResources" to "true" in the "jboss:service=WebService" MBean in $JBOSS/server/$CONFIG/conf/jboss-service.xml.

             

            I'm guessing that this idea can be made to work, but I could be all wrong.

             

            -Ron

            • 3. Re: Retrieve Server's Certificate
              robert.geisler Newbie

              hi jimmy,

               

              your client may retrieve the server's certificate by creating a SSLSocket and try to connect to the server over ssl.

              after handshaking (that should fail) you will be able to retrieve the server's certificate from the TrustManager.

              once you got the certificate(s), you may add it to your client application's truststore.

              added to the truststore, later SSLConnections will trust the server's certificate(s).

               

              example code:

               

              'alias' is just a name to identify your server's certificate(s) in the truststore.

              ip and port is the adress of your server, will try to connect to 'https://${ip}:${port}/'.

               

              {code:java}

              import java.io.*;
              import java.net.*;
              import java.security.*;
              import java.security.cert.*;
              import java.util.*;

              import javax.net.ssl.*;

              public class SSLTrustStoreExtension {
                public void retrieveAskToTrustAndAddCertificates(String alias, String ip, int port) throws Exception {
                  // 1) look for java default truststore
                  File dir = new File(System.getProperty("java.home"));
                  dir = new File(dir, "lib");
                  dir = new File(dir, "security");
                  File file = new File(dir, "jssecacerts");
                  // truststore may be found under <${java.home}/lib/security/cacerts
                  if (!file.isFile()) {
                    file = new File(dir, "cacerts");
                  }
                  // could be custom truststore by system property
                  file = new File(System.getProperty("javax.net.ssl.trustStore", file.getAbsolutePath()));
                  // use custom or default password (default: 'changeit')
                  char[] passphrase = System.getProperty("javax.net.ssl.trustStorePassword", //
                                                         "changeit").toCharArray();

                  // 2) load truststore
                  InputStream input = new FileInputStream(file);
                  KeyStore truststore = KeyStore.getInstance(KeyStore.getDefaultType());
                  truststore.load(input, passphrase);
                  input.close();

                  // 3) create TrustManagerInterceptor
                  TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
                  tmFactory.init(truststore);
                  X509TrustManager tm = (X509TrustManager) tmFactory.getTrustManagers()[0];
                  TrustManagerInterceptor tmInterceptor = new TrustManagerInterceptor(tm);

                  // 4) create SSLContext with TrustManagerInterceptor
                  SSLContext context = SSLContext.getInstance("TLS");
                  context.init(null, new TrustManager[]{tmInterceptor}, null);

                  // 5) create SSLConnection
                  SSLSocketFactory sslFactory = context.getSocketFactory();
                  SSLSocket sslSocket = (SSLSocket) sslFactory.createSocket(ip, port);
                  sslSocket.setSoTimeout(10 * 1000); // timeout: 10secs

                  // 6) test SSLConnection
                  try {
                    sslSocket.startHandshake();
                    // if no exception, certificate is allready trusted!
                  }
                  catch (SSLException ex) {
                    // SSLException, because certificate is not trusted!

                    // TrustManagerInterceptor now holds server's certificate(s)!
                    X509Certificate[] chain = tmInterceptor.chain;

                    // *** TODO *** ask user to trust certificate!?
                    X509Certificate[] trusted = askToTrustCertificates(chain);

                    // 7) add certificates to truststore
                    for (int i = 0; i < trusted.length; i++) {
                      truststore.setCertificateEntry(alias + "[" + (i + 1) + "]", trusted[i]);
                    }

                    // 8) create new truststore (temporary file)
                    String prefix = "jssecacerts";
                    file = File.createTempFile(prefix, null);
                    file.deleteOnExit();

                    // 9) store temporary truststore
                    String password = "tMp!tRuSTstOre";
                    OutputStream output = new FileOutputStream(file);
                    truststore.store(output, password.toCharArray());
                    output.close();

                    // 10) update system property
                    System.setProperty("javax.net.ssl.trustStore", file.getAbsolutePath());
                    System.setProperty("javax.net.ssl.trustStorePassword", password);
                  }
                  catch (ConnectException ex) {
                    // cannot connect :- /
                    return;
                  }
                  finally {
                    sslSocket.close();
                  }
                }

                private X509Certificate[] askToTrustCertificates(X509Certificate[] chain) {
                  List<X509Certificate> trusted = new ArrayList<X509Certificate>();
                  for (int i = 0; i < chain.length; i++) {
                    boolean trust = false;
                    // *** TODO *** show information of certificate to user and get user input
                    if (trust) {
                      trusted.add(chain[i]);
                    }
                  }
                  return trusted.toArray(new X509Certificate[trusted.size()]);
                }

                static class TrustManagerInterceptor implements X509TrustManager {
                  private X509TrustManager tm;
                  private X509Certificate[] chain;

                  TrustManagerInterceptor(X509TrustManager tm) {
                    this.tm = tm;
                  }

                  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 {
                    // when it comes to trust certificates,
                    // the interceptor just keeps reference to the certificates...
                    this.chain = chain;
                    // and forwards checking to 'parent' TrustManager
                    tm.checkServerTrusted(this.chain, authType);
                  }
                }
              }

              {code:java}

               

              i hope that helps.

               

              please let me know if it works.

               

              regards

              recim

               

              ps: for EJB over SSL you have to add server's certificates to truststore before doing any Remote Invocations, of course.