MG>reply > From: michael.osi...@siemens.com > To: users@tomcat.apache.org > Date: Fri, 1 Jun 2012 14:54:47 +0200 > Subject: Connecting to Tomcat APR Connector with TLSv1 fails with Java > HttpsURLConnection > > Hi folks, > > I am on Tomcat 6.0.35, Java 6, HP-UX, Tomcat Native 1.1.22. > > Recenly, I had to switch my Http11AprProtocol connector to TLSv1 due to a > security scans in our company. > After that a CLI client with Java's HttpsURLConnection failed to connect to > that server: > > Exception in thread "main" javax.net.ssl.SSLHandshakeException: Remote host > closed connection during handshake > at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source) > at > com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Unknown > Source) > at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown > Source) > at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown > Source) > at sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source) > at > sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown > Source) > at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown > Source) > at java.net.HttpURLConnection.getResponseCode(Unknown Source) > at > sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(Unknown > Source) > at com.siemens.smartld.kerberos_request.App.main(App.java:80) > Caused by: java.io.EOFException: SSL peer shut down incorrectly > at com.sun.net.ssl.internal.ssl.InputRecord.read(Unknown Source) > ... 10 more > > After a short analysis, I have realized that the client sends: > main, WRITE: TLSv1 Handshake, length = 75 > main, WRITE: SSLv2 client hello message, length = 101 > > Since the server does not support any form of SSL, it aborts the handshake. > After a bit of Goole I found these [1], [2] SO threads saying that this is > correct due to the RFC to retain backwards compat with older servers. > > According to Oracle's docs, this has been dropped in Java 7 [3] and [4]: > > Area: API: JSSE > Synopsis: Support for TLS 1.1 has been added to the SunJSSE provider, and the > SSLv2Hello "pseudo protocol" is no longer active by default in the SunJSSE > provider. > > And > > Area: Runtime > Synopsis: The SSLv2Hello Handshake Protocol is Now Disabled by Default > Description: The SSLv2Hello handshake protocol, which was used by SSLv3 > server implementations to communicate with [...] > > Java 7 is not an option here at the moment. > > What you can do is to explicitly disable SSLv2Hello message with a system > property [5] but still, other folks will stumble upon this problem anyway > doing the same research as I did and waste their time. MG>SSL_PROTOCOL_ALL should have satisfied this requirement as seen in the OR definition for supported protocols MG> /** Define the SSL Protocol options*/ MG> public static final int SSL_PROTOCOL_NONE = 0; MG> public static final int SSL_PROTOCOL_SSLV2 = (1<<0); MG> public static final int SSL_PROTOCOL_SSLV3 = (1<<1); MG> public static final int SSL_PROTOCOL_TLSV1 = (1<<2); MG> public static final int SSL_PROTOCOL_ALL = (SSL_PROTOCOL_SSLV2|SSL_PROTOCOL_SSLV3|SSL_PROTOCOL_TLSV1); > > I retried the same setup with OpenSSL as in my server.xml: > > openssl s_server -cert /etc/opt/ssl/cert/server.crt \ > -key /etc/opt/ssl/key/server.key -tls1 -cipher HIGH > > Fails just as same as Tomcat. Though adding -ssl3 fails too.
> To alleviate this issue for the Java client, I have patched Tomcat to allow > SSLv3+TLSv1 [6] to make both work, the connection does not fail anymore. MG>did you run TestCases on native wrapper make method from SSLContext class? MG> public final class SSLContext { /*** Initialize new SSL context * @param pool The pool to use. * @param protocol The SSL protocol to use. It can be one of: * SSL_PROTOCOL_SSLV2 * SSL_PROTOCOL_SSLV3 * SSL_PROTOCOL_SSLV2 | SSL_PROTOCOL_SSLV3 * SSL_PROTOCOL_TLSV1 * SSL_PROTOCOL_ALL * @param mode SSL mode to use * <PRE> * SSL_MODE_CLIENT * SSL_MODE_SERVER * SSL_MODE_COMBINED */ public static native long make(long pool, int protocol, int mode) throws Exception; MG> MG>do your native libraries support this patch? ....if you are using APR here is the native code from sslcontext.c MG>/* Initialize server context */ TCN_IMPLEMENT_CALL(jlong, SSLContext, make)(TCN_STDARGS, jlong pool, jint protocol, jint mode) { apr_pool_t *p = J2P(pool, apr_pool_t *); tcn_ssl_ctxt_t *c = NULL; SSL_CTX *ctx = NULL; UNREFERENCED(o); switch (protocol) { case SSL_PROTOCOL_SSLV2: case SSL_PROTOCOL_SSLV2 | SSL_PROTOCOL_TLSV1: if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(SSLv2_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(SSLv2_server_method()); else ctx = SSL_CTX_new(SSLv2_method()); break; case SSL_PROTOCOL_SSLV3: case SSL_PROTOCOL_SSLV3 | SSL_PROTOCOL_TLSV1: //Test to see if SSLV3 and TLSV1 is triggered if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(SSLv3_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(SSLv3_server_method()); else ctx = SSL_CTX_new(SSLv3_method()); break; case SSL_PROTOCOL_SSLV2 | SSL_PROTOCOL_SSLV3: case SSL_PROTOCOL_ALL: if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(SSLv23_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(SSLv23_server_method()); else ctx = SSL_CTX_new(SSLv23_method()); break; case SSL_PROTOCOL_TLSV1: if (mode == SSL_MODE_CLIENT) ctx = SSL_CTX_new(TLSv1_client_method()); else if (mode == SSL_MODE_SERVER) ctx = SSL_CTX_new(TLSv1_server_method()); else ctx = SSL_CTX_new(TLSv1_method()); break; } if (!ctx) { tcn_ThrowException(e, "Invalid Server SSL Protocol"); goto init_failed; } if ((c = apr_pcalloc(p, sizeof(tcn_ssl_ctxt_t))) == NULL) { tcn_ThrowAPRException(e, apr_get_os_error()); goto init_failed; } c->protocol = protocol; c->mode = mode; c->ctx = ctx; c->pool = p; c->bio_os = BIO_new(BIO_s_file()); if (c->bio_os != NULL) BIO_set_fp(c->bio_os, stderr, BIO_NOCLOSE | BIO_FP_TEXT); SSL_CTX_set_options(c->ctx, SSL_OP_ALL); if (!(protocol & SSL_PROTOCOL_SSLV2)) SSL_CTX_set_options(c->ctx, SSL_OP_NO_SSLv2); if (!(protocol & SSL_PROTOCOL_SSLV3)) SSL_CTX_set_options(c->ctx, SSL_OP_NO_SSLv3); if (!(protocol & SSL_PROTOCOL_TLSV1)) SSL_CTX_set_options(c->ctx, SSL_OP_NO_TLSv1); /** Configure additional context ingredients*/ SSL_CTX_set_options(c->ctx, SSL_OP_SINGLE_DH_USE); #ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION /* * Disallow a session from being resumed during a renegotiation, * so that an acceptable cipher suite can be negotiated. */ SSL_CTX_set_options(c->ctx, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); #endif /* Default session context id and cache size */ SSL_CTX_sess_set_cache_size(c->ctx, SSL_DEFAULT_CACHE_SIZE); MD5((const unsigned char *)SSL_DEFAULT_VHOST_NAME, (unsigned long)(sizeof(SSL_DEFAULT_VHOST_NAME) - 1), &(c->context_id[0])); if (mode) { SSL_CTX_set_tmp_rsa_callback(c->ctx, SSL_callback_tmp_RSA); SSL_CTX_set_tmp_dh_callback(c->ctx, SSL_callback_tmp_DH); } /* Set default Certificate verification level * and depth for the Client Authentication*/ c->verify_depth = 1; c->verify_mode = SSL_CVERIFY_UNSET; c->shutdown_type = SSL_SHUTDOWN_TYPE_UNSET; /* Set default password callback */ SSL_CTX_set_default_passwd_cb(c->ctx, (pem_password_cb *)SSL_password_callback); SSL_CTX_set_default_passwd_cb_userdata(c->ctx, (void *)(&tcn_password_callback)); /** Let us cleanup the ssl context when the pool is destroyed*/ apr_pool_cleanup_register(p, (const void *)c, ssl_context_cleanup, apr_pool_cleanup_null); return P2J(c); init_failed: return 0; } MG>case SSL_PROTOCOL_SSLV3 | SSL_PROTOCOL_TLSV1: MG>*should* already be accomodated... is this not the case? MG> MG>If APR is enabled Your testcase for SSLContext make method is in AprEndpoint.java init() method MG> // Initialize SSL if needed if (SSLEnabled) { // SSL protocol int value = SSL.SSL_PROTOCOL_ALL; if ("SSLv2".equalsIgnoreCase(SSLProtocol)) { value = SSL.SSL_PROTOCOL_SSLV2; } else if ("SSLv3".equalsIgnoreCase(SSLProtocol)) { value = SSL.SSL_PROTOCOL_SSLV3; } else if ("TLSv1".equalsIgnoreCase(SSLProtocol)) { value = SSL.SSL_PROTOCOL_TLSV1; // Test this! } else if ("SSLv2+SSLv3".equalsIgnoreCase(SSLProtocol)) { value = SSL.SSL_PROTOCOL_SSLV2 | SSL.SSL_PROTOCOL_SSLV3; } // Create SSL Context sslContext = SSLContext.make(rootPool, value, SSL.SSL_MODE_SERVER); MG>your server.xml should contain AprLifecycleListener and a Connector which implements sslProtocol="TLS" as seen here MG><Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> MG> <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" /> MG>when all of these configurations are made do you see value if ("TLSv1".equalsIgnoreCase(SSLProtocol)) being set in AprEndpoint.java init() method? MG>do you see protocol being set to SSL.SSL_PROTOCOL_TLSV1 in public static native long make(long pool, int protocol, int mode) method? MG>in sslcontext.c do you not see the TLSV1 case statement being triggered? > Now, according to the citation in this SO answer [7] my question is: Should > Tomcat ignore the SSLv2Hello wrapped compat message (which is a actually a > SSLv3/TLSv1 version message according to Wireshark) and continue with the > TLSv1 handshake? > > Shall I presume that both OpenSSL s_server and Tomcat are incorrect or is > this some inconsistency in the RFC? > > [1] > http://stackoverflow.com/questions/10196436/ssl-handshaking-with-older-clients-using-sslengine-jsse > [2] > http://stackoverflow.com/questions/4682957/why-does-javas-sslsocket-send-a-version-2-client-hello > [3] http://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html > [4] http://www.oracle.com/technetwork/java/javase/compatibility-417013.html > [5] > http://docs.oracle.com/javase/1.4.2/docs/guide/plugin/developer_guide/faq/troubleshooting.html > [6] https://issues.apache.org/bugzilla/show_bug.cgi?id=53344 > [7] http://stackoverflow.com/a/10198268/696632 > > With best regards, > Michael Osipov Mit freundlichen Grüßen / Best Regards Martin-