在 Websphere 8.0 上使用 JSSEHelper 以编程方式指定出站 SSL 配置。不起作用

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

我正在尝试使用在 WAS 8.0.0.13(使用 java 1.6.0 的 IBM Websphere 应用程序服务器)中配置的自定义出站 ssl 配置以编程方式创建 SSL 连接: (安全->SSL 证书和密钥管理->相关 Items:SSL 配置)。 已创建安全连接 successfully:a 驻留在 WAS 服务器上的 servlet 已连接到侦听 127.0.0.1:1234 的服务器端套接字。 问题是我在 SSL 配置中 'Quality of Protection (QoP) settings' 中定义的首选密码套件被忽略了。 所有其他属性(例如协议或 JSSE 提供程序)都很好。

我实现了一个充当 SSL 客户端角色的 Servlet。 此 Servlet 使用此自定义 SSL 配置,其中定义了以下密码套件:

  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

很遗憾,ClientHello 请求中提供了不同的密码套件列表:

  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

(此自定义 SSL 配置在其定义中包含 TLSv1.1 协议。)

我还尝试了另一种协议 (TLSv1.2) 和一组较小的密码套件:

  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

ClientHello 请求中再次提供了不同的密码套件列表:

  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

此外,我还检查了cell-default cipher suites和node-default-cipher-suites,没有 它们与 ClientHello 中提供的匹配: 默认节点 Configuration/Default 单元格配置:

  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

我已遵循以下说明: https://www.ibm.com/support/knowledgecenter/en/SSAW57_8.0.0/com.ibm.websphere.nd.doc/info/ae/ae/tsec_ssloutconfiguseJSSE.html

并创建了以下实现。 'doGet'方法是一个入口点:

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) {

    }
} 

}

哈希映射 sslMap 的存在似乎并不重要。 设置为 null 或不包含任何值都没有关系。

我还尝试在线程上强制执行 ssl 属性(这个在所有其他线程中具有最高的偏好): 这种方法也行不通:

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);
}

最后,我尝试不绑定到 WAS 服务器 SSL 配置中存在的任何 SSL 配置,而只使用我的 java 代码连接自定义配置:

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");

但是这个方法也行不通。 请你帮助我好吗?我想我错过了一些主体或自定义 SSL 配置在该产品中不存在。

感谢您的提示!我遵循了这些说明:

https://www-01.ibm.com/support/docview.wss?uid=swg21162961

根据您的提示提高 log/trace 等级。 如果我必须进行一些其他配置更改,请告诉我。

我只从这些子目录中收集了日志文件:

  1. ../logs/server1
  2. ../logs/nodeagent
  3. ../logs/ffdc 并将它们放入这些文件中的 ALL_LOGS/logs 目录中:

https://drive.google.com/open?id=18TMYyjKx8L_pd8TxFG1uq1rOmikVyWeg

,所以如果在不同位置还有其他 log/trace 文件,请告诉我。 (只有 Delta 存在于这些文件中,因为我清除了所有 log/trace 文件 在启动服务器并重新测试我的场景之前。)

我在 ffdc 日志中只找到了这个:

The client and server could not negotiate the desired level of security.Reason: Received fatal alert: handshake_failure vmcid: 0x49421000 minor code: 70 completed: No

我不确定这是否是我问题的根本原因,但没有 google 结果似乎与我的问题相关。 就 google 结果而言,我的意思是这些:

https://www.ibm.com/developerworks/community/forums/html/topic?id=a2910c33-8f55-4ef7-823d-7ae367682e35

http://www.dsxchange.com/viewtopic.php?t=134492&sid=f6e236a4f14a9d80fc51c0820e5f7ce7 None 其中有帮助...

除了日志子目录,我还在 TLSv1.1_enforce_OnThread.log 中附加服务器端套接字标准输出和标准错误。 此外,我还附加了客户端配置 CONFIG_TLSv1.1._ENFORSE_SSL_ON_THREAD.png

你能检查一下这些日志文件吗? 非常感谢。

-- 迈克尔

我正在查看涉及使用名为 "TLSv1.1" 的 SSL 配置的错误。 TLSv1.1 不支持您配置的 3 个密码中的 2 个,您可以在此处 https://www.ibm.com/support/knowledgecenter/es/SSYKE2_6.0.0/com.ibm.java.security.component.60.doc/security-component/jsse2Docs/ciphersuites.html?view=embed 找到有关 IBM java6 支持的密码的更多信息。 这给你留下了一个密码,SSL_DHE_DSS_WITH_AES_128_CBC_SHA。

