Java RMI客户端网络连接拒绝疑难解答

时间:2012-03-07 22:36:44

标签: java ssl client rmi

以前从未奏效 - 我是第一次设置环境。

以下多个更新

我已经在"守护进程中运行了我的RMI服务器"因为我希望它在系统启动时像服务一样运行(不使用xinitd功能),所以我可以使用SSL,因此我可以通过一个单独的机制向守护进程发出与进入注册表有关的命令。 (我,我没有使用Runtime.getRuntime().exec("rmiregistry 2020");

之类的东西

我知道RMI服务器已启动并正在运行,因为它可以工作:

telnet localhost <port>

我的客户端代码将许多用于SSL的环境变量编码到命令行中以启动它 - 稍后,我将以编程方式执行此操作,但是现在,它始于:

$ java -Djavax.net.ssl.trustStore=/var/RMIssl/truststore -Djavax.net.ssl.trustStorePassword=<pwd> -Djava.rmi.server.hostname=localhost my.package.Test

起初我得到的只是:

java.rmi.ConnectException: Connection refused to host: localhost; nested excepti
on is: 
        java.net.ConnectException: Connection refused

除了有错误外几乎没用。所以,我仔细检查了各种方式,最后输入了一些跟踪语句,并使用刷新,并将其添加到命令行:

-Djavax.net.debug=all

这有点难以理解,但仍然难以找出错误的位置,因此我将可疑线路置于循环中,捕获异常,现在它已清楚它的查找对于注册表中特定对象的问题,尽管错误消息肯定会产生误导。这就是我得到的:

$ java -Djavax.net.debug=all -Djavax.net.ssl.trustStore=/var/RMIssl/truststore -Djavax.net.ssl.trustStorePassword=<pwd> -Djava.rmi.server.hostname=localhost my.package.Test
Setup the security manager.
Find the registry.
Lookup testClient.
keyStore is : 
keyStore type is : jks
keyStore provider is : 
init keystore
init keymanager of type SunX509
trustStore is: /var/RMIssl/truststore 
trustStore type is : jks
trustStore provider is : 
init truststore
adding as trusted cert:
  Subject: CN=   <my cert>
   Issuer: CN=   <my cert>
  Algorithm: RSA; Serial number: 0x<number>
  Valid from  <dates>

trigger seeding of SecureRandom
done seeding SecureRandom
Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256
Ignoring unavailable cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unavailable cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unavailable cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256
java.rmi.ConnectException: Connection refused to host: localhost; nested exception is: 
        java.net.ConnectException: Connection refused
enter 'quit' to exit:
Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256
Ignoring unavailable cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unavailable cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unavailable cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256
java.rmi.ConnectException: Connection refused to host: localhost; nested exception is: 
        java.net.ConnectException: Connection refused
enter 'quit' to exit: 

请注意,循环结构可以更好地分析错误;关于信任库和我的证书的输出没有重复。

这告诉我,它没有找到合适的密码套件来使用,但这都在同一个系统上!客户端和服务器都在寻找相同的信任库等。

如果有人认为它有用,请点击以下客户端代码:

import java.rmi.*;
import java.rmi.registry.*; 
import java.io.*;
import java.net.*;
import javax.rmi.ssl.*;

public class Test 
{   
   private static String ServerHost = "localhost";
   private static int ServerPort = 6543; private static Registry registry;
   private static testClient c;
   public static void main(String[] args) throws Exception 
   {
      msg("Setup the security manager.");
      System.setSecurityManager(new RMISecurityManager());

      msg("Find the registry.");
      registry = LocateRegistry.getRegistry(ServerHost, ServerPort,
       new SslRMIClientSocketFactory());
      msg("Lookup testClient.");
      boolean keepLooping = true;
      while (keepLooping)
      {
         try
         {
            c = (testClient)registry.lookup(testClient.class.getSimpleName());
         }
         catch (Exception e) 
         {
            msg(e.toString());
         }
         keepLooping = !(Prompt("enter 'quit' to exit: ").equals("quit"));
      }
   }
   public static void msg(String message) 
   {
      System.out.println(message); System.out.flush();
   }
   public static String Prompt(String prompt) 
   {
      String out = ""; 
      BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
       try 
      {
         System.out.print(prompt);
         out = br.readLine(); 
      }
      catch (IOException e) 
      {
         msg("IO error reading from standard-in: "+e.toString()); 
      } 
      return out; 
   } 
}

更新一次:

在我自己的预感之后,我发现了用于设置SslRMI套接字工厂中使用的密码套件的环境变量。所以,我把它添加到服务器端:

javax.rmi.ssl.client.enabledCipherSuites="TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,TLS_DHE_DSS_WITH_AES_256_CBC_SHA,TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA256"

这实际上是整个列表中的任何一个&#34;不可用&#34;或者&#34;不支持&#34; - 也许它有点矫枉过正,但我​​希望服务器能够接受客户端可用的任何内容。

这大大改变了输出!但是,它仍然挂在同一个声明上,这一次,关闭跟踪,我们得到:

java.rmi.ConnectIOException: error during JRMP connection establishment; nested exception is: 
        javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake

当我添加跟踪时,有很多关于实际选择了哪个协议以及它正在做什么的数据,它最终会达到此目的(在此摘录之前严重裁剪):

...
0080: 00 0F 00 10 00 11 00 02   00 12 00 04 00 05 00 14  ................
0090: 00 08 00 16 00 0B 00 02   01 00                    ..........
main, received EOFException: error
main, handling exception: javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
main, SEND TLSv1 ALERT:  fatal, description = handshake_failure
main, WRITE: TLSv1 Alert, length = 2
[Raw write]: length = 7
0000: 15 03 01 00 02 02 28                               ......(
main, called closeSocket()
java.rmi.ConnectIOException: error during JRMP connection establishment; nested exception is: 
        javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake

更新二:

根据EJP的建议,我从他对其他人的回答中尊重这个话题,我测试了服务器没有真正运行注册表的前提,或者至少不是系统/端口组合客户正在寻找的地方。

为了测试这个假设,我只需启动和停止服务器,客户端行为就会发生很大变化。在它没有运行的情况下,我会交替进行长时间的阻止&#34;忽略不可用&#34;或&#34;忽略不受支持的&#34;正如我在上面的第一个案例中所报告的那样,密码套件,后面跟着它们的名字。

当服务器运行时,客户端会给出一个非常不同的响应,向我发送有关加密连接细节的非常详细的信息,正如我在上面的第一次更新中所报告的那样。而且,它说远程主机在握手期间关闭了连接,所以当它掉线时显然正在进行握手。另外,我明白了:

$ netstat -n -l | grep <my port number>
tcp        0      0 :::<my port number>         :::*                        LISTEN

我认为这是决定性的......

更新三:

我注意到输出有所改变,所以我应该澄清一下,等等。当我测试的时候,我确实在我的系统上从我的注册表中找到了我的端口上的监听器(希望如此) )正确启动,使用与客户端相同的信任库和密钥库文件,启动客户端并从SSL获取大量跟踪信息,包括上面提到的密码列表,然后,它列出了一个RandomCookie,空会话ID,另一个密码列表,一些椭圆曲线,并转储一堆字节,这是以下文本选择的地方:

0080: 00 0F 00 10 00 11 00 02   00 12 00 04 00 05 00 14  ................
0090: 00 08 00 16 00 0B 00 02   01 00                    ..........
[Raw read]: length = 5
0000: 15 03 01 00 02                                     .....
[Raw read]: length = 2
0000: 02 28                                              .(
main, READ: TLSv1 Alert, length = 2
main, RECV TLSv1 ALERT:  fatal, handshake_failure
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
java.rmi.ConnectIOException: error during JRMP connection establishment; nested exception is: 
        javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

如果我们收到关于握手被丢弃的干净错误消息,那肯定会很好。不过,我还没弄明白如何从服务器获得类似的输出 - 那会是什么?我已经提供了javax.net.debug = all?!?!

更新四次

我想知道我在RMI注册表中注册的对象是否存在问题。我的原始设计是创建两个对象接口。第一个被实例化并注册。它的工作是创建实例化但未注册的其他对象;客户端从注册表中获取第一个对象并在第二个对象中执行该对象,该实例对该客户端是私有的。然后,客户端调用第二个对象公开的方法,这些方法在服务器端执行,并返回结果。

为了确保在这里没有发挥作用,我创建了以下内容。但是请注意,这并没有改变任何东西 - 它只是缩短了很多,因此更适合包括在这里进行全面披露:

package <myPackage>;

import <myPackage>.*;
import java.rmi.*;

public interface testobject extends Remote
{
   myAPI.Person getObject() throws RemoteException;
}

然后我使用了界面:

package <myPackage>;

import <myPackage>.myAPI;
import <myPackage>.testobject;
import java.rmi.*; 
import java.rmi.server.*; 
public class testObject extends UnicastRemoteObject implements testobject
{

public testObject() throws RemoteException  
{
   // Set the security manager to the RMI security manager, to prevent
   // the client process from using the server in a malicious way.
   try
   {
      if (System.getSecurityManager() == null)
      {
         System.setSecurityManager(new RMISecurityManager());
      }
   }
   catch(Exception e)
   {
      System.out.println("SecurityManager load error: \n"+e);
   }
}

public myAPI.Person getObject() throws RemoteException
{
   myAPI c = new myAPI();
   myAPI.Person TestObject = c.newPerson();
   TestObject.FirstName = "Test";
   TestObject.LastName = "Object";
   return TestObject;
}

}

然后,我使用以下行更新了服务器代码:

   try
   {
      testObject TestObject = new testObject();
      OK = true;
   }
   catch (RemoteException e)
   {
      msg("Error instantiating TestObject: \n"+e.toString());
   }
   if (OK)
   {
      name = testObject.class.getSimpleName();
      try
      {
         registry.bind(name, ServerProcess);
         msg("New testObject has been bound to the RMI registry.");
      }
      catch(Exception e)
      {
         msg("The new testObject could not be bound to the RMI registry: \n"+e.toString());
         OK = false;
      }
   }

最后客户端:

 testObject to;
 try
 {
    msg("Now trying a 'testObject'...");
    to = (testObject)registry.lookup(testObject.class.getSimpleName());
    msg("Well! WE DID NOT THROW AN EXCEPTION!");
    person = to.getObject();
    msg("Name: "+person.FirstName+" "+person.LastName);
 }
 catch (Exception e) 
 {
    msg(e.toString());
 }

不幸的是,它仍然会抛出(相同)异常。

还在,&#34;嗯......&#34;

0 个答案:

没有答案