Saturday, October 1, 2011

The SSL/TLS-based RMI Socket Factories in J2SE 5.0

Since J2SE 5.0 client and server SSL/TLS-based RMI Socket Factories are part of the Java platform. The newly defined java package javax.rmi.ssl defines two new classes:
These two new classes allow to export SSL/TLS-protected remote objects and RMI registries in a standard and portable way. You can specify the cipher suites and protocols to be enabled and if client authentication is required by the server. You don't need anymore to implement and deploy your custom SSL/TLS-based RMI Socket Factories thus avoiding the hassle of having to add to your client classpath your custom factories.
Let's introduce the SSL/TLS-based RMI Socket Factories capabilities through an example that will be incrementally modified.
The example is comprised of the following java classes:
  • Hello: The remote interface defining a single remote method sayHello().
  • HelloImpl: The remote object implementing the Hello remote interface.
  • HelloClient: The client invoking the sayHello() remote method in the Hello remote interface.
  • RmiRegistry: This class denotes the RMI registry and allows to create it with custom factories. The RMI registry can be also created in the same JVM as HelloImpl but let's create it in a separate JVM because this will make clearer the use of SSL/TLS to export remote objects and RMI registries.

Let's have a look first at the example without any SSL/TLS protection at all.
  • Hello:
    public interface Hello extends Remote {
        public String sayHello() throws RemoteException;
    }
  • HelloImpl:
    public class HelloImpl extends UnicastRemoteObject implements Hello {
        public HelloImpl() throws RemoteException {
            super();
        }
        public String sayHello() {
            return "Hello World!";
        }
        public static void main(String args[]) throws Exception {
            // Get reference to the RMI registry running on port 3000 in the local host
            Registry registry = LocateRegistry.getRegistry(null, 3000);
            // Bind this object instance to the name "HelloServer"
            HelloImpl obj = new HelloImpl();
            registry.bind("HelloServer", obj);
            System.out.println("HelloServer bound in registry");
        }
    }
  • HelloClient:
    public class HelloClient {
        public static void main(String args[]) throws Exception {
            // Get reference to the RMI registry running on port 3000 in the local host
            Registry registry = LocateRegistry.getRegistry(null, 3000);
            // Lookup the remote reference bound to the name "HelloServer"
            Hello obj = (Hello) registry.lookup("HelloServer");
            String message = obj.sayHello();
            System.out.println(message);
        }
    }
  • RmiRegistry:
    public class RmiRegistry {
        public static void main(String[] args) throws Exception {
            // Start RMI registry on port 3000
            LocateRegistry.createRegistry(3000);
            System.out.println("RMI registry running on port 3000");
            // Sleep forever
            Thread.sleep(Long.MAX_VALUE);
        }
    }
In order to run the example open a shell window, go to the directory containing the compiled class files and call:
  • $ java RmiRegistry &
    RMI registry running on port 3000
  • $ java HelloImpl &
    HelloServer bound in registry
  • $ java HelloClient
    Hello World!

Now let's export the HelloImpl remote object with the SSL/TLS-based RMI Socket Factories using the default constructors. This means that the default protocol and cipher suites will be chosen by the default SSL socket factory implementation and only server authentication will be required. Let's assume a keystore containing a self-signed certificate has been created beforehand. Also, the server's certificate has been imported as a trusted certificate into a truststore. More detailed information about how to set up all the SSL configuration can be found in the JSSE Reference Guide. The keystore and trustore location and their related passwords are supplied in the command-line through the system properties used by the Sun's JSSE implementation:
  • javax.net.ssl.keyStore
  • javax.net.ssl.keyStorePassword
  • javax.net.ssl.trustStore
  • javax.net.ssl.trustStorePassword
When the client invokes the sayHello() method the server will send its certificate to the client. The client will then verify it against its truststore to see if it is a trusted certificate. If true, the method invocation goes on. Otherwise, the SSL handshake fails and an exception is thrown.
The following file needs to be changed as follows:
  • HelloImpl:
    public class HelloImpl extends UnicastRemoteObject implements Hello {
        public HelloImpl() throws RemoteException {
            super(0, new SslRMIClientSocketFactory(), new SslRMIServerSocketFactory());
        }
        public String sayHello() {
            return "Hello World!";
        }
        public static void main(String args[]) throws Exception {
            // Get reference to the RMI registry running on port 3000 in the local host
            Registry registry = LocateRegistry.getRegistry(null, 3000);
            // Bind this object instance to the name "HelloServer"
            HelloImpl obj = new HelloImpl();
            registry.bind("HelloServer", obj);
            System.out.println("HelloServer bound in registry");
        }
    }
