2015-01-11 2 views
1

В нашем приложении, когда приложение общается с сервером, я проверил дополнительную проверку сертификатов (с открытым ключом) (против MITMA). Для связи с сервером я использую HttpClient. Также у меня есть прокси-сервер в производстве, поэтому мне нужно использовать SNI. Прежде чем мы опубликуем приложение для производства, мы проверим его в другой среде (TEST env). Для TEST env у нас есть только самозаверяющий сертификат, поэтому его использование используется только для тестирования, и мы не хотим покупать новый сертификат только для этого случая.Пользовательский SSLSocketFactory не использует настраиваемый trustmanager

Для его реализации я создал настраиваемый 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

Вы уверены, что используете версию HTTPClient, которая поддерживает SNI? Обычно Android поставляется со старой версией Apache HTTPClient, которая не поддерживает SNI. –

+0

Если вы хотите поддерживать * самозаверяющие сертификаты и обычные CA-корневые сертификаты из того же 'SSLContext', вам нужно создать подходящий' TrustManager', который может выполнять логическую логику. См. Мой «TrustManagerBuilder» в моей [CWAC-Security library] (https://github.com/commonsguy/cwac-security/). – 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.

Смежные вопросы