2

I'm tried and read a number of different things but have to concede I can't figure this out and couldn't find anything on point out there.

I'm trying to code a Java client app that can make SSL connections with different servers (using different keys) during a single session. The desired workflow is:

1a) Client makes SSL connection (ssl1) with Server 1 based on keystore 1 and certificate 1 1b) Client and Server 1 exchange data and then close ssl1 2a) Client makes SSL connection (ssl2) with Server 2 based on keystore 2 and certificate 2 2b) Client and Server 2 exchange data and then close ssl2

I can't implement this workflow. My Client can do 1 or 2 above in a given session but when I try to do 1 and 2 in the same session I get the error below when the handshake for the 2nd connection is attempted:

javax.net.ssl.SSLException: Connection has been shutdown: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: signature check failed

So obviously the certificate for the 2nd connection is not being accepted/recognized. The following explains my Client's code.

First, I build a List containing 2 KeyManagerFactory instances - 1 for each connection. Here's the method I do this in - the ksFileList array contains a value for the file path for each jks file involved in the two connections. keyManagerFactoryList contains the 2 KeyManagerFactory instances.

public static List getKeyManagerFactories(String[] ksFileList) {
    List keyManagerFactoryList = new ArrayList<KeyManagerFactory>();
    String ksFile;
    for (int i = 0; i < ksFileList.length; i++) {
        ksFile = ksFileList[i];
        try {
            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream(ksFile), "passdemo".toCharArray());
            KeyManagerFactory factory = 
                 KeyManagerFactory.getInstance("SunX509");       
            factory.init(ks, "passdemo".toCharArray());
            keyManagerFactoryList.add(factory);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return keyManagerFactoryList;
}    

Second, I build a List containing 2 TrustManagerFactory instances - 1 for each certificate I'm trying to use to connect. Here's the method I do this in - the ksFileList array is the same as above. trustManagerFactoryList contains the 2 TrustManagerFactory instances needed for the connections.

public static List getTrustManagerFactories(String[] ksFileList) {
    List trustManagerFactoryList = new ArrayList<KeyManagerFactory>();
    String ksFile;
    for (int i = 0; i < ksFileList.length; i++) {
        ksFile = ksFileList[i];
        try {
            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream(ksFile), "passdemo".toCharArray());
            TrustManagerFactory factory = 
                 TrustManagerFactory.getInstance("SunX509");     
            factory.init(ks);
            trustManagerFactoryList.add(factory);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    return trustManagerFactoryList;
}

Once I get the above lists I pass them both to methods called testServerA and testServerB to perform the desired workflow. The methods try to make SSL connections to different running Servers. After completing the handshake, the connected to Server sends a message to the Client that is printed out. The code for testServerA is below:

public static void testServerA(List keyManagerFactoryList, List trustManagerFactoryList) {
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    SSLSocketFactory f = (SSLSocketFactory) SSLSocketFactory.getDefault();
    try {

        SSLContext sc = SSLContext.getInstance("TLS"); 

        KeyManagerFactory kmf = (KeyManagerFactory)keyManagerFactoryList.get(0);
        TrustManagerFactory tmf = (TrustManagerFactory)trustManagerFactoryList.get(0);  
        sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

        SSLSocket client = (SSLSocket) f.createSocket("localhost", 7777);
        printSocketInfo(client);
        client.startHandshake();

        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
        BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
        String content;

        while ((content=reader.readLine())!= null) {

            System.out.println("Client: " + content);
            writer.newLine();
            writer.flush();                 
            writer.close();
            reader.close();
            client.close();             
        }
    } catch (Exception e) {
        System.err.println(e.toString());
    }       
}

The testServerB method is analogous to the above except 1) variables "kmf" and "tmf" above are set using the objects at index 1 of the manager factory array lists instead of those at index 0 and 2) SSLSocket client is created for port 8888 on the local host.

When my Client executes testServerA and testServerB at runtime, whichever method is called first works and the second one causes the CertPathValidatorException provided above. The exception confirms the connections is failing in the handshake phase.

Unfortunately, the code provided above was my be best attempt at the desired workflow and I am at a loss on how to move forward. My best guess is that my Client is failing because I am using an SSLContext instance's "init" method to set the KeyManager and TrustManager objects I want for each connection. While this approach is working for the first connection, the second connection fails because the KeyManager and/or TrustManager objects from the first connection are not being updated correctly. Another guess is that the SSLContext "init" method can only be called effectively once in a given session. Are either of my guesses right in anyway?

Thanks for your attention - I'd appreciate any advice or ideas I can get.

-----Supplemental Post 1-----------------

Here is the error output (sorry i did not format it) by my Client app when I try to make both server connections. Note the "op by DCLKS" print out which is the content sent by Server 2 (or B) to the Client after a successful handshake.

enter image description here

Here is the error output by my Client app when I try to make both server connections and I use the getDefault call suggested below...

enter image description here

In this case, my Client did not receive the content sent by Server 2/B (SocketException is thrown instead) even through the Client and Server B completed their handshake.

ad479
  • 33
  • 6
  • Your hunch seems correct based on what your are observing. We had a similar problem but Axis2 was a way that let us get around this limitation. – Tim Biegeleisen Jul 27 '15 at 05:13
  • A few points of note re the above...1) the fact that I instantiate the ArrayList for KeyManagerFactory instances in getTrustManagerFactory does not effect my Client's output...2) When I print out the server socket info for the second connection (before the handshake) the values for "Cipher suite" and "Protocol" are "SSL_NULL_WITH_NULL_NULL" and "NONE">. This is in contrast to info printed on the first server socket connected to where the values are "TLS..." and "TLSv1.2". – ad479 Jul 27 '15 at 05:21
  • Don't post pictures of text. Waste of your time and our bandwidth. Post the text. – user207421 Jul 27 '15 at 08:32

0 Answers0