关于java:HttpClient:每次请求均使用ssl握手

HttpClient: ssl handshake on every request

我使用静态HttpClient,它在https上的运行速度非常慢。我添加了-Djavax.net.debug = ssl,并发现再次为每个https请求开始握手。
看起来它无法重用旧的会话,但是我找不到原因。

1
2
3
4
5
6
7
8
9
10
11
12
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

顺便说一句。在此主机上遇到另一个问题:"收到致命警报:bad_record_mac"之前,仅允许使用SSLv3

UPD1:HttpClient初始化代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
    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:修改过的SSLSocketFactory("收到的致命警报:bad_record_mac"问题)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
  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:仅SSLv3存在问题,TLSv1正常工作


HttpClient仅在可以确保它们属于同一用户/安全上下文(出于明显的原因)的情况下,才使用具有客户端身份验证的持久SSL连接。

确保所有逻辑相关的请求都使用相同的HttpContext。这将确保安全主体(客户端证书的DN)将在各个HTTP请求之间传播。

跟进

它调出了服务器,只是不想重复使用连接。每个响应都包含'Connection: close'指令,该指令提示客户端在收到响应后关闭连接。但是,服务器可能会根据请求消息的组成不同地对待不同的客户端。尝试通过使用不同的User-Agent标头值伪装HttpClient,看看是否有任何区别。


当您在注释中指出该问题仅发生在一台服务器上时,显然问题出在该服务器上。他们设置了非常短的SSL会话超时,或者完全禁用了会话恢复。

从一开始您就无能为力。