2013-07-29 36 views
4

我使用静态HttpClient,并且它通过https工作得非常慢。我添加了-Djavax.net.debug = ssl,并发现再次为每个https请求启动握手。 看起来像不能重用旧会话,但我找不到原因。HttpClient:每个请求的SSL握手

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。我面对这个主机上的另一个问题,才道:“收到致命警报:bad_record_mac”,它解决了只允许SSLv3的

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:修改的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:问题只存在于SSLv3的,使用TLSv1工作正常

+0

请发送你如何在http客户端进行请求的代码。 – drvdijk

+0

我已经添加了HttpClient的init代码,比我只调用httpClient.execute()时,url始终是相同的 – John

回答

2

HttpClient的重新使用持久性SSL connecti只有在可以确保它们属于同一用户/安全上下文(出于显而易见的原因)的情况下才使用客户端身份验证。

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

后续

它调出来的服务器根本不想连接被重新使用。每个响应都包含'Connection: close'指令,该指令在收到响应后提示客户端关闭连接。但是,可能会发生这样的情况:服务器根据请求消息组成不同地处理不同的客户端。尝试使用不同的User-Agent标头值伪装HttpClient,看看是否有任何区别。

+0

我试图创建静态HttpContext,并在每次调用httpClient.execute()时指定它。顺便说一句只有1服务器发生此问题。 – John

+0

如果HttpClient或HttpContext与它有任何关系,我会感到惊讶。 SSL会话支持发生在SSLSocket层,而不是HTTP层。 – EJP

+0

@EJP:相信我,它的确如此。 HttpClient将SSL连接视为有状态的客户端身份验证,并依赖上下文来传播该状态 – oleg

0

正如您在评论中所述,该问题只发生在一台服务器上,显然问题出在该服务器上。他们设置了一个非常短的SSL会话超时,或者完全禁用会话恢复。

从你的最后,你无能为力。

+0

C#HttpSimpleClientProtocol可以正常使用这个服务器,所以我想这可能会“修复”java HttpClient – John

+0

它适用于HTTPS? – EJP

+0

正如我在Fiddler中看到的那样,它可以与https协同工作 – John