セキュアなRMI接続を表すサンプル・コード
HelloClient.java
の例では、セキュアなJava Remote Method Invocation (RMI)接続の作成方法を示しています。このサンプル・コードは、基本的には、カスタムRMIソケット・ファクトリをインストールして使用するように変更されたHello Worldサンプルです。JSSEを使用してSSLトランスポート層を介してRMIを使用します。サーバーは、内部RMIレジストリを設定するHelloImpl.java
を実行します(rmiregistry
コマンドは使用しません)。クライアントは、HelloClient
を実行し、セキュリティ保護された接続を介して通信します。
使用方法
このサンプルの設定は少々複雑です。必要なステップは次のとおりです。
% javac *.java
% rmic HelloImpl
% java -Djava.security.policy=policy HelloImpl
(別のウィンドウで実行)
% java HelloClient
(別のウィンドウで実行)
サーバーについては、RMIセキュリティ・マネージャがインストールされ、指定されたポリシー・ファイルにより、任意のホストからの接続を受け入れるためのアクセス権が付与されます。すべてのアクセス権を付与することは、本番環境では決して行わないでください。次のように、適切に制限されたネットワーク権限を付与する必要があります。
permission java.net.SocketPermission "hostname:1024-", "accept,resolve";
また、この例は、簡単な更新を加えることで、標準のSSL/TLSベースのRMIソケット・ファクトリを使用して実行されるようにできます。これを行うには、使用するHelloImpl.java
ファイルを次のように変更します。
javax.rmi.ssl.SslRMIClientSocketFactory
javax.rmi.ssl.SslRMIServerSocketFactory
変更前の内容は次のとおりです。
RMISSLClientSocketFactory
RMISSLServerSocketFactory
これらのクラスは、SSLSocketFactory.getDefault()およびSSLServerSocketFactory.getDefault()を使用するため、キーとトラスト・データを検出するようにシステム・プロパティを適切に構成する必要があります。
注意:
標準のSSL/TLSベースのRMIソケット・ファクトリを使用する場合は、次のようにシステム・プロパティでキーストアを指定できます。
-Djavax.net.ssl.keyStore=testkeys
-Djavax.net.ssl.keyStorePassword=passphrase
HelloClient.java
import java.net.InetAddress;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class HelloClient {
private static final int PORT = 2019;
public static void main(String args[]) {
try {
// Make reference to SSL-based registry
Registry registry = LocateRegistry.getRegistry(
InetAddress.getLocalHost().getHostName(), PORT,
new RMISSLClientSocketFactory());
// "obj" is the identifier that we'll use to refer
// to the remote object that implements the "Hello"
// interface
Hello obj = (Hello) registry.lookup("HelloServer");
String message = "blank";
message = obj.sayHello();
System.out.println(message+"\n");
} catch (Exception e) {
System.out.println("HelloClient exception: " + e.getMessage());
e.printStackTrace();
}
}
}
Hello.java
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Hello extends Remote {
String sayHello() throws RemoteException;
}
HelloImpl.java
import java.io.*;
import java.net.InetAddress;
import java.rmi.RemoteException;
import java.rmi.RMISecurityManager;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl extends UnicastRemoteObject implements Hello {
private static final int PORT = 2019;
public HelloImpl() throws Exception {
super(PORT,
new RMISSLClientSocketFactory(),
new RMISSLServerSocketFactory());
}
public String sayHello() {
return "Hello World!";
}
public static void main(String args[]) {
// Create and install a security manager
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
try {
// Create SSL-based registry
Registry registry = LocateRegistry.createRegistry(PORT,
new RMISSLClientSocketFactory(),
new RMISSLServerSocketFactory());
HelloImpl obj = new HelloImpl();
// Bind this object instance to the name "HelloServer"
registry.bind("HelloServer", obj);
System.out.println("HelloServer bound in registry");
} catch (Exception e) {
System.out.println("HelloImpl err: " + e.getMessage());
e.printStackTrace();
}
}
}
RMISSLClientSocketFactory.java
import java.io.*;
import java.net.*;
import java.rmi.server.*;
import javax.net.ssl.*;
public class RMISSLClientSocketFactory
implements RMIClientSocketFactory, Serializable {
public Socket createSocket(String host, int port) throws IOException {
SSLSocketFactory factory =
(SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket)factory.createSocket(host, port);
return socket;
}
public int hashCode() {
return getClass().hashCode();
}
public boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (obj == null || getClass() != obj.getClass()) {
return false;
}
return true;
}
}
RMISSLServerSocketFactory.java
import java.io.*;
import java.net.*;
import java.rmi.server.*;
import javax.net.ssl.*;
import java.security.KeyStore;
import javax.net.ssl.*;
public class RMISSLServerSocketFactory implements RMIServerSocketFactory {
/*
* Create one SSLServerSocketFactory, so we can reuse sessions
* created by previous sessions of this SSLContext.
*/
private SSLServerSocketFactory ssf = null;
public RMISSLServerSocketFactory() throws Exception {
try {
// set up key manager to do server authentication
SSLContext ctx;
KeyManagerFactory kmf;
KeyStore ks;
char[] passphrase = "passphrase".toCharArray();
ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("testkeys"), passphrase);
kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, passphrase);
ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers(), null, null);
ssf = ctx.getServerSocketFactory();
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
public ServerSocket createServerSocket(int port) throws IOException {
return ssf.createServerSocket(port);
}
public int hashCode() {
return getClass().hashCode();
}
public boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (obj == null || getClass() != obj.getClass()) {
return false;
}
return true;
}
}
policy
// In this example, for simplicity, we will use a policy file that
// gives global permission to anyone from anywhere. Do not use this
// policy file in a production environment.
grant {
permission java.net.SocketPermission "*", "accept,resolve";
};