Using RMI over SSL authentication for application-level access control

Alexander V. Konstantinou
akonstan@acm.org

Version 10-DEC-2001 (documentation update 25-FEB-2003)


Sun provides support for running RMI over SSL in its Java2 platforms using a custom RMISocketFactory. In cases when applications need to provide their own fine-grained access control, it is useful to obtain access to the Java security principal (java.security.Principal) that was SSL authenticated.

The Java2 SSLSocket class provides a getSession() method which can be used to obtain the SSL principal who was authenticated. Unfortunately, by the time the RMI server method is invoked, the socket used to read the remote invocation parameters has been hidden by the RMI implementation.

RMI/SSL/TCP stack diagram

I searched the RMI-USERS mailing list archives and found a relevant posting by Edward Burcher and the reply by Tim Blackman. These messages did not contain any code but got me going in the right path. Clearly, these people are not responsible for the code in this example (and neither am I if you read the disclaimer!)

The basic idea is to intercept the SSLServerSocket.accept() to return an SSLSocket object whose getInputStream() method returns a modified object whose read() methods have the side-effect of modifying some thread-local variable.

Based on the assumption that the arguments are unmarshalled in the same thread as the one used to invoke the method call, we can then query the thread local object from the server's code (using a static method).

WARNING: this code depends on an assumption on RMI server behavior (which may change at any time!)

The code in this package provides a simple RMISocketFactory that supports retrieval of that lost authentication context.

OUT OF DATE WARNING: this code has not been updated for JDK 1.5. Please read the following message dated August 2006:
Alexander

There are some problems with this rather old Web page and it is causing confusion.

Firstly, the RMISocketFactory.setSocketFactory() method is deprecated and should not be called. Instead the super() line of the remote object should supply the CSF and SSF as required. Doing it the way you have it, i.e. changing the RMI socket factory globally, inhibits the use of plaintext Registries in conjunction with secure objects, and also Activation in conjunction with secure objects (because the activatable stub must talk plaintext to RMID but SSL to the secure Activatable).

Secondly, much of it was obsoleted with the introduction of javax.rmi.ssl.SslRMIClientSocketFactory and javax.rmi.ssl.SslRMIServerSocketFactory in JDK 1.5.

Your fundamental technique of saving the accepted socket so you can get the Principal &c is of course still sound, and I don't know why Sun haven't provided better access to that after all these years.

Regards

Esmond Pitt
Author 'java.rmi': http://www.telekinesis.com.au

Requirements

The code requires JDK version 1.4 due to the following dependencies on the Socket library API: Due to the socket constructor issues, backporting to 1.2/1.3 will not be simple (unless you can guarantee that the Socket(InetAddress, port) constructor will succeed; that is, you know some well known server)

Copyright

Copyright © 2001 Alexander V. Konstantinou (akonstan@acm.org)

Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. Alexander V. Konstantinou makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.

Download

The latest version of this code (source + binary) can be downloaded from the URL

Source Code Browsing

The main class is SecureRMISocketFactory.java with the following utility classes

Its use is demonstrated in a simple RMI application:

Example

The following example demonstrates how to execute the sample code.

Compilation

[ UNIX users can type "make compile" ]

javac *.java
rmic TestServer

Key Management

[ UNIX users can type "make keystore" ]

  1. Create a self-signed server and a self-signed client key each in its own keystore
    keytool -genkey -v -keyalg RSA -keystore server.keystore -dname "CN=Server, OU=Bar, O=Foo, L=Some, ST=Where, C=UN"
    
    keytool -genkey -v -keyalg RSA -keystore client.keystore -dname "CN=Client, OU=Bar, O=Foo, L=Some, ST=Where, C=UN"
    
  2. Export the server's and the client's public keys from their respective keystores
    keytool -export -rfc -keystore server.keystore -alias mykey -file server.public-key
    
    keytool -export -rfc -keystore client.keystore -alias mykey -file client.public-key
    
  3. Import the client's public key to the server's keystore, and vice-versa:
    keytool -import -alias client -keystore server.keystore -file client.public-key
    
    keytool -import -alias server -keystore client.keystore -file server.public-key
    

Execution

  1. Start the server with the appropriate SSL configuration

    [ UNIX users can type "make run-server" ]

    java -Djavax.net.ssl.trustStore=server.keystore -Djavax.net.ssl.keyStore=server.keystore  -Djavax.net.ssl.keyStorePassword=server TestServer
    System time: Mon Dec 10 14:12:59 EST 2001
    Registering secure RMI socket factory ...
    Starting RMI registry on port 7123 ...
    Binding TestRemote server to rmi://localhost:7123/Test ...
    Test object bound to rmi://localhost:7123/Test
    
  2. Start the client with the appropriate SSL configuration (once the "Test object bound to ..." message is printed by the server

    [ UNIX users can type "make run-client" ]

    java -Djavax.net.ssl.trustStore=client.keystore -Djavax.net.ssl.keyStore=client.keystore  -Djavax.net.ssl.keyStorePassword=client TestClient
    System time: Mon Dec 10 14:13:02 EST 2001
    Registering secure RMI socket factory ...
    Looking up rmi://localhost:7123/Test ...
    Object found ... invoking remote method
    toLowerCase(Hello World!) = hello world!
    
  3. Watch the output at the server
    Mon Dec 10 14:13:04 EST 2001 toLowerCase(Hello World!): invoked by CN=Client, OU=Bar, O=Foo, L=Some, ST=Where, C=UN
    

$Id: README.html,v 1.7 2003/02/25 20:11:02 akonstan Exp $