MG>reply
> From: [email protected]
> To: [email protected]
> 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-