【问题标题】:Tomcat with APR connector is disconnecting Java clients with SSL带有 APR 连接器的 Tomcat 正在使用 SSL 断开 Java 客户端
【发布时间】:2013-08-27 02:49:34
【问题描述】:

我正在尝试将 SSL 与 Tomcat 和 APR 连接器一起使用。我可以更改服务器,但不能更改现有的 Java 客户端。

由于某种原因,每当 Java 客户端使用 SSL 连接到 Tomcat 服务器时,它都无法连接 - 服务器会重置连接。但是,完全相同的 Java 代码可以使用 SSL 连接到 Apache 托管的不同端口上的同一服务器。此外,非 Java 代码(如 curl)可以连接到 Tomcat SSL 连接。

我通过强制 Java 客户端使用 TLSv1 协议 (-Dhttps.protocols=TLSv1) 来实现这一点。但是,这不是一个实用的解决方案,因为我目前无法为我们的 Java 客户端发布更新。

由于这适用于服务器上的 Apache,因此在我看来,我应该能够在服务器上进行某种配置更改以也适用于 Tomcat,而无需更改 Java 客户端。

总结: Java 连接到 Tomcat SSL = FAIL

curl 连接到 Tomcat SSL = 好

Java 连接到 Apache SSL = 好

curl 连接到 Apache SSL = 好

下面是一些说明问题的示例 Java 代码。我在 Mac OS X 上运行 Java 6。

public class SSLConnectTest {
    public static void main(String[] args) throws Exception {
        System.setProperty( "javax.net.debug", "all" );

        testConnection( "https://secure2.360works.com" ); //Apache running SSL. This works.
        testConnection( "https://secure2.360works.com:8443/" ); //Tomcat running SSL and APR. This fails.
    }

    private static void testConnection( String urlString ) throws IOException {
        new URL( urlString ).openStream().close();
    }
}

以下是 SSL 握手中发生的情况:

trigger seeding of SecureRandom
done seeding SecureRandom
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
%% No cached client session
*** ClientHello, TLSv1
RandomCookie:  GMT: 1377233856 bytes = { 69, 128, 29, 114, 252, 186, 13, 192, 212, 243, 179, 208, 124, 196, 220, 137, 23, 124, 30, 226, 98, 148, 243, 6, 188, 230, 109, 119 }
Session ID:  {}
Cipher Suites: [SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_DES_CBC_SHA, SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
Compression Methods:  { 0 }
***
main, WRITE: TLSv1 Handshake, length = 81
main, WRITE: SSLv2 client hello message, length = 110
main, handling exception: java.net.SocketException: Connection reset
main, SEND TLSv1 ALERT:  fatal, description = unexpected_message
main, WRITE: TLSv1 Alert, length = 2
main, Exception sending alert: java.net.SocketException: Broken pipe
main, called closeSocket()
Disconnected from the target VM, address: '127.0.0.1:62146', transport: 'socket'
Exception in thread "main" java.net.SocketException: Connection reset
    at java.net.SocketInputStream.read(SocketInputStream.java:168)
    at com.sun.net.ssl.internal.ssl.InputRecord.readFully(InputRecord.java:422)
    at com.sun.net.ssl.internal.ssl.InputRecord.read(InputRecord.java:460)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:863)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1188)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1215)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1199)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:434)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1172)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:234)
    at java.net.URL.openStream(URL.java:1010)
    at com.prosc.license.client.network.SSLConnectTest.testConnection(SSLConnectTest.java:22)
    at com.prosc.license.client.network.SSLConnectTest.main(SSLConnectTest.java:18)

这是 server.xml 中的连接器配置。我希望这里的一些改变可以解决这个问题:

<Connector port="8443" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
sslProtocol="SSLv2+TLSv1+SSLv3"
SSLHonorCipherOrder="true"
protocol="org.apache.coyote.http11.Http11AprProtocol"
clientAuth="false" SSLCertificateFile="/etc/apache2/ssl.crt/secure2.360works.com.crt"
SSLCertificateKeyFile="/etc/apache2/ssl.crt/secure2.360works.com.key"
SSLCertificateChainFile="/etc/apache2/ssl.crt/secure2.360works.com.chcrt" />

【问题讨论】:

    标签: java tomcat ssl apr


    【解决方案1】:
    main, WRITE: TLSv1 Handshake, length = 81
    main, WRITE: SSLv2 client hello message, length = 110
    

    我认为这是来自 Java 6 环境(除非客户端已明确启用 SSLv2Hello 协议)。您是否尝试查看客户端在 Java 7 JRE(doesn't enable SSLv2Hello by default)上运行时是否能够连接。

    我猜这个问题的发生是因为 APR 连接器可能不喜欢接收 SSLv2 Client Hello(它来自 Java,并不是真正的 SSLv2 Client Hello,而是包装到 v2 中的 v3,请参阅EJP's answer) .

    一些可以解决此问题的建议:

    • 尝试让 APR 接受 SSLv2 连接,至少将 SSLv3 或更高版本包装到 v2 Hello 中。 (使用 SSLv2 不是一个好主意,但 SSLv2Hello 与 SSLv3/TLS 应该不成问题。)
    • 在 Apache Tomcat 中切换 APR 以获得纯 Java 连接器(BIO 或 NIO,请参阅 comparative table)。您可能需要将证书和密钥转换为密钥库,但这应该不会太难,尤其是转换为 PKCS12 密钥库。

    【讨论】:

    • 感谢您的回复。我更新了我的问题以在 server.xml 文件中显示连接器元素。我目前正在启用 SSLv2、SSLv3 和 TLSv1(根据文档,它们都是受支持的协议)。
    • 我不想切换到 BIO 或 NIO,因为我所有的研究都表明,为了支持 SSL,APR 更快。我改用 APR 的原因是为了提高性能。
    • 我在客户端使用 Java 7 对其进行了测试,结果确实有效!但是,我 90% 的客户都使用 Java 6,所以我仍在寻找服务器端解决方案来解决他们的问题。
    • 抱歉,我不确定如何让 APR 接受 SSLv2Hello。与完全失去连接相比,我怀疑可能的小幅性能损失不应该是您主要关心的问题。顺便说一句,有任何实际的基准来支持您的性能问题(尤其是针对 NIO)?
    • 我必须承认我不知道SSLv2+TLSv1+SSLv3 是否包含 SSLv3 ClientHello 包装到 SSLv2 Hello 中的情况。
    猜你喜欢
    • 2017-03-07
    • 1970-01-01
    • 2015-06-29
    • 2011-02-27
    • 2015-05-15
    • 1970-01-01
    • 2016-09-13
    相关资源
    最近更新 更多