2013-07-29 35 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