从 websphere-liberty 16 迁移到版本 17 后出现 SSLHandshakeException

SSLHandshakeException after migration from websphere-liberty 16 to version 17

最近我们开始将我们的应用程序从 websphere-liberty 16.0.0.2 迁移到版本 17.0.0.2(在这两种情况下都使用 javaee7 配置文件)。在 server.xml 中使用相同的 SSL 配置,由于 SSL 握手失败,应用程序无法通过 https 调用远程休息服务。 这是我的 server.xml

<?xml version="1.0" encoding="UTF-8"?>
<server description="Default server">

    <featureManager>
        <feature>appSecurity-2.0</feature>
        <feature>transportSecurity-1.0</feature>
        <feature>jaxrs-2.0</feature>
        <feature>json-1.0</feature>
        <feature>javaMail-1.5</feature>
        <feature>ssl-1.0</feature>
    </featureManager>

    <sslDefault sslRef="defaultSSLSettings" />
    <ssl id="defaultSSLSettings" keyStoreRef="defaultKeyStore" trustStoreRef="defaultTrustStore" clientAuthentication="true" sslProtocol="TLSv1" />
    <keyStore id="defaultKeyStore" location="/opt/ibm/wlp/output/defaultServer/resources/security/key.jks" password="**********" />
    <keyStore id="defaultTrustStore" location="/opt/ibm/wlp/output/defaultServer/resources/security/trust.jks" password="***********" />

    <basicRegistry id="basic" realm="BasicRealm">
        <!-- <user name="yourUserName" password="" />  -->
    </basicRegistry>

    <variable name="defaultHostName" value="default-host.com" />

    <!-- To allow access to this server from a remote client host="*" has been added to the following element -->
    <httpEndpoint id="defaultHttpEndpoint" host="*" httpPort="9080" httpsPort="9443" />

    <!-- Automatically expand WAR files and EAR files -->
    <applicationManager autoExpand="true"/>

    <logging logDirectory="/var/log/wlp" traceSpecification="*=INFO:SSL=all" traceFileName="trace.log" consoleLogLevel="info" copySystemStreams="true"/>

</server>

websphere-liberty v.16 server.xml 的唯一区别是没有启用 transportSecurity-1.0 功能。

应用程序通过 https 调用的 rest 服务提供由 "GeoTrust Global CA" 签名的 SSL 证书。如果将证书导入信任库 'trust.jks' 那么一切正常,但我希望证书在握手期间被自动接受,因为它不是自签名的。

websphere-liberty 17.0.0.2 做了哪些与 SSL 相关的改动? 在 server.xml 中是否应该进行任何额外的安全配置?

服务器版本:

WebSphere Application Server 17.0.0.2 (1.0.17.cl170220170523-1818) on IBM J9 VM, version pxa6480sr4fp10-20170727_01 (SR4 FP10) (en_US)
WebSphere Application Server 16.0.0.2 (1.0.13.cl160220160526-2258) on IBM J9 VM, version pxa6480sr3fp10-20160720_02 (SR3 FP10) (en_US)

错误堆栈跟踪:

