Making SSLEngine use TLSv1.2 on Android (4.4.2)?

Glen picture Glen · Jun 23, 2014 · Viewed 29.9k times · Source

Folks, I'm hoping there's something obvious that I'm missing, and I hope someone will be able to shed some light. I'm trying to get TLSv1.2 running in an SSL + NIO context (using the AndroidAsync library), so I'm trying to enable it via an SSLEngine. I can run code like this:

        SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
        sslContext.init(null, null, null);
        String[] protocols = sslContext.getSupportedSSLParameters().getProtocols();
        for (String protocol : protocols) {
            Timber.d("Context supported protocol: " + protocol);
        }

        SSLEngine engine = sslContext.createSSLEngine();
        String[] supportedProtocols = engine.getSupportedProtocols();
        for (String protocol : supportedProtocols) {
            Timber.d("Engine supported protocol: " + protocol);
        }

And I end up seeing this on logcat:

06-22 21:56:27.715    1117-1117/? D/XXX﹕ Context supported protocol: SSLv3
06-22 21:56:27.715    1117-1117/? D/XXX﹕ Context supported protocol: TLSv1
06-22 21:56:27.725    1117-1117/? D/XXX﹕ Context supported protocol: TLSv1.1
06-22 21:56:27.725    1117-1117/? D/XXX﹕ Context supported protocol: TLSv1.2
06-22 21:56:27.735    1117-1117/? D/XXX﹕ Engine supported protocol: TLSv1
06-22 21:56:27.745    1117-1117/? D/XXX﹕ Engine supported protocol: SSLv3

Of course if I try to engine.setEnabledProtocols(new String[] { "TLSv1.2" }) I get an IllegalArgumentException "Protocol TLSv1.2 is not supported."

I can see the context claims to support TLSv1.2, but then the engine I make from that context doesn't? What's going on here? None of this changes if I use "TLS" instead of "TLSv1.2" in the first line above, btw.

I gather this might have something to do with this issue, and I've read this (as yet unanswered) question and articles like this, but they don't quite seem to be hitting the spot - solutions I've seen all seem to rely on SSLSocket rather than SSLEngine.

Thanks much for any knowledge you can drop.

UPDATE 6/23/14 10AMEDT

So I found SSLEngine.setSSLParameters, which I was hoping would let me pass in an SSLParameters that I got from SSLContext.getSupportedSSLParameters(), but when I call that I get an exception that claims the cipher suites aren't supported, so it looks like setSSLParameters() is just doing the same thing that setEnabledCipherSuites() does, and the engine is already in a state where it doesn't recognize the TLS 1.2 protocol/suites as supported.

Answer

Wolfgang Profer picture Wolfgang Profer · Oct 27, 2014

The Android API docs correctly state that TLSv1.2 is only supported for SSLEngine in API Level 20 or later (Lollipop) while SSLSocket supports it since level 16.

Using SSLSocket or requiring API 20 was no option for our project and neither was changing the server code to allow TLSv1 or SSLv3. Our solution was to install a newer security provider using Google Play Services:

    ProviderInstaller.installIfNeeded(getApplicationContext());

This effectively gives your app access to a newer version of OpenSSL and Java Security Provider which includes support for TLSv1.2 in SSLEngine. Once the new provider is installed, you can create an SSLEngine which supports SSLv3, TLSv1, TLSv1.1 and TLSv1.2 the usual way:

    SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
    sslContext.init(null, null, null);
    SSLEngine engine = sslContext.createSSLEngine();

Or you can restrict the enabled protocols using engine.setEnabledProtocols.