java HttpClient:对每个请求进行 ssl 握手

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17929191/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-01 19:38:56  来源:igfitidea点击:

HttpClient: ssl handshake on every request

javasslhttpsapache-httpclient-4.xhandshake

提问by John

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.

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

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

顺便提一句。在我在这台主机上遇到另一个问题:“收到致命警报:bad_record_mac”之前,通过只允许 SSLv3 解决了这个问题

UPD1: HttpClient init code

UPD1:HttpClient 初始化代码

    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)

UPD2:修改 SSLSocketFactory(“收到致命警报:bad_record_mac”问题)

  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

UPD3:问题仅存在于 SSLv3,TLSv1 工作正常

回答by ok2c

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).

仅当 HttpClient 可以确保它们属于相同的用户/安全上下文(出于显而易见的原因)时,HttpClient 才会重新使用具有客户端身份验证的持久 SSL 连接。

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

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

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-Agentheader value and see if that makes any difference.

它调出服务器根本不希望重新使用连接。每个响应都包含'Connection: close'指令,提示客户端在收到响应后关闭连接。但是,服务器可能会根据请求消息的组成对不同的客户端进行不同的处理。尝试通过使用不同的User-Agent标头值来伪装 HttpClient ,看看这是否有什么不同。

回答by user207421

As you state in a comment that the problem only occurs with one server, clearly the problem is at that server. They have set a very short SSL session timeout, or disabled session resumption altogether somehow.

正如您在评论中指出问题仅出现在一台服务器上,显然问题出在该服务器上。他们设置了一个非常短的 SSL 会话超时,或者以某种方式完全禁用了会话恢复。

There's nothing you can do about it from your end.

从你的角度来看,你无能为力。