Search code examples
sslencryptionwebspherejsseoutbound

Specifying an outbound SSL configuration programmatically using JSSEHelper on Websphere 8.0. does not work


I am trying to create an SSL connection programmatically using a CUSTOM outbound ssl configuration configured in WAS 8.0.0.13 (IBM Websphere application server that uses java 1.6.0): (Security->SSL certificate and key managemement->Related Items:SSL configurations). The secure connection has been created successfully:a servlet that resides on the WAS server has connected to a server-side Socket listening on 127.0.0.1:1234. The problem is that my preferred cipher suites defined in 'Quality of Protection (QoP) settings' within SSL configuration are ignored. All the other properties (such as protocol or JSSE provider) are nicely regarded.

I have implemented a Servlet which was in the role of an SSL-client. This Servlet used this custom SSL configuration which had the following cipher suites defined:

  1. SSL_RSA_WITH_AES_128_CBC_SHA
  2. SSL_DHE_RSA_WITH_AES_128_CBC_SHA
  3. SSL_DHE_DSS_WITH_AES_128_CBC_
  4. SHA SSL_RSA_WITH_AES_128_GCM_SHA256
  5. SSL_RSA_WITH_AES_128_CBC_SHA256
  6. SSL_DHE_RSA_WITH_AES_128_GCM_SHA256
  7. SSL_DHE_RSA_WITH_AES_128_CBC_SHA256
  8. SSL_DHE_DSS_WITH_AES_128_GCM_SHA256
  9. SSL_DHE_DSS_WITH_AES_128_CBC_SHA256

Unfortunately, a different list of cipher suites has been provided in ClientHello request:

  1. SSL_RSA_WITH_AES_128_CBC_SHA
  2. SSL_DHE_RSA_WITH_AES_128_CBC_SHA
  3. SSL_DHE_DSS_WITH_AES_128_CBC_SHA
  4. SSL_RSA_WITH_3DES_EDE_CBC_SHA
  5. SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA
  6. SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA
  7. SSL_RSA_WITH_DES_CBC_SHA
  8. SSL_DHE_RSA_WITH_DES_CBC_SHA
  9. SSL_DHE_DSS_WITH_DES_CBC_SHA
  10. SSL_RENEGO_PROTECTION_REQUEST

(This custom SSL configuration contained TLSv1.1 protocol in its definition.)

I have also tried another protocol (TLSv1.2) with a smaller set of cipher suites:

  1. SSL_RSA_WITH_AES_128_CBC_SHA
  2. SSL_DHE_RSA_WITH_AES_128_CBC_SHA
  3. SSL_DHE_DSS_WITH_AES_128_CBC_SHA
  4. SSL_DHE_DSS_WITH_AES_128_CBC_SHA256

Once again, a different list of cipher suites was provided in ClientHello request:

  1. SSL_RSA_WITH_AES_128_CBC_SHA
  2. SSL_DHE_RSA_WITH_AES_128_CBC_SHA
  3. SSL_DHE_DSS_WITH_AES_128_CBC_SHA
  4. SSL_RSA_WITH_3DES_EDE_CBC_SHA
  5. SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA

Moreover, I have also checked cell-default cipher suites and node-default-cipher-suites and there is no match between them and those provided in the ClientHello: Default Node Configuration/Default Cell Configuration:

  1. SSL_RSA_WITH_AES_128_CBC_SHA
  2. SSL_DHE_RSA_WITH_AES_128_CBC_SHA
  3. SSL_DHE_DSS_WITH_AES_128_CBC_SHA
  4. SSL_RSA_WITH_AES_128_GCM_SHA256
  5. SSL_RSA_WITH_AES_128_CBC_SHA256
  6. SSL_DHE_RSA_WITH_AES_128_GCM_SHA256
  7. SSL_DHE_RSA_WITH_AES_128_CBC_SHA256
  8. SSL_DHE_DSS_WITH_AES_128_GCM_SHA256
  9. SSL_DHE_DSS_WITH_AES_128_CBC_SHA256

I have followed these instructions: https://www.ibm.com/support/knowledgecenter/en/SSAW57_8.0.0/com.ibm.websphere.nd.doc/info/ae/ae/tsec_ssloutconfiguseJSSE.html

and have created the following implementation. 'doGet' method is an entry point:

public class TLSv1_1 extends HttpServlet {
private static final long serialVersionUID = 1L;
com.ibm.websphere.ssl.JSSEHelper jsseHelper;

public TLSv1_1() {
    super();
    jsseHelper = com.ibm.websphere.ssl.JSSEHelper.getInstance();
}

protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

    Properties existing_sslProps = null;
    try {
        String existingAlias = "TLSv1.1";
        existing_sslProps = jsseHelper.getProperties(existingAlias);

    } catch (com.ibm.websphere.ssl.SSLException e) {
        e.printStackTrace();
    }
    printSSLproperties(response, existing_sslProps);
    SSLSocket socket = getSslSocket(existing_sslProps);
    writeToSocket(socket, 1234);
}

