2
votes

I have an android application (4.4.4+) that uses the following libraries:

compile 'com.squareup.okhttp3:okhttp:3.2.0'
compile 'com.squareup.okhttp3:okhttp-urlconnection:3.2.0'
compile 'com.squareup.okio:okio:1.7.0'

I have nginx (version 1.9.14) compiled with OpenSSL (version 1.0.2g source compiled) usage of openSSL confirmed with nginx -V on centOS 7. Configuration of nginx http2 enabled for the reverse proxy:

 server {
            listen  443 ssl http2;
            ssl     on;
            server_name     mobile.site.com;
            ssl_certificate         /etc/pki/tls/certs/start_cert.pem;
            ssl_certificate_key     /etc/pki/tls/certs/start_cert.key;
            proxy_cache     one;
            underscores_in_headers  on;
            access_log      /var/log/nginx/ssl.log ssl_custom;
            location / {
                    proxy_set_header X-Real-IP $remote_addr;
                    proxy_set_header X-Forwarded-For $remote_addr;
                    proxy_set_header Host $host;
                    proxy_cache_bypass $cookie_nocache $arg_nocache $arg_comment;
                    proxy_pass https://10.10.1.31:443;
            }
    }

Running command on server:

echo | openssl s_client -alpn h2 -connect

Returns:

No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 3718 bytes and written 442 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 4096 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
ALPN protocol: h2

Browsing an API URL from any modern browser results an HTTP/2 connection showing in the SSL logs and the HTTP/2 chrome add-in. Assuming HTTP/2 configured correctly in nginx / OpenSSL.

Android OKHttp clients refuse to HTTP/2:

            ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
                    .tlsVersions(TlsVersion.TLS_1_2)
                    .build();
            okHttpClient = new OkHttpClient.Builder()
                    .followSslRedirects(true)
                    .protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1))
                    .connectionSpecs(Collections.singletonList(spec))
                    .sslSocketFactory(new TLSSocketFactory((SSLSocketFactory) TLSSocketFactory.getDefault()))
                    .retryOnConnectionFailure(true)
                    .connectTimeout(25, TimeUnit.SECONDS)
                    .connectionPool(new ConnectionPool(30, 120, TimeUnit.SECONDS))
                    .addInterceptor(new AddCookiesInterceptor())
                    .addInterceptor(new ReceivedCookiesInterceptor())
                    .build();

I have written a custom TLS Socket Factory to enable usage of TLS 1.2 on <= Android 4.4.4.

TLSv1.2 does actually work on 4.4.4+ with this custom socket factory, but I have also tried running on 5.0+ with and without the custom socket factory, no HTTP/2 luck.

HTTP/2 fails on kit-kat, lollipop and marshmallow, but works on "N" developer preview (a.k.a. New York Cheesecake). No errors are thrown, just only ever connects as HTTP/1.1. I am having trouble finding any articles that relate to OKhttp and HTTP/2, most help questions / posts just say use ALPN, but with no resources to elaborate on the usage. OkHTTP documentation is not clear on HTTP/2 usages and deployment practices, but the main page says it supports it. Any help or expert guidance would be greatly appreciated, even just a nudge in the right direction would be a huge help. Please let me know if I have missed anything that may help in the process, thanks in advance!

Update 1: I found one of the SO questions that relate to this, with a very vague answer: [SO link] How to use http/2 with Okhttp on Android devices? with a comment that makes a suggestion to use jetty, but only in regards to desktop usage, not Android usage.

Update 2: This article shows that ALPN is enabled with Android 4.4: https://github.com/http2/http2-spec/wiki/ALPN-Status

Update 3: Was able to connect HTTP/2 using +5.0, but not 4.4.4. I don't need to go any lower than 4.4.4, as the in-house target devices are the concern for http/2 and they run 4.4.4. I was able to update the app to use the Google Play Services security protocol as the OKHttp Socket Connection, but the 4.4.4 devices we are using are not Google enabled, so I can't use PlayServices security sockets on these.

1
Did some more research on the ciphers being used trying to find a pattern: 4.4.4: TLSv1.2 ECDHE-RSA-AES128-SHA - 5.1.1: TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 - 6.0: TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 -7.0: TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 - Chrome/FireFox: TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256. Only Chrome/FireFox & "7.0(N)" connects using HTTP/2.DoctorD
Any updates on this? Were you able to get http2 on 4.4.4 or are you going with some other alternative?Zasz
I moved on to other things at the moment, but I want to revisit here soon. I may look tomorrow and see what I can do with any updated libraries.DoctorD
There seems to be a new (extended) method in the OkHttpClient.Builder "sslSocketFactory". The original method (depreciated) only required a sslSocketFactory, however the new method needs sslSocketFactory & X509TrustFactory. There may be something here, but I'll have to investigate next build cycle if this has any impact. My limited understanding is that Android's (< 5.0) built in cert provider doesn't have the correct way to handshake on the HTTP2 SSL certs. There may be a way to use Google Play's cert provider, but my target devices don't have GMS enabled.DoctorD

1 Answers

0
votes

According to swankjesse,

Next steps: Don't use ALPN on Android 4.4 and earlier; it's broken there. Restore NPN for versions of Android where it was previously stable.

Don't use broken ALPN on Android 4.4