server_1  | [INFO    ] FFDC1015I: An FFDC Incident has been created: "java.security.cert.CertPathBuilderException: PKIXCertPathBuilderImpl could not build a valid CertPath.; internal cause is: 
server_1  |     java.security.cert.CertPathValidatorException: The certificate issued by CN=GeoTrust Global CA, O=GeoTrust Inc., C=US is not trusted; internal cause is: 
server_1  |     java.security.cert.CertPathValidatorException: Certificate chaining error com.ibm.ws.ssl.core.WSX509TrustManager checkServerTrusted" at ffdc_17.08.09_14.50.00.0.log
server_1  | [ERROR   ] CWPKI0022E: SSL HANDSHAKE FAILURE:  A signer with SubjectDN CN=*.api.ibm.com, O=International Business Machines, L=Armonk, ST=New York, C=US was sent from the target host.  The signer might need to be added to local trust store /opt/ibm/wlp/output/defaultServer/resources/security/trust.jks, located in SSL configuration alias defaultSSLSettings.  The extended error message from the SSL handshake exception is: PKIX path building failed: java.security.cert.CertPathBuilderException: PKIXCertPathBuilderImpl could not build a valid CertPath.; internal cause is: 
server_1  |     java.security.cert.CertPathValidatorException: The certificate issued by CN=GeoTrust Global CA, O=GeoTrust Inc., C=US is not trusted; internal cause is: 
server_1  |     java.security.cert.CertPathValidatorException: Certificate chaining error
server_1  | [WARNING ] Interceptor for {https://dev.api.ibm.com/scx/test}WebClient has thrown exception, unwinding now
server_1  | Could not send Message.
server_1  | [ERROR   ] 2017-08-09 14:50:00 ExceptionMapper:23 - javax.net.ssl.SSLHandshakeException: SSLHandshakeException invoking https://dev.api.ibm.com/scx/test/customer/321321321: java.security.cert.CertificateException: PKIXCertPathBuilderImpl could not build a valid CertPath.
server_1  | javax.ws.rs.ProcessingException: javax.net.ssl.SSLHandshakeException: SSLHandshakeException invoking https://dev.api.ibm.com/scx/test/customer/321321321: java.security.cert.CertificateException: PKIXCertPathBuilderImpl could not build a valid CertPath.
server_1  |     at org.apache.cxf.jaxrs.client.AbstractClient.checkClientException(AbstractClient.java:632)
server_1  |     at org.apache.cxf.jaxrs.client.AbstractClient.preProcessResult(AbstractClient.java:608)
server_1  |     at org.apache.cxf.jaxrs.client.WebClient.doResponse(WebClient.java:1105)
server_1  |     at org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1042)
server_1  |     at org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:895)
server_1  |     at org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:863)
server_1  |     at org.apache.cxf.jaxrs.client.WebClient.invoke(WebClient.java:426)
server_1  |     at org.apache.cxf.jaxrs.client.WebClient$SyncInvokerImpl.method(WebClient.java:1554)
server_1  |     at org.apache.cxf.jaxrs.client.WebClient$SyncInvokerImpl.method(WebClient.java:1549)
server_1  |     at org.apache.cxf.jaxrs.client.WebClient$SyncInvokerImpl.get(WebClient.java:1469)
server_1  |     at org.apache.cxf.jaxrs.client.spec.InvocationBuilderImpl.get(InvocationBuilderImpl.java:80)
server_1  |     at com.ibm.si.saas.sbs.service.SBSApiService.getCustomer(SBSApiService.java:84)
server_1  |     at com.ibm.si.saas.sbs.resource.SbsEndpointApiResource.getConsoles(SbsEndpointApiResource.java:49)
server_1  |     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
server_1  |     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:90)
server_1  |     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:55)
server_1  |     at java.lang.reflect.Method.invoke(Method.java:508)
server_1  |     at com.ibm.ws.jaxrs20.server.LibertyJaxRsServerFactoryBean.performInvocation(LibertyJaxRsServerFactoryBean.java:632)
server_1  |     at com.ibm.ws.jaxrs20.server.LibertyJaxRsInvoker.performInvocation(LibertyJaxRsInvoker.java:118)
server_1  |     at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:96)
server_1  |     at com.ibm.ws.jaxrs20.server.LibertyJaxRsInvoker.invoke(LibertyJaxRsInvoker.java:252)
server_1  |     at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:189)
server_1  |     at com.ibm.ws.jaxrs20.server.LibertyJaxRsInvoker.invoke(LibertyJaxRsInvoker.java:423)
server_1  |     at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:99)
server_1  |     at org.apache.cxf.interceptor.ServiceInvokerInterceptor.run(ServiceInvokerInterceptor.java:61)
server_1  |     at org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:99)
server_1  |     at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
server_1  |     at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:124)
server_1  |     at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:274)
server_1  |     at com.ibm.ws.jaxrs20.endpoint.AbstractJaxRsWebEndpoint.invoke(AbstractJaxRsWebEndpoint.java:134)
server_1  |     at com.ibm.websphere.jaxrs.server.IBMRestServlet.handleRequest(IBMRestServlet.java:149)
server_1  |     at com.ibm.websphere.jaxrs.server.IBMRestServlet.doGet(IBMRestServlet.java:115)
server_1  |     at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
server_1  |     at com.ibm.websphere.jaxrs.server.IBMRestServlet.service(IBMRestServlet.java:99)
server_1  |     at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1290)
server_1  |     at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:778)
server_1  |     at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:475)
server_1  |     at com.ibm.ws.webcontainer.filter.WebAppFilterManager.invokeFilters(WebAppFilterManager.java:1161)
server_1  |     at com.ibm.ws.webcontainer.webapp.WebApp.handleRequest(WebApp.java:4983)
server_1  |     at com.ibm.ws.webcontainer31.osgi.webapp.WebApp31.handleRequest(WebApp31.java:528)
server_1  |     at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost.handleRequest(DynamicVirtualHost.java:315)
server_1  |     at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:1025)
server_1  |     at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost.run(DynamicVirtualHost.java:280)
server_1  |     at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink$TaskWrapper.run(HttpDispatcherLink.java:967)
server_1  |     at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink.wrapHandlerAndExecute(HttpDispatcherLink.java:359)
server_1  |     at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink.ready(HttpDispatcherLink.java:318)
server_1  |     at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:471)
server_1  |     at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.handleNewRequest(HttpInboundLink.java:405)
server_1  |     at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.processRequest(HttpInboundLink.java:285)
server_1  |     at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.ready(HttpInboundLink.java:256)
server_1  |     at com.ibm.ws.tcpchannel.internal.NewConnectionInitialReadCallback.sendToDiscriminators(NewConnectionInitialReadCallback.java:174)
server_1  |     at com.ibm.ws.tcpchannel.internal.NewConnectionInitialReadCallback.complete(NewConnectionInitialReadCallback.java:83)
server_1  |     at com.ibm.ws.tcpchannel.internal.WorkQueueManager.requestComplete(WorkQueueManager.java:504)
server_1  |     at com.ibm.ws.tcpchannel.internal.WorkQueueManager.attemptIO(WorkQueueManager.java:574)
server_1  |     at com.ibm.ws.tcpchannel.internal.WorkQueueManager.workerRun(WorkQueueManager.java:929)
server_1  |     at com.ibm.ws.tcpchannel.internal.WorkQueueManager$Worker.run(WorkQueueManager.java:1018)
server_1  |     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1160)
server_1  |     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
server_1  |     at java.lang.Thread.run(Thread.java:785)
server_1  | Caused by: javax.net.ssl.SSLHandshakeException: SSLHandshakeException invoking https://dev.api.ibm.com/scx/test/customer/321321321: java.security.cert.CertificateException: PKIXCertPathBuilderImpl could not build a valid CertPath.
server_1  |     at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
server_1  |     at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:83)
server_1  |     at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:57)
server_1  |     at java.lang.reflect.Constructor.newInstance(Constructor.java:437)
server_1  |     at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.mapException(HTTPConduit.java:1385)
server_1  |     at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1369)
server_1  |     at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
server_1  |     at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:653)
server_1  |     at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)
server_1  |     at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
server_1  |     at org.apache.cxf.jaxrs.client.AbstractClient.doRunInterceptorChain(AbstractClient.java:704)
server_1  |     at org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1041)
server_1  |     ... 55 more
server_1  | Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: PKIXCertPathBuilderImpl could not build a valid CertPath.
server_1  |     at com.ibm.jsse2.k.a(k.java:17)
server_1  |     at com.ibm.jsse2.at.a(at.java:851)
server_1  |     at com.ibm.jsse2.D.a(D.java:333)
server_1  |     at com.ibm.jsse2.D.a(D.java:113)
server_1  |     at com.ibm.jsse2.E.a(E.java:79)
server_1  |     at com.ibm.jsse2.E.a(E.java:107)
server_1  |     at com.ibm.jsse2.D.r(D.java:610)
server_1  |     at com.ibm.jsse2.D.a(D.java:372)
server_1  |     at com.ibm.jsse2.at.a(at.java:558)
server_1  |     at com.ibm.jsse2.at.i(at.java:73)
server_1  |     at com.ibm.jsse2.at.a(at.java:357)
server_1  |     at com.ibm.jsse2.at.startHandshake(at.java:723)
server_1  |     at com.ibm.net.ssl.www2.protocol.https.c.afterConnect(c.java:215)
server_1  |     at com.ibm.net.ssl.www2.protocol.https.d.connect(d.java:34)
server_1  |     at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1561)
server_1  |     at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1489)
server_1  |     at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:491)
server_1  |     at com.ibm.net.ssl.www2.protocol.https.b.getResponseCode(b.java:80)
server_1  |     at org.apache.cxf.transport.http.URLConnectionHTTP
server_1  | Conduit$URLConnectionWrappedOutputStream.getResponseCode(URLConnectionHTTPConduit.java:370)
server_1  |     at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.doProcessResponseCode(HTTPConduit.java:1586)
server_1  |     at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1615)
server_1  |     at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1559)
server_1  |     at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1356)
server_1  |     ... 61 more
server_1  | Caused by: java.security.cert.CertificateException: PKIXCertPathBuilderImpl could not build a valid CertPath.
server_1  |     at com.ibm.ws.ssl.core.WSX509TrustManager.checkServerTrusted(WSX509TrustManager.java:322)
server_1  |     at com.ibm.jsse2.az.checkServerTrusted(az.java:78)
server_1  |     at com.ibm.jsse2.E.a(E.java:5)
server_1  |     ... 79 more

