You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tomcat.apache.org by "Osipov, Michael" <mi...@siemens.com> on 2012/06/01 14:54:47 UTC
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.
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.
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
Re: Connecting to Tomcat APR Connector with TLSv1 fails with Java
HttpsURLConnection
Posted by Christopher Schultz <ch...@christopherschultz.net>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Martin,
On 6/2/12 7:05 AM, Martin Gainty wrote:
> 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);
The source code for both Tomcat and mod_jk are publicly available. You
didn't have to re-publish it on the mailing list with very little
explanation.
- -chris
-----BEGIN PGP SIGNATURE-----
Version: GnuPG/MacGPG2 v2.0.17 (Darwin)
Comment: GPGTools - http://gpgtools.org
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iEYEARECAAYFAk/KT3YACgkQ9CaO5/Lv0PDcJACfW2NG5MLY29CJ43kCbZYoshyH
JtMAoJxlURmKUMjuDT0UZ3CwDNIE4878
=c4Ja
-----END PGP SIGNATURE-----
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org
RE: Connecting to Tomcat APR Connector with TLSv1 fails with Java
HttpsURLConnection
Posted by Martin Gainty <mg...@hotmail.com>.
MG>reply
> From: michael.osipov@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-