In order to run the example open a shell window, go to the directory containing the compiled class files and call:
  • $ java -Djavax.net.ssl.trustStore=truststore -Djavax.net.ssl.trustStorePassword=trustword RmiRegistry &
    RMI registry running on port 3000
  • $ java -Djavax.net.ssl.keyStore=keystore -Djavax.net.ssl.keyStorePassword=password HelloImpl &
    HelloServer bound in registry
  • $ java -Djavax.net.ssl.trustStore=truststore -Djavax.net.ssl.trustStorePassword=trustword HelloClient
    Hello World!

Now let's export the HelloImpl remote object with the SSL/TLS-based RMI Socket Factories which require client authentication too. Now when the client invokes the sayHello() method the server will send its certificate to the client. The client will then verify it against its truststore to see if it is a trusted certificate. What's new here is that the client has to send also a certificate to the server. The server will verify the client's certificate against its truststore in order to see if it's trusted. If both server and client authentication succeeds, the method invocation goes on. Otherwise, the SSL handshake fails and an exception is thrown.
The following file needs to be changed as follows:
  • HelloImpl:
    public class HelloImpl extends UnicastRemoteObject implements Hello {
        public HelloImpl() throws RemoteException {
            super(0, new SslRMIClientSocketFactory(),
                     new SslRMIServerSocketFactory(null, null, true));
        }
        public String sayHello() {
            return "Hello World!";
        }
        public static void main(String args[]) throws Exception {
            // Get reference to the RMI registry running on port 3000 in the local host
            Registry registry = LocateRegistry.getRegistry(null, 3000);
            // Bind this object instance to the name "HelloServer"
            HelloImpl obj = new HelloImpl();
            registry.bind("HelloServer", obj);
            System.out.println("HelloServer bound in registry");
        }
    }
In order to run the example open a shell window, go to the directory containing the compiled class files and call:
  • $ java -Djavax.net.ssl.keyStore=keystore -Djavax.net.ssl.keyStorePassword=password -Djavax.net.ssl.trustStore=truststore -Djavax.net.ssl.trustStorePassword=trustword RmiRegistry &
    RMI registry running on port 3000
  • $ java -Djavax.net.ssl.keyStore=keystore -Djavax.net.ssl.keyStorePassword=password -Djavax.net.ssl.trustStore=truststore -Djavax.net.ssl.trustStorePassword=trustword HelloImpl &
    HelloServer bound in registry
  • $ java -Djavax.net.ssl.keyStore=keystore -Djavax.net.ssl.keyStorePassword=password -Djavax.net.ssl.trustStore=truststore -Djavax.net.ssl.trustStorePassword=trustword HelloClient
    Hello World!

Now let's export the HelloImpl remote object with the SSL/TLS-based RMI Socket Factories which require client authentication and the use of the TLSv1 protocol and the SSL_RSA_WITH_RC4_128_MD5 cipher suite. Now when the client invokes the sayHello() method besides verifying the client and server certificates the SSL handshake will fail if any of the server or client JSSE implementations does not support the supplied protocol and/or cipher suite. The enabled protocols and cipher suites are specified through the SslRMIServerSocketFactory constructor in the server side and through the system properties defined by SslRMIClientSocketFactory in the client side:
  • javax.rmi.ssl.client.enabledCipherSuites
  • javax.rmi.ssl.client.enabledProtocols
The following file needs to be changed as follows:
  • HelloImpl:
    public class HelloImpl extends UnicastRemoteObject implements Hello {
        public HelloImpl() throws RemoteException {
            super(0, new SslRMIClientSocketFactory(),
                     new SslRMIServerSocketFactory(new String[] {"SSL_RSA_WITH_RC4_128_MD5"},
                                                   new String[] {"TLSv1"},
                                                   true));
        }
        public String sayHello() {
            return "Hello World!";
        }
        public static void main(String args[]) throws Exception {
            // Get reference to the RMI registry running on port 3000 in the local host
            Registry registry = LocateRegistry.getRegistry(null, 3000);
            // Bind this object instance to the name "HelloServer"
            HelloImpl obj = new HelloImpl();
            registry.bind("HelloServer", obj);
            System.out.println("HelloServer bound in registry");
        }
    }
