Given an Android app that uses httpclient to connect to a third party web site, how can I get a successful SSL connection from an older Android device?
Earlier, both current and older Android versions worked, but the third party web site changed their SSL configuration. This precipitated an error, but only on the older Android platforms:
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)
After doing some research (google issue tracker), I thought I might try to remove SSLv3
from the list of available protocols. I have been able to do that, and also have narrowed the number of cipher suites, but still have the SSLException
problem.
The code, which uses httpclient-4.4.1.2
, is patched to allow SNI, and works on newer Android (API 22), but not on an older Android (API 15) is this:
nHttpclient = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).setDefaultCookieStore(nCookieStore).build();
Here is some code I've been experimenting with. Although I am able to remove SSLv3, I'm still unable to establish a secure connection:
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();
The above code limits the protocols to include TLSv1
(no longer includes SSLv3
. Also, I have temporarily altered SSLConnectionSocketFactory.createLayeredSocket()
so that the socket only includes cipher suites included in both new and old Android platforms. In other words, I removed a bunch of suites that were not reported as available on a new Android platform.
So, limiting to TLSv1
and limiting to newer cipher suites, I'm still getting the error and am not able to establish a secure connection.
Logging on older 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
The same code running on newer 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
Summary
It must be a common problem to enable older Android platforms to be able to use httpclient-4.4.1.2
(or similar) when the sites being accessed have been updated to newer SSL/TLS standards.
My experiments with API 15
indicate that having httpclient code that manages SNI, where SSLv3
is removed, and where older cipher suites are removed still does not allow a secure connection, but instead throws a SSLException
in SSL_do_handshake()
.
If I try to force the old Android to use TLSv1.1, I get:
java.security.NoSuchAlgorithmException: SSLContext TLSv1.1 implementation not found
My research indicates that API 15
or lower is just never going to work with TLSv1.1
and higher, but that API 16
through API 19
might work, but the newer protocol is not enabled by default. API 20
and higher, the newer protocol is enabled by default.
What needs to be done to allow a secure connection from httpclient on Android OS's in the API 16
range?