HttpClient 在 Android 5.0 Lollipop 中因握手失败而失败
Posted
技术标签:
【中文标题】HttpClient 在 Android 5.0 Lollipop 中因握手失败而失败【英文标题】:HttpClient fails with Handshake Failed in Android 5.0 Lollipop 【发布时间】:2015-01-22 14:13:46 【问题描述】:android 5.0 Lollipop 中的 DefaultHttpClient 似乎已损坏。它无法设置与以前版本的 Android 成功设置的某些站点的连接。
例如我尝试连接到https://uralsg.megafon.ru
//Create httpclient like in https://***.com/questions/18523784/ssl-tls-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 closed by peer。 当然这是可以理解的,因为 Android 5.0 试图用 TLSv1.2 和现代密码连接这个旧服务器,但它不支持它们。
好的,使用SSL/TLS protocols and cipher suites with the 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 上运行流畅。
我用wireshark检查了流量:
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)
您可以看到连接已建立,但服务器发出警报,因为它可能无法解码加密的握手消息。
我没有设法在 Android 5.0 上使用 HttpClient 连接到 https://uralsg.megafon.ru。股票浏览器确实连接它。 Android 2.3-4.4 以任何方式连接本网站没有任何困难。
有什么方法可以让 HttpClient 连接这些网站?这只是一个例子,我相信有很多遗留服务器无法通过 Android 5.0 和 HttpClient 连接。
【问题讨论】:
我遇到了同样的问题。如果我知道解决方案,我将添加 cmets。真的很烦 【参考方案1】:更新:原来是后端的一个错误,而不是 android 5,尽管确实存在问题的密码。
我遇到了同样的问题。对我来说,它原来是密码TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
,它是从 android 5 的(更新的)默认密码集中选择的。
一旦我将其从可接受密码的客户端列表中删除,连接就会再次起作用。
android 5 change log 提到:
AES-GCM (AEAD) 密码套件现已启用,
我很确定这是罪魁祸首。只要TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
被首选(由服务器),连接就会失败。
请注意,TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
有效。
我的猜测是TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
的 Android 实现有问题,或者您正在与之交谈的服务器之一。
解决方案:
-
从服务器上的可用密码中删除
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
(无需重新部署应用)。
从客户端提供的密码列表中删除TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
(在CLIENT_HELLO
期间)。
您可以在客户端通过实现您自己的 SSLSocketFactory 并调用来做到这一点
sslSocket.setEnabledCipherSuites(String[] suites);
关于 SSLSocket 创建。
编辑:请注意,这不一定是 android 错误,可能是服务器实现有问题。如果您的问题确实是由密码引起的,请在 android bug tracker 上发表评论](https://code.google.com/p/android/issues/detail?id=81603)。谢谢!
【讨论】:
我的情况并非如此。就我而言,该站点仅支持 SSL_RSA_WITH_RC4_128_MD5。当我建议它使用 SSL_RSA_WITH_RC4_128_MD5 它在 Lollipop 上失败并在 Jelly Bean 及以下成功连接。 据我所知,出于安全考虑,SSL_RSA_WITH_RC4_128_MD5 已从棒棒糖中删除!见code.google.com/p/android-developer-preview/issues/… 我知道,但问题是手动添加并不能解决问题。 删除对我的情况没有帮助:( @DmitryKochin 您可以尝试启用所有支持的密码套件,这可能会有所帮助。详情:***.com/a/30742240/2055854.【参考方案2】:我尝试在自定义套接字工厂中更改密码套件,但这没有帮助。就我而言,我必须从套接字的 EnabledProtocols 中删除 TLSv1.1 和 TLSv1.2 协议。似乎一些较旧的服务器不能很好地处理新协议的协议协商。有各种用于创建自定义套接字工厂的示例,例如How to override the cipherlist sent to the server by Android when using 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);
【讨论】:
我当然手动将协议限制为 SSLv3、TLSv1、TLSv1.1 或更高版本之一,但对我来说没有帮助。以上是关于HttpClient 在 Android 5.0 Lollipop 中因握手失败而失败的主要内容,如果未能解决你的问题,请参考以下文章
Apache HttpComponents HttpClient 5.0 - Kerberos SPNEGO 客户端
在httpclient5中实现HttpRequestRetryHandler和RequestBuilder
Android studio 中想用 HttpClient 下载网络图片,却没有 HttpClient 以及相关的类