【问题标题】:Simple RMI Server with SSL带有 SSL 的简单 RMI 服务器
【发布时间】:2014-03-02 10:32:54
【问题描述】:

尝试使用 SSL 加密设置简单的 RMI 服务器。这是一个简单的聊天应用程序,它有一个 java 服务器应用程序和一个 java 客户端应用程序,但是,我现在什至无法使用一个简单的 RMI 示例!

我可以让它工作的唯一方法是客户端和服务器都具有相同的信任库和密钥库。不过对我来说,这听起来不正确,因为这意味着每个客户端也都拥有服务器的私钥..

我按照this guide 创建了信任/密钥库。我首先尝试生成一个密钥库和信任库,然后只运行带有密钥库的服务器和带有信任库的客户端。这不起作用,所以我为每个生成了一对并加载,如下面的代码所示。

它认为我可能在某个地方遗漏了一些明显的东西,只是我一生都无法弄清楚我做错了什么。我目前有以下内容,但是在运行服务器时我得到了errors below

错误:

Server exception: java.rmi.ConnectIOException: error during JRMP connection establishment; nested exception is: 
    javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
java.rmi.ConnectIOException: error during JRMP connection establishment; nested exception is: 
    javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.rmi.transport.tcp.TCPChannel.createConnection(Unknown Source)
    at sun.rmi.transport.tcp.TCPChannel.newConnection(Unknown Source)
    at sun.rmi.server.UnicastRef.newCall(Unknown Source)
    at sun.rmi.registry.RegistryImpl_Stub.bind(Unknown Source)
    at Server.main(Server.java:38)

Hello.java

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Hello extends Remote {

    String sayHello() throws RemoteException;

}

Server.java

import java.io.IOException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.rmi.ssl.SslRMIClientSocketFactory;
import javax.rmi.ssl.SslRMIServerSocketFactory;


public class Server extends UnicastRemoteObject implements Hello {

    private static final long serialVersionUID = 5186776461749320975L;

    protected Server(int port) throws IOException {

        super(port, new SslRMIClientSocketFactory(), new SslRMIServerSocketFactory(null, null, true));      
    }

    @Override
    public String sayHello() {
        return "Hello, world!";
    }

    public static void main(String[] args) throws RemoteException, IllegalArgumentException {

        try {           

            setSettings();

            Server server = new Server(2020);

            LocateRegistry.createRegistry(2020, new SslRMIClientSocketFactory(), new SslRMIServerSocketFactory(null, null, true));
            System.out.println("RMI registry running on port " + 2020);             

            Registry registry = LocateRegistry.getRegistry("DAVE-PC", 2020, new SslRMIClientSocketFactory());

            registry.bind("Hello",  server);

        } catch (Exception e) {
            System.err.println("Server exception: " + e.toString());
            e.printStackTrace();
        }

    }

    private static void setSettings() {

        String pass = "password";

        System.setProperty("javax.net.ssl.debug", "all");

    System.setProperty("javax.net.ssl.keyStore", "C:\\ssl\\serverkeystore.jks");
    System.setProperty("javax.net.ssl.keyStorePassword", pass);
    System.setProperty("javax.net.ssl.trustStore", "C:\\ssl\\servertruststore.jks");
    System.setProperty("javax.net.ssl.trustStorePassword", pass);




    }

}

Client.java

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import javax.rmi.ssl.SslRMIClientSocketFactory;

public class Client {

    private Client() {}

    public static void main(String[] args) {        

        try {

            setSettings();  

            Registry registry = LocateRegistry.getRegistry("DAVE-PC", 2020, new SslRMIClientSocketFactory());

            Hello hello = (Hello) registry.lookup("Hello");

            String message = hello.sayHello();

            System.out.println(message);            

        } catch (Exception e) {
            System.err.println("Client exception: " + e.toString());
            e.printStackTrace();
        }
    }

private static void setSettings() {

        String pass = "password";
        System.setProperty("javax.net.ssl.debug", "all");
    System.setProperty("javax.net.ssl.keyStore", "C:\\ssl\\clientkeystore.jks");
    System.setProperty("javax.net.ssl.keyStorePassword", pass);
    System.setProperty("javax.net.ssl.trustStore", "C:\\ssl\\clienttruststore.jks");
    System.setProperty("javax.net.ssl.trustStorePassword", pass);

    }

}

