Java Security: Secure RMI

The underlying socket communication used by Java’s default RMI implementation is not secured. So, how do you develop Java RMI applications that can make remote invocations over secure SSL connections? In this section, we shall modify our calculator RMI application (discussed in the RMI chapter) so that the underlying network communication becomes secured.

In general, Java RMI framework allows us to export a remote object specifying custom socket factories that create sockets of desired type for underlying network communication. These factories may be specified either in the overloaded constructor or exportObject() method of java.rmi.server.UnicastRemoteObject or java.rmi.activation.Activatable class. They take a java. rmi.server.RMIClientSocketFactory instance and a java.rmi.server.RMIServerSocketFactory instance. The RMIClientSocketFactory may be used to control how connections are established and the type of socket to use. On the other hand, the RMIServerSocketFactory may be used to control how incoming connections are listened for and accepted as well as the type of sockets to use for incoming connections.

Since, we want our calculator application to work over an SSL connection, our task is now to specify socket factories that create SSL sockets. Fortunately, RMI system provides two classes SslRMIClientSocketFactory and SslRMIServerSocketFactory (in javax.rmi.ssl package) for this purpose which implement RMIClientSocketFactory and RMIServerSocketFactory over the SSL/TLS protocols respectively.

So, in the calculator server application, let us export our calculator object specifying them as follows:

SimpleCalculator cal = new SimpleCalculator();

Calculator stub = (Calculator)UnicastRemoteObject.exportObject(cal,0, new SslRMIClientSocketFactory(), new

SslRMIServerSocketFactory());

So, our modified server (SSLCalculatorServer.java) looks like this:

//SSLCalculatorServer.java

import java.rmi.*;

import java.rmi.registry.*;

import java.rmi.server.*;

import javax.rmi.ssl.*;

public class SSLCalculatorServer {

public static void main(String args[]) {

try {

SimpleCalculator cal = new SimpleCalculator();

Calculator stub = (Calculator)UnicastRemoteObject.exportObject(cal,0, new SslRMIClientSocketFactory(), new SslRMIServerSocketFactory());

Registry registry = LocateRegistry.createRegistry(1099);

String name = “calculator”; registry.rebind(name, stub);

System.out.println(”Calculator server ready…”);

}catch (Exception e) { e.printStackTrace(); }

}

}

The modified piece of code has been shown with bold face. The code of the client remains unchanged as it was. To run the application, server creates a keystore server.ks using the following command:

keytool -genkey -alias mykey -keyalg RSA -keystore server.ks -storepass 123456

-keypass 123456 -dname ”CN=U. K. Roy, OU=IT, O=JU, L=Kolkata, ST=WB, C=IN”

Now, export this certificate to a certificate file server.cer using the following command:

keytool -export -alias mykey -keystore server.ks -storepass 123456 -file server.cer

Copy this server.cer file to client’s home directory and import it in the client’s trust store client.ts using the following command:

keytool -import -v -keystore client.ts -storepass 123456 -file server.cer

The application is now ready to be started. Start the server as follows:

java -Djavax.net.ssl.keyStore=server.ks -Djavax.net.ssl.keyStorePassword=123456

SSLCalculatorServer

Start the client as follows:

java -Dj avax.net.ssl.trustStore=client.ts -Dj avax.net.ssl.trustStorePassword=123456

CalculatorClient 172.16.5.81

A sample output is shown in Figure 19.6:

If the server also needs client authentication, the overloaded constructor of SsiRMiServerSocketFactory may be used when an object is exported as follows:

Calculator stub = (Calculator)UnicastRemoteObject.exportObject(cal,0, new

SslRMIClientSocketFactory(), new SslRMIServerSocketFactory(null, null, true));

In this case, client creates a certificate and makes an entry in the keystore. Similarly, the server imports this certificate in its truststore.

Note that a remote method invocation takes place in steps:

  • A client consults remote registry to get a reference to the stub of the remote object
  • The client then invokes methods on the remote object

Our application is so far secured for the second step. The communication with the remote registry still takes place in a non-secure way. To solve this problem, custom socket factories may be specified in the createRegistry() and getRegistry() methods of LocateRegistry. So, modify the calculator server as follows:

Registry registry = LocateRegistry. createRegistry (1 0 9 9 , new

SslRMIClientSocketFactory(), new SslRMIServerSocketFactory(null, null, true));

Similarly, modify the client (call it SSLCalculatorClient.java) as follows:

Registry registry = LocateRegistry.getRegistry(args[0],1099, new

javax.rmi.ssl.SslRMIClientSocketFactory());

To run this application, client creates a keystore client.ks using the following command:

keytool -genkey -alias mykey -keyalg RSA -keystore client.ks -storepass 123456 -keypass 123456 -dname ”CN=U. K. Roy, OU=IT, O=JU, L=Kolkata, ST=WB, C = IN”

Now, export this certificate to a certificate file client.cer using the following command:

keytool -export -alias mykey -keystore client.ks -storepass 123456 -file client.cer

Copy this client.cer file to server’s home directory and import it in server’s trust store server. ts using the following command:

keytool -import -v -keystore server.ts -storepass 123456 -file client.cer

The application is now ready to be started. Start the server as follows:

kava -Djavax.net.ssl.keyStore=server.ks -Djavax.net.ssl.keyStorePassword=123456 –

Djavax.net.ssl.trus tStore=server.t s -Dj avax.net.ssl.trustStorePas sword=123456

SSLCalculatorServer

Start the client as follows:

java -Dj avax.net.ssl.keyStore=client.ks -Dj avax.net.ssl.keyStorePassword=1234 5 6

-Djavax.net.ssl.trustStore=client.ts -Dj avax.net.ssl.trustStorePassword=123456

SSLCalculatorClient 172.16.5.81

1. Writing Custom Socket Factories

In our previous example, we have used classes provided by the Java RMI framework as custom socket factories. It is also possible to develop our own factories and specify them. To create a custom client socket factory, we implement the RMIClientSocketFactory interface which has a single method. Here is a sample implementation (MySSLClientSocketFactory.java):

//MySSLClientSocketFactory.java import java.io.*;

import java.net.*;

import java.rmi.server.*;

import javax.net.ssl.*;

public class MySSLClientSocketFactory implements RMIClientSocketFactory, Serializable {

public Socket createSocket(String host, int port) throws IOException {

System.setProperty(“javax.net.ssl.keyStore”, “client.ks”);

System.setProperty(“javax.net.ssl.keyStorePassword”, “1234 5 6”);

System.setProperty(“javax.net.ssl.trustStore”, “client.ts”);

System.setProperty(“javax.net.ssl.trustStorePassword”, “123456”);

SocketFactory factory = SSLSocketFactory.getDefault();

Socket s = factory.createSocket(host, port);

return s;

}

}

Here, we have created an SSLSocket using default implementation of SSLSocketFactory and returned it. We also have set up necessary properties for SSL. Writing a custom server socket factory is similar as follows (MySSLServerSocketFactory.java):

//MySSLServerSocketFactory.java import java.io.*;

import java.net.*; import java.rmi.server.*;

import javax.net.ssl.*; import javax.net.*;

public class MySSLServerSocketFactory implements RMIServerSocketFactory {

public ServerSocket createServerSocket(int port) throws IOException {

System.setProperty(”javax.net.ssl.keyStore”, “server.ks”);

System.setProperty(”javax.net.ssl.keyStorePassword”, ”123456”);

System.setProperty(”javax.net.ssl.trustStore”, ”server.ts”);

System.setProperty(”javax.net.ssl.trustStorePassword”, ”123456”);

ServerSocketFactory ssf= SSLServerSocketFactory.getDefault();

ServerSocket ss = ssf.createServerSocket(port);

((SSLServerSocket)ss).setNeedClientAuth(true);

return ss;

}

}

Here, we have created an ssLServerSocket using default implementation of ssLServerSocketFactory and returned it. We also have set up necessary properties for SSL. The factory also specifies that the server needs client authentication using setNeedCiientAuth() method.

The server is then modified as follows:

Calculator stub = (Calculator)UnicastRemoteObject.exportObject(cal,0, new

MySSLClientSocketFactory(), new MySSLServerSocketFactory());

Registry registry = LocateRegistry.createRegistry(1099, new

MySSLClientSocketFactory(), new MySSLServerSocketFactory());

Modify the client as follows:

Registry registry =

LocateRegistry.getRegistry(args[0],1099, new MySSLClientSocketFactory());

Start the server as follows:

java SSLCalculatorServer

Start the client as follows:

java SSLCalculatorClient 172.16.5.81

Source: Uttam Kumar Roy (2015), Advanced Java programming, Oxford University Press.

Leave a Reply

Your email address will not be published. Required fields are marked *