2016-11-16 6 views
3

Мне удалось включить правильный набор шифров, но в процессе я нарушил сравнение имени хоста.Получение несоответствия имени хоста после включения устаревшего набора шифров в Android 7 org.apache.http (Apache HttpClient 4.0)

Вот код, прежде чем я начал:

HttpParams params = new BasicHttpParams(); 
HttpConnectionParams.setConnectionTimeout(params, 10000); 
HttpConnectionParams.setSoTimeout(params, 20000); 

DefaultHttpClient client = new DefaultHttpClient(params); 

HttpPost request = new HttpPost(url); 
... 
HttpResponse response = client.execute(request); 
... 

Начиная с Android 7, я получаю следующее сообщение об ошибке:

javax.net.ssl.SSLHandshakeException: Handshake failed 
    at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:429) 
    at org.apache.http.conn.ssl.SSLSocketFactory.createSocket(SSLSocketFactory.java:406) 
    at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:170) 
    at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:169) 
    at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:124) 
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:366) 
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:560) 
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:492) 
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:470) 
    ... 
Caused by: javax.net.ssl.SSLProtocolException: SSL handshake terminated: ssl=0x9f7a03c0: Failure in SSL library, usually a protocol error 
error:10000410:SSL routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE (external/boringssl/src/ssl/s3_pkt.c:610 0x9c571b00:0x00000001) 
error:1000009a:SSL routines:OPENSSL_internal:HANDSHAKE_FAILURE_ON_CLIENT_HELLO (external/boringssl/src/ssl/s3_clnt.c:764 0x9c614196:0x00000000) 
    at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method) 
    at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:357) 
     ... 19 more 

Это, очевидно, связано с веб-сервера, требующего клиента в службу поддержки RC4, который, как представляется, поддерживается through Android 6 (api 23). К сожалению, обновление веб-сервера в настоящий момент невозможно, поэтому мне нужно включить требуемый набор шифров в приложении.

Вот что я пробовал:

// HttpParams params is same as before 

DefaultHttpClient client = new DefaultHttpClient(params); 

ClientConnectionManager connManager = client.getConnectionManager(); 
SchemeRegistry schemeRegistry = connManager.getSchemeRegistry(); 
Scheme scheme = schemeRegistry.getScheme("https"); 
final SSLSocketFactory impl = (SSLSocketFactory) scheme.getSocketFactory(); 

SocketFactory sf = new SocketFactory() { 

    @Override 
    public Socket createSocket() throws IOException { 
     SSLSocket socket = (SSLSocket) impl.createSocket(); 
     ArrayList<String> list = new ArrayList<>(Arrays.asList(socket.getEnabledCipherSuites())); 
     list.add("SSL_RSA_WITH_RC4_128_SHA"); 
     socket.setEnabledCipherSuites(list.toArray(new String[list.size()])); // this is where I add the required cipher suites after the SSLSocket is created 
     return socket; 
    } 

    // unchanged wrappers 
    @Override 
    public Socket connectSocket(Socket socket, String s, int i, InetAddress inetAddress, int i1, HttpParams httpParams) throws IOException { return impl.connectSocket(socket, s, i, inetAddress, i1, httpParams); } 

    @Override 
    public boolean isSecure(Socket socket) throws IllegalArgumentException { return impl.isSecure(socket); } 
}; 

schemeRegistry.register(new Scheme("https", sf, 443)); 

// request & execute same as before 

Это успешно добавляет необходимый набор шифров, но, кажется, разорвать сравнение имен хостов, потому что тогда я получаю следующее:

javax.net.ssl.SSLException: hostname in certificate didn't match: <10.10.10.10> != <sub.example.com> OR <sub.example.com> 

IP-адрес является правильным для этот хозяин. Предположительно, все мои переопределения/обертки привели к тому, что вместо исходного https://sub.example.com/ имя хоста было использовано IP-адрес, прошедший в пределах url.

Вы можете помочь мне здесь? Я не знаю, как преодолеть проблему с неправильным совпадением имени хоста.

ответ

0

Получил это! Похоже, что библиотека Apache HTTP Client проверяет, является ли мой Socket Factory sfinstanceof LayeredSocketFactory, и в этом случае для этой библиотеки используется другой метод createSocket, чтобы передать требуемое имя хоста (sub.example.com вместо 10.10.10.10) для правильного сравнения SSL/TLS.

Итак, sf необходимо написать следующим образом. Примечание: дополнительный createSocket переопределение:

LayeredSocketFactory sf = new LayeredSocketFactory() { 

    javax.net.ssl.SSLSocketFactory defaultFactory = HttpsURLConnection.getDefaultSSLSocketFactory(); 

    @Override 
    public Socket createSocket(Socket tcpSocket, String host, int port, boolean autoClose) throws IOException { 
     // I'm not actually using impl.createSocket, because then the hostname verifier would have kicked in prior to addMyCipherSuite 
     SSLSocket sslSocket = (SSLSocket) defaultFactory.createSocket(tcpSocket, host, port, autoClose); 
     impl.getHostnameVerifier().verify(host, addMyCipherSuite(sslSocket)); 
     return sslSocket; 
    } 


    @Override 
    public Socket createSocket() throws IOException { 
     return addMyCipherSuite((SSLSocket) impl.createSocket()); 
    } 

    private Socket addMyCipherSuite(SSLSocket socket) { 
     ArrayList<String> list = new ArrayList<>(Arrays.asList(socket.getEnabledCipherSuites())); 
     list.add("SSL_RSA_WITH_RC4_128_SHA"); 
     socket.setEnabledCipherSuites(list.toArray(new String[list.size()])); // this is where I add the required cipher suites after the SSLSocket is created 
     return socket; 
    } 

    // unchanged wrappers 
    @Override 
    public Socket connectSocket(Socket socket, String s, int i, InetAddress inetAddress, int i1, HttpParams httpParams) throws IOException { return impl.connectSocket(socket, s, i, inetAddress, i1, httpParams); } 

    @Override 
    public boolean isSecure(Socket socket) throws IllegalArgumentException { return impl.isSecure(socket); } 
};