【问题讨论】:

  • 您不应该编辑您的原始帖子,因为当作者更正您的代码并且外部用户尝试同时关注答案和原始帖子时,接受的答案毫无意义。您应该自己做出回答,或者至少评论说您编辑了它。

标签: java ssl rmi keystore public-key-encryption


【解决方案1】:

PKIX 错误意味着客户端不信任服务器证书,本例中的服务器是注册表。

为了澄清,您需要两个私钥和两个密钥库来保存它们,各一个。然后,您需要在每个密钥库中创建证书,导出它们,然后将它们导入到对等点的信任库中。服务器的信任库必须信任客户端的密钥库,反之亦然。

您的代码看起来基本没问题。 createRegistry() 的结果应该存储在一个静态变量中,以防止它被 GC'd。无论您的 IDE 告诉您什么,您都不需要服务器类中的 serialVersionUID。它不会被序列化,至少 RMI 不会。

编辑问题出在:

System.setProperty("javax.net.ssl.keyStore", "C:\\ssl\\keystore-server.jks");
System.setProperty("javax.net.ssl.trustStore", "C:\\ssl\\truststore-client.jks");

应该是:

System.setProperty("javax.net.ssl.keyStore", "C:\\ssl\\keystore-server.jks");
System.setProperty("javax.net.ssl.trustStore", "C:\\ssl\\truststore-server.jks");

这里:

System.setProperty("javax.net.ssl.keyStore", "C:\\ssl\\keystore-client.jks");
System.setProperty("javax.net.ssl.trustStore", "C:\\ssl\\truststore-server.jks"

应该是:

System.setProperty("javax.net.ssl.keyStore", "C:\\ssl\\keystore-client.jks");
System.setProperty("javax.net.ssl.trustStore", "C:\\ssl\\truststore-client.jks"

编辑 2 根本问题是绑定到 Registry 时需要的信任库是客户端信任库,但运行服务器时需要的信任库是服务器信任库。

至少有三种可能的解决方案,按优点升序排列:

  1. 设置SslRMIClientSocketFactory 的子类及其自己的SSLContext,并从客户端信任库加载自己的TrustManager,并覆盖createSocket()。哎哟。

  2. 将服务器的证书也导入到服务器的信任库中。

  3. 使用createRegistry()的返回值做bind(),而不是在服务器中调用getRegistry(),避免整个问题。

【讨论】:

  • 感谢 EJP。我尝试重新生成密钥,这次将导出的证书导入到对面的信任库中。我现在收到signature check failed 错误,已启用更多 ssl 日志记录,因此此处提供更多详细信息:pastebin.com/yNaKJN2N(底部错误!)。我使用以下脚本生成密钥/导出/导入等:pastebin.com/Vn2uq0Vb。有什么想法吗?
  • 你一定做错了什么。您能否发布您为服务器密钥库和客户端信任库采取的确切步骤?注意您的代码表明您正在使用客户端密钥库和服务器信任库:这是不正确的,它应该是客户端信任库,这就是您应该将服务器证书导出到的那个。
  • 这是我链接到的 bat 脚本的输出,因此您可以看到我的步骤,据我所知一切正常。 pastebin.com/dnTJiGrz。此外,我编辑了我的帖子以反映当前代码(使用 bat 脚本生成的文件名)。
  • EJP,我不敢相信事情就这么简单——你真的是个传奇。我使用了 createRegistry() 的返回而不是调用 getRegistry() 并且它很有魅力。更好的是,我实际上理解了这个问题! :)
  • 又一个小问题。每个客户端是否应该拥有唯一的密钥/证书,而不是每个客户端都拥有相同的密钥对。你将如何促进这一点?也许在服务器上生成它们然后下载?
猜你喜欢
  • 1970-01-01
  • 2011-06-14
  • 1970-01-01
  • 1970-01-01
  • 2022-01-15
  • 2016-10-12
  • 2023-03-26
  • 1970-01-01
  • 2013-01-08
相关资源
最近更新 更多