In order to run the example open a shell window, go to the directory containing the compiled class files and call:
  • $ java -Djavax.net.ssl.keyStore=keystore -Djavax.net.ssl.keyStorePassword=password -Djavax.net.ssl.trustStore=truststore -Djavax.net.ssl.trustStorePassword=trustword -Djavax.rmi.ssl.client.enabledCipherSuites=SSL_RSA_WITH_RC4_128_MD5 -Djavax.rmi.ssl.client.enabledProtocols=TLSv1 RmiRegistry &
    RMI registry running on port 3000
  • $ java -Djavax.net.ssl.keyStore=keystore -Djavax.net.ssl.keyStorePassword=password -Djavax.net.ssl.trustStore=truststore -Djavax.net.ssl.trustStorePassword=trustword HelloImpl &
    HelloServer bound in registry
  • $ java -Djavax.net.ssl.keyStore=keystore -Djavax.net.ssl.keyStorePassword=password -Djavax.net.ssl.trustStore=truststore -Djavax.net.ssl.trustStorePassword=trustword -Djavax.rmi.ssl.client.enabledCipherSuites=SSL_RSA_WITH_RC4_128_MD5 -Djavax.rmi.ssl.client.enabledProtocols=TLSv1 HelloClient
    Hello World!

Let's finally protect the access to the RMI registry with SSL/TLS. In order to do that the methods taking as input parameters RMI socket factories in the LocateRegistry class, i.e. createRegistry and getRegistry will be used. The SSL/TLS-based RMI Socket Factories used to create the RMI registry must require client authentication as this is the only way the RMI registry can refuse requests from clients sending untrusted certificates.
The following files need to be changed as follows:
  • HelloImpl:
    public class HelloImpl extends UnicastRemoteObject implements Hello {
        public HelloImpl() throws RemoteException {
            super(0, new SslRMIClientSocketFactory(),
                     new SslRMIServerSocketFactory(null, null, true));
        }
        public String sayHello() {
            return "Hello World!";
        }
        public static void main(String args[]) throws Exception {
            // Get reference to the RMI registry running on port 3000 in the local host
            Registry registry = LocateRegistry.getRegistry(null, 3000, new SslRMIClientSocketFactory());
            // Bind this object instance to the name "HelloServer"
            HelloImpl obj = new HelloImpl();
            registry.bind("HelloServer", obj);
            System.out.println("HelloServer bound in registry");
        }
    }
  • HelloClient:
    public class HelloClient {
        public static void main(String args[]) throws Exception {
            // Get reference to the RMI registry running on port 3000 in the local host
            Registry registry = LocateRegistry.getRegistry(null, 3000, new SslRMIClientSocketFactory());
            // Lookup the remote reference bound to the name "HelloServer"
            Hello obj = (Hello) registry.lookup("HelloServer");
            String message = obj.sayHello();
            System.out.println(message);
        }
    }
  • RmiRegistry:
    public class RmiRegistry {
        public static void main(String[] args) throws Exception {
            // Start RMI registry on port 3000
            LocateRegistry.createRegistry(3000,
                                          new SslRMIClientSocketFactory(),
                                          new SslRMIServerSocketFactory(null, null, true));
            System.out.println("RMI registry running on port 3000");
            // Sleep forever
            Thread.sleep(Long.MAX_VALUE);
        }
    }
In order to run the example open a shell window, go to the directory containing the compiled class files and call:
  • $ java -Djavax.net.ssl.keyStore=keystore -Djavax.net.ssl.keyStorePassword=password -Djavax.net.ssl.trustStore=truststore -Djavax.net.ssl.trustStorePassword=trustword RmiRegistry &
    RMI registry running on port 3000
  • $ java -Djavax.net.ssl.keyStore=keystore -Djavax.net.ssl.keyStorePassword=password -Djavax.net.ssl.trustStore=truststore -Djavax.net.ssl.trustStorePassword=trustword HelloImpl &
    HelloServer bound in registry
  • $ java -Djavax.net.ssl.keyStore=keystore -Djavax.net.ssl.keyStorePassword=password -Djavax.net.ssl.trustStore=truststore -Djavax.net.ssl.trustStorePassword=trustword HelloClient
    Hello World! 

Feel free to download the resource zip file in attachment and play with it or tailor it to your specific application needs.
 

No comments:

Post a Comment