Tomcat с соединителем APR отключает клиентов Java с помощью SSL

Я пытаюсь использовать SSL с Tomcat и соединителем APR. Я могу вносить изменения в сервер, но не в существующий клиент Java.

По какой-то причине всякий раз, когда клиент Java подключается к серверу Tomcat по протоколу SSL, он не может подключиться - соединение сбрасывается сервером. Однако один и тот же код Java может подключаться к одному и тому же серверу через другой порт, размещенный на Apache с SSL. Кроме того, не-Java-код (например, curl) может подключаться к соединению Tomcat SSL.

Я заставил это работать, заставляя клиента Java использовать протокол TLSv1 (-Dhttps.protocols=TLSv1). Однако это не практическое решение, потому что я не могу выпустить обновление для наших клиентов Java в настоящее время.

Так как это работает с Apache на сервере, мне кажется, что я должен иметь возможность внести какие-то изменения конфигурации на сервере, чтобы также работать с Tomcat, без необходимости менять клиентов Java.

В итоге: подключение Java к Tomcat SSL = FAIL

скручиваемость подключения к Tomcat SSL = хорошо

Java подключается к Apache SSL = хорошо

скручиваемость подключения к Apache SSL = хорошо

Вот пример кода Java, который иллюстрирует проблему. Я работаю с Java 6 на Mac OS X.

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" />

1 ответ

main, WRITE: TLSv1 Handshake, length = 81
main, WRITE: SSLv2 client hello message, length = 110

Я предполагаю, что это происходит из среды Java 6 (если клиент явно не включил SSLv2Hello Протокол). Вы пытались увидеть, если клиент смог подключиться при запуске на Java 7 JRE (который не позволяет SSLv2Hello по умолчанию).

Я предполагаю, что эта проблема возникает из-за того, что соединителю APR может не понравиться получение Hellov клиента Clientv2 (который, исходя из Java, на самом деле не Hello Hello клиента SSLv2, а v3, обернутый в v2, см . Ответ EJP).

Несколько предложений, которые могут это исправить:

  • Попытайтесь заставить APR принимать соединения SSLv2, по крайней мере, SSLv3 или выше, включенные в Hello v2. (Использование SSLv2 не очень хорошая идея, но SSLv2Hello с SSLv3/TLS не должно быть проблемой.)
  • Переключите APR в Apache Tomcat для чистого соединителя Java (BIO или NIO, см. Сравнительную таблицу). Вам может понадобиться преобразовать сертификат и ключ в хранилище ключей, но это не должно быть слишком сложно, особенно в PKCS12 Хранилище ключей.
Другие вопросы по тегам