这是尝试使用 openssl 连接时的输出:

openssl s_client -CAfile /opt/ibm/java/jre/lib/security/cacerts -connect dev.api.ibm.com:443
Certificate chain
 0 s:/C=US/ST=New York/L=Armonk/O=International Business Machines/CN=*.api.ibm.com
   i:/C=US/O=GeoTrust Inc./CN=GeoTrust SSL CA - G3
 1 s:/C=US/O=GeoTrust Inc./CN=GeoTrust SSL CA - G3
   i:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
---
No client certificate CA names sent
Peer signing digest: SHA256
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 2859 bytes and written 431 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES128-GCM-SHA256
    Session-ID: 9509AB7194AB9DE0ABE219ECAE442C5D2C93145946FC2285CB9C4F5CCC81514F
    Session-ID-ctx: 
    Master-Key: 335D047C41E5FE75096E53C9CBACC7C1CC8F6254872599EFE93C7AD5935638181AAF9240656BD44A858723C38108BB31
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1502310152
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)

你描述事情的方式听起来像是你在 运行 Liberty 16.0.0.2 时,你的场景中使用了 JSSE SSL 上下文。如果 cacerts 文件被用于信任,那么它一定是。老实说,如果您不在代码中提供 ssl 引用,我认为 jaxrs 应该回退到 Liberty SSLContext。我对此可能是错误的,将不得不进行调查。

因此,在 17.0.0.2 并使用 transportSecurity-1.0 功能,您现在使用的是 Liberty SSLContext。这是 transportSecurity-1.0 的正确行为,如果 server.xml 文件中没有 ssl 配置,jaxrs 只会回退到 JSSE 默认 SSLContext。 transportSecurity-1.0 功能支持新的出站 SSL 功能,出站默认值 https://www.ibm.com/support/knowledgecenter/en/was_beta_liberty/com.ibm.websphere.wlp.nd.multiplatform.doc/ae/twlp_config_ssl_outbound.html and outbound SSL Filter feature, https://www.ibm.com/support/knowledgecenter/en/was_beta_liberty/com.ibm.websphere.wlp.nd.multiplatform.doc/ae/rwlp_config_ssl_outbound_filter.html.

因此,如果您使用 transportSecurity-1.0 功能,您将需要将签名者添加到您的 Liberty 信任库,Liberty 不会自动接受签名者。

如果使用 ssl-1.0 功能,我将不得不调查行为应该是什么。