public static void printSSLproperties(HttpServletResponse response, Properties sslProps) throws IOException {
    if (sslProps != null) {
        StringBuilder sb = new StringBuilder();
        Set set = sslProps.entrySet();
        Iterator it = set.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            Object value = entry.getValue();
            sb.append("key: " + entry.getKey() + ", value: " + value + "\n");
        }
        System.out.println("sslProps:    -----------\n" + sb.toString());           
    } else {
        System.out.println("sslProps == null");
        response.getWriter().append("sslProps == null");
    }
}

public SSLSocket getSslSocket(Properties sslProps) {
    Map<String, Object> sslMap = new HashMap<String, Object>();

    sslMap.put("com.ibm.ssl.direction", "outbound");
    sslMap.put("com.ibm.ssl.remoteHost", "127.0.0.1");
    sslMap.put("com.ibm.ssl.remotePort", "1234");
    sslMap.put("com.ibm.ssl.endPointName", "HTTP");
    SSLSocketFactory sslSocketFactory = null;
    try {
        sslSocketFactory = jsseHelper.getSSLSocketFactory(sslMap, sslProps);
    } catch (SSLException e) {
        e.printStackTrace();
    }

    SSLSocket socket = null;
    try {
        socket = (SSLSocket) sslSocketFactory.createSocket();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return socket;
}

public static void writeToSocket(Socket socket, int port) throws IOException, UnknownHostException {

    InetAddress address = InetAddress.getByName("127.0.0.1");
    socket.connect(new InetSocketAddress(address, port));
    BufferedWriter stream = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

    for (int i = 0; i < 3; i++) {
        String lineX = UUID.randomUUID().toString();
        stream.write(lineX);
        stream.newLine();
        stream.flush();
        System.out.println("NEW LINE SUCCESSFULLY WRITTEN INTO SOCKET:" + lineX);
        sleep();
    }
}

private static void sleep() {

    try {
        Thread.sleep(1000 * 30);
    } catch (InterruptedException e) {

    }
} 

}

The presence of the hash map sslMap seems to be of no importance. It does not matter whether is set to null or contains no values.

I have also tried to enforce ssl properties on thread (this one has the highest preference among all the others): This approach also dodn't worked:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

String existingAlias = "TLSv1.1";
existing_sslProps = jsseHelper.getProperties(existingAlias);

jsseHelper.setSSLPropertiesOnThread(existing_sslProps);

CommonIO.printSSLproperties(response, existing_sslProps);       
SSLSocket socket = getSslSocket(existing_sslProps);
CommonIO.writeToSocket(socket, 1234);

jsseHelper.setSSLPropertiesOnThread(null);
}

Finally, I have tried not to bind to any SSL configuration present in WAS server SSL configs, but only wire up a custom configuration with my java code:

sslProps.setProperty("com.ibm.ssl.protocol", "TLSv1.1");
    sslProps.setProperty("com.ibm.ssl.enabledCipherSuites",
            "SSL_DHE_DSS_WITH_AES_128_CBC_SHA SSL_DHE_DSS_WITH_AES_128_GCM_SHA256 SSL_DHE_DSS_WITH_AES_128_CBC_SHA256");
    sslProps.setProperty("com.ibm.ssl.trustStore",
            "/opt/IBM/Websphere/profiles/AppSrv01/config/cells/localhostCell01/nodes/localhostNode01/trust.p12");
    sslProps.setProperty("com.ibm.ssl.trustStorePassword", "***");
    sslProps.setProperty("com.ibm.ssl.trustStoreType", "PKCS12");
    sslProps.setProperty("com.ibm.ssl.keyStore",
            "/opt/IBM/Websphere/profiles/AppSrv01/config/cells/localhostCell01/key.p12");
    sslProps.setProperty("com.ibm.ssl.keyStorePassword", "***");
    sslProps.setProperty("com.ibm.ssl.keyStoreType", "PKCS12");

    sslProps.setProperty("security.provider.1", "com.ibm.jsse2.IBMJSSEProvider2");
    sslProps.setProperty("ssl.SocketFactory.provider", "com.ibm.jsse2.SSLSocketFactoryImpl");

But this approach also did not work. Could you please help me? I suppose I missed something principal or custom SSL configuration does not exist in this product.


Solution

  • So due to my not looking at your code carefully initially. I see the problem. Because you get the Socket factory directly from the JSSEHelper, we are not getting the chance to put the ciphers on the socket.

    In your case you should follow the WAS's programmatic SSL methods. Get the properties and put them on the thread. eg

     try {
            String existingAlias = "TLSv1.1";
            existing_sslProps = jsseHelper.getProperties(existingAlias);
        jsseHelper.setSSLPropertiesOnThread(existing_sslProps);
    
        } catch (com.ibm.websphere.ssl.SSLException e) {
            e.printStackTrace();
        } 
    

    The later don't get the Socket factory from the JSSE, get the default. So in getSslSocket() do something like:

    public SSLSocket getSslSocket(Properties sslProps) {
    
        SSLSocketFactory factory = SSLSocketFactory.getDefault();
    
        SSLSocket socket = null;
        try {
            socket = (SSLSocket) factory.createSocket();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return socket;
    }
    

    This will get you a WAS socket factory. The create socket call should return you a socket with the ciphers set on the SSLSocket. When you are done you should clear the properties off the thread.

    jsseHelper.setSSLPropertiesOnThread(null);