title: Simple two way RMI SSL Connection, PKIX path building failed
body: <p>I have a program, the program has two runnable files one called ServerImpl and the other called ClientImpl.  My aim is to be able to have the ClientImpl connect to the ServerImpl using RMI with SSL and invoke a method.  The server should also be able to call methods on the client using RMI and SSL via a callback.  I can get the client to connect to the server using SSL and invoke methods on that server, however I can't get the Server to connect back to the client using RMI with SSL.</p>
  
  <p>The program also has two directories that contain two different sets of cert, keystore, truststore files:</p>
  
  <pre><code>resources/client/
      client_cert.cer
      client_keystore.jks
      client_truststore.jks
  
   resources/server/
       server_cert.cer
       server_keystore.jks
       server_truststore.jks
  </code></pre>
  
  <p>The ServerImpl.java file:</p>
  
  <pre><code>public class ServerImpl implements ServerInt {
  
      private ClientInt client;
  
      public static void main(String[] args) {
          ServerImpl server = new ServerImpl();
          server.bind();
      }
  
      public void bind() {
          System.setProperty("java.rmi.server.hostname", "192.168.0.32");
          System.setProperty("javax.net.ssl.keyStore", "./resources/server/server_keystore.jks");
          System.setProperty("javax.net.ssl.keyStorePassword", "password");
  
          RMIClientSocketFactory rmiClientSocketFactory = new SslRMIClientSocketFactory();
          RMIServerSocketFactory rmiServerSocketFactory = new SslRMIServerSocketFactory();
  
          try {
              ServerInt si = (ServerInt) UnicastRemoteObject.exportObject(this, 0, rmiClientSocketFactory, rmiServerSocketFactory);
              Registry reg = LocateRegistry.createRegistry(1099);
  
              reg.rebind("server", si);
              System.out.println("RMIServer is bound in registry");
          } catch (RemoteException ex) {
              Logger.getLogger(ServerImpl.class.getName()).log(Level.SEVERE, null, ex);
          }
      }
  
      @Override
      public void connect(ClientInt ci) throws RemoteException {
          System.out.println("client connected");
      }
  
  }
  </code></pre>
  
  <p>The ClientImpl.java file:</p>
  
  <pre><code>public class ClientImpl implements ClientInt {
  
      private ServerInt server;
  
      public static void main(String[] args) {
          ClientImpl client = new ClientImpl();
          client.bind();
          client.initConnection();
          client.connectToServer();
  
      }
  
      public void initConnection() {
          System.setProperty("javax.net.ssl.trustStore", "./resources/client/client_truststore.jks");
          System.setProperty("javax.net.ssl.trustStorePassword", "password");
          try {
              Registry reg = LocateRegistry.getRegistry("192.168.0.32", 1099);
              server = (ServerInt) reg.lookup("server");
  
          } catch (RemoteException ex) {
              Logger.getLogger(ClientImpl.class.getName()).log(Level.SEVERE, null, ex);
          } catch (NotBoundException ex) {
              Logger.getLogger(ClientImpl.class.getName()).log(Level.SEVERE, null, ex);
          }
      }
  
      public void bind() {
          System.setProperty("java.rmi.server.hostname", "192.168.0.32");
          System.setProperty("javax.net.ssl.keyStore", "./resources/client/client_keystore.jks");
          System.setProperty("javax.net.ssl.keyStorePassword", "password");
  
          RMIClientSocketFactory rmiClientSocketFactory = new SslRMIClientSocketFactory();
          RMIServerSocketFactory rmiServerSockeyFactory = new SslRMIServerSocketFactory();
          try {
  
              ClientInt ci = (ClientInt) UnicastRemoteObject.exportObject(this, 0, rmiClientSocketFactory, rmiServerSockeyFactory);
              Registry reg = LocateRegistry.createRegistry(5001);
              reg.rebind("client", ci);
              System.out.println("RMIClient is bound in registry");
          } catch (RemoteException ex) {
              Logger.getLogger(ClientImpl.class.getName()).log(Level.SEVERE, null, ex);
          }
      }
  
      public void connectToServer() {
          try {
              server.connect(this);
          } catch (RemoteException ex) {
              Logger.getLogger(ClientImpl.class.getName()).log(Level.SEVERE, null, ex);
          }
      }
  
      @Override
      public void sayHelloToClient(String helloText) throws RemoteException {
          System.out.println(helloText);
      }
  }
  </code></pre>
  
  <p>I then run the ServerImpl.java file, and no problems it runs fine.  Then I run the ClientImpl.java file and I get an error when I call the connectToServer method:</p>
  
  <pre><code>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
  </code></pre>
  
  <p>Can anyone shed some light on what the problem is here and how I might be able to solve it?  Failing that, can anyone point me towards a good tutorial on having two RMI entities that use SSL to talk to each other?  Thanks.</p>