[12/17/17 6:16:19:524 EST] 00000000 SystemOut     O   Ignoring unsupported cipher suite: SSL_DHE_DSS_WITH_AES_128_GCM_SHA256
[12/17/17 6:16:19:524 EST] 00000000 SystemOut     O   Ignoring unsupported cipher suite: SSL_DHE_DSS_WITH_AES_128_CBC_SHA256
[12/17/17 6:16:19:527 EST] 00000000 SystemOut     O   %% No cached client session
[12/17/17 6:16:19:528 EST] 00000000 SystemOut     O   *** ClientHello, TLSv1.1
[12/17/17 6:16:19:528 EST] 00000000 SystemOut     O   RandomCookie:  GMT: 1513509379 bytes = { 108, 16, 192, 144, 124, 116, 226, 48, 69, 61, 93, 187, 104, 67, 120, 166, 233, 194, 67, 244, 136, 159, 105, 130, 106, 175, 18, 251 }
[12/17/17 6:16:19:529 EST] 00000000 SystemOut     O   Session ID:  {}
[12/17/17 6:16:19:529 EST] 00000000 SystemOut     O   Cipher Suites: [SSL_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_RENEGO_PROTECTION_REQUEST] 

此连接将连接到同一主机 127.0.0.1 上的端口 9202。最终以错误结束,javax.net.ssl.SSLHandshakeException:收到致命警报:handshake_failure。我认为您的服务器正在尝试使用错误的 SSL 配置与您的节点代理通信。它似乎是从动态出站选择过滤器中选择该配置的。

[12/17/17 6:16:19:440 EST] 00000000 SSLConfigMana 3   SSLConfig dynamic selection info: *,127.0.0.1,*
[12/17/17 6:16:19:440 EST] 00000000 SSLConfigMana 3   Parsing entry 0 of 1: *,127.0.0.1,*
[12/17/17 6:16:19:440 EST] 00000000 SSLConfigMana 3   This entry has 3 attributes.
[12/17/17 6:16:19:440 EST] 00000000 SSLConfigMana 3   Protocol: *, Host: 127.0.0.1, Port: *
[12/17/17 6:16:19:440 EST] 00000000 SSLConfigMana 3   Found a dynamic selection match!
[12/17/17 6:16:19:440 EST] 00000000 SSLConfigMana <  getPropertiesFromDynamicSelectionInfo -> found. Exit

您的过滤器将匹配任何进入 127.0.0.1 的内容,以使用名为 "TLSv1.1" 的 SSL 配置。这个连接似乎使用的是 TLSv1 协议。因此,由于协议不匹配,连接失败。似乎此 SSL 配置不打算用于访问 127.0.0.1 和端口 9202。这可能是您的本地节点代理或 dmgr 端口吗?如果是这样,连接需要 NodeDefaultSSLSettings。

在跟踪中,我看不出什么连接正在与您的服务器、本地主机和端口 1234 通信。也许如果您修复您的配置,以便节点在转到端口 9202 和 127.0 时将使用正确的 SSL 配置。 0.1 可能更容易算出来。

感谢您的大力支持&swift回复! 根据您的输入,我删除了 SSL 配置设置中存在的所有动态出站规则。

我也修改了支持的密码套件列表 在 WAS 端(客户端),基于您提供的 link!:-)。 更准确地说,我只选择了这些套房 因为所有 TLS 协议(TLSv1.0、TLSv1.1、TLSv1.2)都支持它们:

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

我再次清除了所有日志文件,然后启动了应用程序服务器。 重新测试我的方案后,已生成日志文件中的此增量: https://drive.google.com/open?id=19KaDlsx2UVS_YfByaORQOf89mLlJke4e

监听1234端口的服务端应用同样支持 密码套件:

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

当然没想到改完之后问题就解决了 但我预计我会在 logs/traces 中发现一些错误,以减轻强加给您的负担。不幸的是,我没有成功。你能检查一下这些 logs/traces 吗?非常感谢!

(请忽略此异常:

FFDC Exception:java.net.MalformedURLException SourceId:class com.ibm.wkplc.extensionregistry.PluginDescriptor.initFromDom ProbeId:1

存在于 server1_b7f67871_17.12.19_04.44.51.8551967663872103714940.txt 文件中, 因为我再次重新测试它(第 3 次)并且在第 3 次重新测试后没有出现此异常。)

所以由于我最初没有仔细查看您的代码。我看到了问题。因为您直接从 JSSEHelper 获取套接字工厂,所以我们没有机会将密码放在套接字上。

在您的情况下,您应该遵循 WAS 的编程 SSL 方法。获取属性并将它们放在线程上。例如

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

    } catch (com.ibm.websphere.ssl.SSLException e) {
        e.printStackTrace();
    } 

以后不要从JSSE获取Socket工厂,获取默认值。所以在 getSslSocket() 中做类似的事情:

public SSLSocket getSslSocket(Properties sslProps) {

    SSLSocketFactory factory = SSLSocketFactory.getDefault();

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

这将为您提供一个 WAS 套接字工厂。创建套接字调用应该 return 为您提供一个在 SSLSocket 上设置了密码的套接字。完成后,您应该清除线程​​中的属性。

jsseHelper.setSSLPropertiesOnThread(null);