2015-01-11 85 views
1

在我們的應用程序中,當應用程序與服務器通信時,我對證書進行了額外的檢查(針對公鑰密碼)(針對MITMA)。 要與服務器通信,請使用HttpClient。 另外我在生產中有一些代理服務器,所以我需要使用SNI。在我們將應用程序發佈到產品之前,我們在另一個環境(TEST env)中檢查它。 對於測試env,我們只有自簽名證書,導致其僅用於測試,並且我們不希望僅爲這種情況購買新證書。自定義SSLSocketFactory不使用自定義信任管理器

爲了實現它,我創建了自定義SSLSocketFactory(org.apache.http.conn.ssl.SSLSocketFactory)。 但問題是它不適用於自簽名證書。 我將自定義trustManager(IgnoreCertificatesTrustManager)設置爲sslSocketFactory(SSLCertificateSocketFactory)。 如果我使用下面的代碼:

SSLContext sslContext = SSLContext.getInstance(SSLSocketFactory.TLS); 
sslContext.init(null, new TrustManager[] { new IgnoreCertificatesTrustManager() }, null); 
sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); 

的自我燒焦證書工程的檢查(忽略的TrustManager(IgnoreCertificatesTrustManager)的檢查)。但代碼不支持SNI解決方案。 我做錯了什麼?

謝謝。

private class CustomSSLSocketFactory extends SSLSocketFactory { 

     public CustomSSLSocketFactory() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException, CertificateException { 
      super(null); 
     } 

     // Plain TCP/IP (layer below TLS) 

     @Override 
     public Socket connectSocket(Socket s, String host, int port, InetAddress localAddress, int localPort, HttpParams params) throws IOException { 
      return null; 
     } 

     @Override 
     public Socket createSocket() throws IOException { 
      return null; 
     } 

     @Override 
     public boolean isSecure(Socket s) throws IllegalArgumentException { 
      if (s instanceof SSLSocket) { 
       return ((SSLSocket) s).isConnected(); 
      } 
      return false; 
     } 

     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) 
     @Override 
     public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { 
      SSLSocket sslSocket = null; 

//   if (isProduction()) { 
       if (autoClose) { 
        // we don't need the plainSocket 
        socket.close(); 
       } 

       // create and connect SSL socket, but don't do hostname/certificate verification yet 
       SSLCertificateSocketFactory sslSocketFactory = (SSLCertificateSocketFactory) SSLCertificateSocketFactory.getDefault(0, null); 


       // NOT works! 
       sslSocketFactory.setTrustManagers(new TrustManager[] { new IgnoreCertificatesTrustManager() }); 
       // ---- 


       sslSocket = (SSLSocket) sslSocketFactory.createSocket(socket, host, port, autoClose); 

       // enable TLSv1.1/1.2 if available (see https://github.com/rfc2822/davdroid/issues/229) 
       sslSocket.setEnabledProtocols(sslSocket.getSupportedProtocols()); 

       // set up SNI before the handshake 
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 
        logger.debug("Setting SNI hostname"); 
        sslSocketFactory.setHostname(sslSocket, host); 
       } else { 
        logger.debug("No documented SNI support on Android <4.2, trying with reflection"); 
        try { 
         java.lang.reflect.Method setHostnameMethod = sslSocket.getClass().getMethod("setHostname", String.class); 
         setHostnameMethod.invoke(sslSocket, host); 
        } catch (Exception e) { 
         logger.error("SNI not useable", e); 
        } 
       } 

//   } else { 
//    try { 
//     SSLContext sslContext = SSLContext.getInstance(SSLSocketFactory.TLS); 
//     sslContext.init(null, new TrustManager[] { new IgnoreCertificatesTrustManager() }, null); 
//     sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); 
//    } catch (java.security.NoSuchAlgorithmException e) { 
//     throw new IOException(e); 
//    } catch (KeyManagementException e) { 
//     throw new IOException(e); 
//    } 
//   } 

      // verify certificate 
      SSLSession session = sslSocket.getSession(); 
      X509Certificate[] certificates = (X509Certificate[]) session.getPeerCertificates(); 

      if (!checkPublicKey(certificates)) { 
       throw new IOException("SSL_HANDSHAKE_FAILED"); 
      } 

      return sslSocket; 
     } 

    } 
+1

你不需要任何這個。只需使用您的信任管理器初始化一個「SSLContext」即可。 'isSecure()的奇怪定義''isConnected()'與它有什麼關係? – EJP

+0

您確定使用支持SNI的HTTPClient版本嗎?通常,Android會附帶不支持SNI的舊版Apache HTTPClient。 –

+0

如果您希望支持來自同一個'SSLContext'的*自簽名證書和常規CA根證書,您需要制定一個合適的'TrustManager'來執行布爾邏輯。在我的[CWAC-Security庫](https://github.com/commonsguy/cwac-security/)中查看我的'TrustManagerBuilder'。 – CommonsWare

回答

2

我發現了這個問題。我的錯。代替行: sslSocket = (SSLSocket) sslSocketFactory.createSocket(socket, host, port, autoClose); 您應該使用: sslSocket = (SSLSocket) sslSocketFactory.createSocket(InetAddress.getByName(host), port); 爲什麼?由於未使用sslSocketFactory.createSocket(InetAddress,int)方法執行主機名驗證。

此外,方法'isSecure'也寫在帖子中,因爲我們不知道我們使用的是什麼端口,所以如果套接字的實例是ssl,請檢查它是否連接。如果是,那麼isSecure應返回true否則爲false。