HttpClient не работает с Handshake Failed в Android 5.0 Lollipop - программирование
Подтвердить что ты не робот

HttpClient не работает с Handshake Failed в Android 5.0 Lollipop

DefaultHttpClient в Android 5.0 Lollipop кажется сломанным. Он не может установить соединение с некоторыми сайтами, которые были успешно установлены предыдущими версиями Android.

Например, я пытаюсь подключиться к https://uralsg.megafon.ru

//Create httpclient like in /info/61127/ssltls-protocols-and-cipher-suites-with-the-androidhttpclient
HttpClient client = new DefaultHttpClient(manager, params);
HttpGet httpGet = new HttpGet("https://uralsg.megafon.ru");
HttpResponse client = httpclient.execute(httpGet);

Этот код работает в Android 2.3-4.4, но не работает на Android 5.0 (устройства и эмулятор) с ошибкой Connection, закрытой одноранговым узлом. Конечно, это понятно, потому что Android 5.0 пытается подключить этот старый сервер к TLSv1.2 и современным шифрам, и он не поддерживает их.

Хорошо, используя пример кода в протоколах SSL/TLS и наборах шифров с AndroidHttpClient, мы ограничиваем протокол и шифр TLSv1 и SSL_RSA_WITH_RC4_128_MD5. Теперь он терпит неудачу с другой ошибкой:

javax.net.ssl.SSLHandshakeException: Handshake failed
caused by 
    error:140943FC:SSL routines:SSL3_READ_BYTES:sslv3 alert bad record mac 
    (external/openssl/ssl/s3_pkt.c:1286 0x7f74c1ef16e0:0x00000003) 
    at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake

И, конечно, этот код работает плавно на Android 2.3-4.4.

Я просмотрел трафик с помощью wirehark:

302 4002.147873000  192.168.156.30  83.149.32.13    TLSv1   138 Client Hello
303 4002.185362000  83.149.32.13    192.168.156.30  TLSv1   133 Server Hello
304 4002.186700000  83.149.32.13    192.168.156.30  TLSv1   1244    Certificate
305 4002.186701000  83.149.32.13    192.168.156.30  TLSv1   63  Server Hello Done
307 4002.188117000  192.168.156.30  83.149.32.13    TLSv1   364 Client Key Exchange, Change Cipher Spec, Encrypted Handshake Message
308 4002.240695000  83.149.32.13    192.168.156.30  TLSv1   61  Alert (Level: Fatal, Description: Bad Record MAC)

Вы можете видеть, что соединение было установлено, но сервер был предупрежден, потому что он, вероятно, не мог декодировать зашифрованное сообщение подтверждения.

Мне не удалось подключиться к https://uralsg.megafon.ru с помощью HttpClient на Android 5.0. Однако браузер в браузере подключается. Android 2.3-4.4 никак не связывает этот сайт без каких-либо трудностей.

Есть ли способ, чтобы HttpClient мог подключать такие сайты? Это только один пример, я уверен, что существует множество устаревших серверов, которые не могут быть подключены к Android 5.0 и HttpClient.

4b9b3361

Ответ 1

update: он оказался ошибкой в ​​back-end, а не андроиде 5, хотя действительно с данным шифром.

У меня была та же проблема. Для меня это оказался шифр TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, который был выбран из набора исправлений по умолчанию android 5 (обновлено).

Как только я удалил его из списка допустимых шифров клиента, соединения снова работали.

В журнале изменений андроида 5 говорится:

  • Шифрование AES-GCM (AEAD) теперь включено,

Я уверен, что это преступник. Как только TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 является предпочтительным (сервером), соединение не удастся.

Обратите внимание, что TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 работает.

Я предполагаю, что либо реализация Android TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 является ошибкой, либо той, с которой вы разговариваете.

Решения:

  • Удалите TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 из доступных шифров на сервере (не требуется перераспределение приложений).
  • Удалите TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 из списка шифров, предлагаемых клиентом (во время CLIENT_HELLO).

Вы можете сделать это на стороне клиента, выполнив свой собственный SSLSocketFactory и позвонив

sslSocket.setEnabledCipherSuites(String[] suites);

при создании SSLSocket.

edit: обратите внимание, что это не обязательно ошибка Android, возможно, что реализация сервера ошибочна. если ваша проблема действительно вызвана шифром, пожалуйста, оставьте комментарий к трекеру с ошибкой android] (https://code.google.com/p/android/issues/detail?id=81603). спасибо!

Ответ 2

Я попытался изменить cipherSuites в пользовательском сокете factory, но это не помогло. В моем случае мне пришлось удалить протоколы TLSv1.1 и TLSv1.2 из сокета EnabledProtocols. Похоже, что некоторые старые серверы не очень хорошо обрабатывают протокольные переговоры для новых протоколов. Существуют различные примеры создания пользовательского сокета factory, такого как Как переопределить шифрованный список, отправленный на сервер Android при использовании HttpsURLConnection?, и другие для сокетов Apache. Это делается, я просто вызвал следующий метод AdjustSocket для удаления протоколов.

private void AdjustSocket(Socket socket)
{
    String[] protocols = ((SSLSocket) socket).getSSLParameters().getProtocols();
    ArrayList<String> protocolList = new ArrayList<String>(Arrays.asList(protocols));

    for (int ii = protocolList.size() - 1; ii >= 0; --ii )
        {
        if ((protocolList.get(ii).contains("TLSv1.1")) || (protocolList.get(ii).contains("TLSv1.2")))
            protocolList.remove(ii);
        }

    protocols = protocolList.toArray(new String[protocolList.size()]);
    ((SSLSocket)socket).setEnabledProtocols(protocols);
}