HttpClient: ssl handshake on every request

14,410

HttpClient re-uses persistent SSL connections with client authentication only if it can make sure they belong to the same user / security context (for obvious reasons).

Make sure you are using the same HttpContext for all logically related requests. This will ensure the security principal (DN of the client certificate) will get propagated between individual HTTP requests.

Follow-up

It tuned out the server simply does not want connections to be re-used. Every response contains 'Connection: close' directive that prompts the client to close connections after receiving the response. It may happen, though, that the server treats different clients differently based on the request message composition. Try masquerading HttpClient by using a different User-Agent header value and see if that makes any difference.

Share:
14,410
John
Author by

John

Updated on June 19, 2022

Comments

  • John
    John almost 2 years

    I use static HttpClient, and it works very slowly over https. I have added -Djavax.net.debug=ssl and found that handshaking is started for every https request again. looks like it can not reuse old session, but I can not found why.

    9007199254743735, setSoTimeout(0) called
    Allow unsafe renegotiation: false
    Allow legacy hello messages: true
    Is initial handshake: true
    Is secure renegotiation: false
    9007199254743735, setSoTimeout(0) called
    %% No cached client session
    *** ClientHello, SSLv3
    ...
    %% Didn't cache non-resumable client session: [Session-1, SSL_RSA_WITH_RC4_128_MD5]
    ...
    Is initial handshake: true
    

    BTW. before I was faced with another problem on this host: "Received fatal alert: bad_record_mac", it was solved by allowing only SSLv3

    UPD1: HttpClient init code

        final SSLContext sslCtx;
        sslCtx = SSLContext.getInstance("SSL");
        sslCtx.init(null, new TrustManager[]{new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] cert,
                        String authType) {
                }
    
                @Override
                public void checkServerTrusted(X509Certificate[] cert,
                        String authType) {
                }
    
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            }}, null);
    
        X509HostnameVerifier verifier = new X509HostnameVerifier() {
            @Override
            public void verify(String string, SSLSocket ssls) throws IOException {
            }
    
            @Override
            public void verify(String string, X509Certificate xc) throws SSLException {
            }
    
            @Override
            public void verify(String string, String[] strings, String[] strings1) throws SSLException {
            }
    
            @Override
            public boolean verify(String string, SSLSession ssls) {
                return true;
            }
        };
        final SSLSocketFactory socketFactory = new SSLv3SocketFactory(sslCtx, verifier);
        final SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("https", 443, socketFactory));
    
        final PoolingClientConnectionManager cm = new PoolingClientConnectionManager(registry);
        cm.setMaxTotal(100);
        cm.setDefaultMaxPerRoute(50);
        final HttpParams httpParams = new BasicHttpParams();
        HttpConnectionParams.setSoTimeout(httpParams, timeout);
    
        httpClient = new DefaultHttpClient(cm, httpParams);
    
        ((DefaultHttpClient) httpClient).setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {
            @Override
            public long getKeepAliveDuration(HttpResponse hr, HttpContext hc) {
                return 0;
            }
        });
        httpClient.getParams().setParameter("http.socket.timeout", 900000);
    

    UPD2: modified SSLSocketFactory("Received fatal alert: bad_record_mac" issue)

      public class SSLv3SocketFactory extends SSLSocketFactory {
    
        private final javax.net.ssl.SSLSocketFactory socketfactory;
    
        public SSLv3SocketFactory(SSLContext sslContext, X509HostnameVerifier hostnameVerifier) {
            super(sslContext, hostnameVerifier);
            this.socketfactory = sslContext.getSocketFactory();
        }
    
        @Override
        public Socket createLayeredSocket(
                final Socket socket,
                final String host,
                final int port,
                final boolean autoClose) throws IOException, UnknownHostException {
            SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket(
                    socket,
                    host,
                    port,
                    autoClose);
            sslSocket.setEnabledProtocols(new String[]{"SSLv3"});
    
    
            return sslSocket;
        }
    
        @Override
        public Socket connectSocket(
                final Socket socket,
                final InetSocketAddress remoteAddress,
                final InetSocketAddress localAddress,
                final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
    
            if (socket instanceof SSLSocket) {
                ((SSLSocket) socket).setEnabledProtocols(new String[]{"SSLv3"});;
            }
            return super.connectSocket(socket, remoteAddress, localAddress, params);
        }
    }
    

    UPD3: Problem exists only for SSLv3, TLSv1 works fine