tomcat-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Martin Gainty <mgai...@hotmail.com>
Subject RE: Connecting to Tomcat APR Connector with TLSv1 fails with Java HttpsURLConnection
Date Sat, 02 Jun 2012 11:05:49 GMT

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-

 		 	   		  
Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message