2015-01-11 3 views
1

응용 프로그램이 서버와 통신 할 때 우리는 인증서 (공개 키 고정) (MITMA에 대한 추가 검사)를 작성했습니다. 서버와 통신하려면 HttpClient를 사용하십시오. 또한 프로덕션 환경에 프록시 서버가 있으므로 SNI를 사용해야합니다. 앱을 프로덕션에 게시하기 전에 다른 환경 (TEST 환경)에서 확인합니다. TEST 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'메소드는 우리가 어떤 포트를 사용하고 있는지 알지 못하기 때문에 게시물에 썼습니다. 따라서 socket의 인스턴스가 ssl 인 경우 연결되어 있는지 확인하십시오. 그렇다면 isSecure는 true를 그렇지 않으면 false를 리턴해야합니다.