HttpClient失败,握手在Android 5.0中失败棒棒糖

问题描述:

Android 5.0中的DefaultHttpClient棒棒糖似乎被打破。它无法将连接设置为由以前版本的Android成功设置的某些网站。HttpClient失败,握手在Android 5.0中失败棒棒糖

例如我尝试连接到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(装置和仿真器)。 当然这是可以理解的,因为Android 5.0试图将这个旧服务器与TLSv1.2和现代密码连接起来,但它不支持它们。

好的,使用SSL/TLS protocols and cipher suites with the AndroidHttpClient我们限制协议和加密到使用TLSv1SSL_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连接。

+0

我得到了与此相同的问题。如果我知道解决方案,我会添加评论。这真的很烦人 – cmcromance 2014-12-01 01:14:29

更新:原来是在后端的错误,而不是android 5,虽然的确有问题的密码。

我有同样的问题。对我来说,它变成了密码TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,它是从android 5的(更新的)一组默认密码中选择的。

只要我从可接受的密码客户端列表中删除它,连接再次工作。

android 5 change log提到:

  • AES-GCM (AEAD) cipher suites are now enabled,

我敢肯定这是罪魁祸首。只要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是越野车,还是您正在与之交谈的服务器之一。

解决方案:

  1. 从服务器(无需重新部署应用程序)上可用的密码删除TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
  2. 从客户端提供的密码列表中删除TLS_DHE_RSA_WITH_AES_256_GCM_SHA384(在CLIENT_HELLO期间)。

您可以通过实现自己的SSLSocketFactory和创建的SSLSocket调用

sslSocket.setEnabledCipherSuites(String[] suites); 

做在客户端。

编辑:请注意,这不一定是一个android bug,它可能是服务器实现有问题。如果您的问题确实是由密码造成的,请在Android bug跟踪器上留下评论](https://code.google.com/p/android/issues/detail?id=81603)。谢谢!

+1

在我的情况并非如此。在我的情况下,该站点仅支持SSL_RSA_WITH_RC4_128_MD5。当我建议使用SSL_RSA_WITH_RC4_128_MD5时,它会在棒棒糖上失败,并在Jelly Bean和以下版本上成功连接。 – 2015-01-03 12:52:38

+0

据我所知,由于安全考虑,SSL_RSA_WITH_RC4_128_MD5已从棒棒糖中删除!请参阅https://code.google.com/p/android-developer-preview/issues/detail?id=1200#c8 – stefs 2015-01-05 14:26:03

+0

我知道,但问题是手动添加它并不能解决问题。 – 2015-01-06 15:19:12

我试着改变自定义套接字工厂中的cipherSuites,但这没有帮助。就我而言,我必须从套接字的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); 
} 
+0

我限制协议SSLv3 ,TLSv1,TLSv1.1或以上手动,当然,但它并没有帮助我的情况。 – 2015-06-07 18:47:25