android - 在较旧的 Android 平台上使用 cz.msebera.android.httpclient 时防止 SSL_do_handshake 失败

标签 android sockets ssl apache-httpclient-4.x sslhandshakeexception

给定一个使用 httpclient 连接到第三方网站的 Android 应用程序,我如何才能从旧的 Android 设备成功建立 SSL 连接?

早些时候,当前和旧的 Android 版本都可以工作,但第三方网站更改了他们的 SSL 配置。这引发了一个错误,但仅限于较旧的 Android 平台:

System.err: javax.net.ssl.SSLException: Connection closed by peer
System.err: at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
System.err: at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:406)

经过一些研究 ( google issue tracker ),我想我可能会尝试从可用协议(protocol)列表中删除 SSLv3。我已经能够做到这一点,并且还缩小了密码套件的数量,但仍然存在 SSLException 问题。

使用 httpclient-4.4.1.2 的代码已修补以允许 SNI,并且适用于较新的 Android (API 22),但不适用于较旧的 Android (API 15) 是这样的:

            nHttpclient = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).setDefaultCookieStore(nCookieStore).build();

这是我一直在试验的一些代码。尽管我能够删除 SSLv3,但我仍然无法建立安全连接:

            HttpClientBuilder clientBuilder = HttpClientBuilder.create();
            SSLContextBuilder contextBuilder = SSLContextBuilder.create();
            contextBuilder.useProtocol("TLSv1");
            SSLContext sslContext = contextBuilder.build();
            clientBuilder.setSslcontext(sslContext);
            nHttpclient = clientBuilder.setRedirectStrategy(new LaxRedirectStrategy()).setDefaultCookieStore(nCookieStore).build();

上面的代码将协议(protocol)限制为包括TLSv1(不再包括SSLv3。另外,我临时更改了SSLConnectionSocketFactory.createLayeredSocket()因此套接字仅包含新旧 Android 平台中包含的密码套件。换句话说,我删除了一堆未报告为在新 Android 平台上可用的套件。

因此,限制为 TLSv1 并限制为较新的密码套件,我仍然遇到错误并且无法建立安全连接。

在旧版 Android 上登录:

cz.msebera.android.httpclient.conn.ssl.SSLConnectionSocketFactory: Enabled protocols: [TLSv1]
cz.msebera.android.httpclient.conn.ssl.SSLConnectionSocketFactory: !!Enabled cipher suites:[TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
cz.msebera.android.httpclient.conn.ssl.SSLConnectionSocketFactory: Starting handshake
cz.msebera.android.httpclient.impl.conn.DefaultManagedHttpClientConnection: http-outgoing-1: Shutdown connection
cz.msebera.android.httpclient.impl.execchain.MainClientExec: Connection discarded
cz.msebera.android.httpclient.impl.conn.DefaultManagedHttpClientConnection: http-outgoing-1: Close connection

在较新的 Android 上运行相同的代码:

cz.msebera.android.httpclient.conn.ssl.SSLConnectionSocketFactory: Enabled protocols: [TLSv1, TLSv1.1, TLSv1.2]
cz.msebera.android.httpclient.conn.ssl.SSLConnectionSocketFactory: Enabled cipher suites:[TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_RC4_128_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
cz.msebera.android.httpclient.conn.ssl.SSLConnectionSocketFactory: Starting handshake
cz.msebera.android.httpclient.conn.ssl.SSLConnectionSocketFactory: Secure session established

总结

当访问的站点已更新为较新的 SSL/TLS 标准时,使较旧的 Android 平台能够使用 httpclient-4.4.1.2(或类似的)一定是一个常见问题。

我对 API 15 的实验表明,拥有管理 SNI 的 httpclient 代码,其中 SSLv3 被删除,旧的密码套件被删除仍然 不允许安全连接,而是在 SSL_do_handshake() 中抛出 SSLException

如果我试图强制旧 Android 使用 TLSv1.1,我会得到:

java.security.NoSuchAlgorithmException: SSLContext TLSv1.1 implementation not found

我的 research indicates API 15 或更低版本永远不会与 TLSv1.1 和更高版本一起使用,但是 API 16API 19 可能有效,但默认情况下未启用较新的协议(protocol)。 API 20 及更高版本,较新的协议(protocol)默认启用

要在 API 16 范围内的 Android 操作系统上允许来自 httpclient 的安全连接,需要做什么?

最佳答案

问题是 httpclient-android 代码使用 getEnabledProtocols() 而不是 getSupportedProtocols()

work around 我雇用的是获得 source code for smarek httpclient-android ,将其拉入我项目的模块中,删除对 httpclient-android 预构建版本的引用,然后更改 SSLConnectionSocketFactory.java 中的一行:

    //final String[] allProtocols = sslsock.getEnabledProtocols();
    final String[] allProtocols = sslsock.getSupportedProtocols();

这具有允许连接到我试图通过 API 16、17、18 和 19 访问的第三方网站的效果。

我认为从 API 15 或更低版本访问 TLSv1.1 或 TLSV1.2 是“不可能的”,除非您愿意在 Android 操作系统级别进行一些重大更改。

但这个简单的更改允许在旧版 Android 上运行的应用程序访问不再支持旧版 SSL 和 TLSv1 的网站。

关于android - 在较旧的 Android 平台上使用 cz.msebera.android.httpclient 时防止 SSL_do_handshake 失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43825807/

相关文章:

java - Java中的异步IO?

javascript - Angular + Node(Express) + SSL 集成

android - QT 和 Android 8.0 : Error creating SSL context () 问题

java - SSL 客户端身份验证 - 即使我的客户端证书与 'Cert Authorities' 中的列表匹配,也找不到合适的证书

java - 在 ListView 元素内绘制到 Canvas

java - 在可跨越的字符串中搜索时,如何忽略要突出显示的字符的大小写?

Python 套接字 - "blocking"套接字操作到底是什么?

c# - .NET Socket.Receive() 没有从 Java SocketServer 接收大数据流

android - DBFlow 一对多示例

android - 在 android programaticall/dynamically 中将单选按钮添加到 tableLayout 中的单选组时出错?