27

My application uses WebViewClient to make SSL connections to the server. The server is configured to only accept TLSv1.1 and above protocols.

  1. How do I check which SSL protocols are a) Supported and b) Enabled by default when using Android WebViewClient on a device.
  2. How do I enable specific SSL protocols for Android WebViewClient instance used in my application.

On one of the test devices running Android 4.3, WebViewClient throws onReceivedError callback with the following description:

"Failed to perform SSL handshake"

Chrome logs are as follows:

01-29 15:58:00.073 5486 5525 W chromium_net: external/chromium/net/http/http_stream_factory_impl_job.cc:865: [0129/155800:WARNING:http_stream_factory_impl_job.cc(865)] Falling back to SSLv3 because host is TLS intolerant: 10.209.126.125:443 01-29 15:58:00.083 5486 5525 E chromium_net: external/chromium/net/socket/ssl_client_socket_openssl.cc:792: [0129/155800:ERROR:ssl_client_socket_openssl.cc(792)] handshake failed; returned 0, SSL error code 5, net_error -107 

My application also uses HttpClient and HttpsUrlConnection classes to setup SSL Connections. I was able to use SSLSocket API to enable specific protocols when using these classes. http://developer.android.com/reference/javax/net/ssl/SSLSocket.html#setEnabledProtocols(java.lang.String[])

I need to do the same with WebViewClient.

user802467
  • 951
  • 2
  • 14
  • 22
  • 1
    AFAIK the enabled protocols WebView can not be cahnged by an App. What protocols are supported or not depends on the Android version. For TLS 1.1 and higher you need an Android 4.4 or newer (see [here](https://www.ssllabs.com/ssltest/viewClient.html?name=Android&version=4.3)). – Robert Feb 04 '15 at 19:36
  • Did anyone ever figure this out? I'm working on a project currently where we need to fallback from TLS v1.2 to 1.1 for three non-production test servers. We're trying to use something like this: adb shell 'echo "browser --show-fps-counter --ssl-version-min=tls1.1 --ssl-version-max=tls1.1" > /data/local/tmp/webview-command-line' The FPS counter shows on an Android L tablet but not on Android TV (which is weird as they should be the same webview code). The ssl-version flags don't seem to work; a tcpdump capture from the device still shows TLS v1.2 being used. Any help would be great. – cynod Jul 29 '15 at 19:27
  • @user802467 Did you find solution for the issue ?? I am also facing the same issue – KK_07k11A0585 Aug 04 '15 at 10:02
  • @KK_07k11A0585 No, I couldn't find a way to configure this in WebViewClient. I think comment above from Robert is correct. – user802467 Aug 04 '15 at 14:43
  • https://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/ – Ultimo_m Jun 19 '18 at 13:24

4 Answers4

8

As per documenation it is NOT possible to support TLS 1.0 in WebView in Android < 4.3. For Android 4.4 it is disabled by default.

Check this chart for support of TLS 1.0 in different browsers: https://en.wikipedia.org/wiki/Transport_Layer_Security#Web_browsers

parkerfath
  • 1,648
  • 1
  • 12
  • 18
M. C.
  • 139
  • 1
  • 3
3

If your app is using, or you are willing to use, Google Play services, you can use newer security features on older phones by installing their Provider. It is easy to install, only one line (plus exception handling, etc). You will also need to add google play services to your gradle file if you do not already have it. ProviderInstaller is included in the -base package.

try {
    ProviderInstaller.installIfNeeded(this);
} catch (GooglePlayServicesRepairableException e) {
     // Fix it
} catch (GooglePlayServicesNotAvailableException e) {
     // Skip it
}

For a full example, see "Updating Your Security Provider to Protect Against SSL Exploits" from Google.

iagreen
  • 31,470
  • 8
  • 76
  • 90
  • 1
    Note: This is the synchronous call that is called on the main thread (the UI thread). You can run this call in a SyncAdapter that operates in the background or an AsyncTask to perform this attempt to update, or call using the installIfNeededAsync version of this call to avoid blocking the main thread – Lucas Crawford Nov 17 '15 at 18:01
  • 2
    Also, even after installing the provider successfully, you need to enable TLS 1.2/TLS 1.1 explicitly while making https request in Android Versions between [16-20]. I verified this on a KITKAT device. – ua741 Jan 08 '16 at 17:57
  • 2
    Note: This doesn't seem to resolve the issue with 4.1.2 clients. – frank Jun 07 '16 at 21:05
  • 1
    @frank lowest version I managed to get it working was 4.4.2. Intercepting socket creation and calling `setEnabledProtocols` seems to be the only solution there. – Agent_L Jan 31 '17 at 13:06
  • Does this still work for anyone? I've noticed my 4.4 devices no longer load TLS 1.2 served content. – frank May 19 '17 at 23:10
3

Actually, I managed to make it work, but you need okHttp library for that. Try this when you're setting up browser activity:

    WebViewClient client = new WebViewClient() {
        private OkHttpClient okHttp = new OkHttpClient.Builder().build();

        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
            Request okHttpRequest = new Request.Builder().url(url).build();
            try {
                Response response = okHttp.newCall(okHttpRequest).execute();
                return new WebResourceResponse(response.header("Content-Type", "plain/text"), response.header("Content-Encoding", "deflate"), response.body().byteStream());
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    };
    webView.setWebViewClient(client);

Also, you'll need classic Trust Manager Manipulator, SSL socket factory and its implementation in your Application class:

public class TrustManagerManipulator implements X509TrustManager {


    private static TrustManager[] trustManagers;
    private static final X509Certificate[] acceptedIssuers = new X509Certificate[] {};

    public boolean isClientTrusted(X509Certificate[] chain) {
        return true;
    }

    public boolean isServerTrusted(X509Certificate[] chain) {
        return true;
    }

    public static void allowAllSSL()
    {

        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        });
        SSLContext context = null;
        if (trustManagers == null) {
            trustManagers = new TrustManager[] { new TrustManagerManipulator() };
        }
        try {
            context = SSLContext.getInstance("TLS");
            context.init(null, trustManagers, new SecureRandom());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
        HttpsURLConnection.setDefaultSSLSocketFactory(context
                .getSocketFactory());
    }

    public void checkClientTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
    }

    public void checkServerTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
    }

    public X509Certificate[] getAcceptedIssuers() {
        return acceptedIssuers;
    }
}

SSl Socket Factory:

public class TLSSocketFactory extends SSLSocketFactory {

    private SSLSocketFactory internalSSLSocketFactory;

    public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
        SSLContext context = SSLContext.getInstance("TLS");
        TrustManager[] managers = new TrustManager[] { new TrustManagerManipulator() };
        context.init(null, managers, new SecureRandom());
        internalSSLSocketFactory = context.getSocketFactory();
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return internalSSLSocketFactory.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return internalSSLSocketFactory.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
        return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort));
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
    }

    private Socket enableTLSOnSocket(Socket socket) {
        if(socket != null && (socket instanceof SSLSocket)) {
            ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
        }
        return socket;
    }
}

App class:

public class App extends Application {
    private static App appInstance;

    @Override
    public void onCreate() {
        super.onCreate();

        setupSSLconnections();
    }

    private void setupSSLconnections() {
        try {
            HttpsURLConnection.setDefaultSSLSocketFactory(new TLSSocketFactory());
        } catch (KeyManagementException | NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }
}
Alex
  • 31
  • 3
  • 1
    Unfortunately, I only see plain text in my webView; it probably depends on the execution of javaScript. Also found this post https://artemzin.com/blog/android-webview-io/ – ar-g Jul 27 '16 at 12:51
  • @ar-g Have you solved your problem?Please let me know if you done it. – Alexander Zar Oct 17 '16 at 14:44
  • @AlexanderZarovniy the problem was in 3rd-party service, we use different scheme for android below 4.4(it's not related to android) – ar-g Oct 17 '16 at 19:18
0

it's beause android 4.3 not support TSL 1.1 but only TSL1.0 read this article https://www.ssllabs.com/ssltest/clients.html find android 4.3 will see

Protocols TLS 1.3 No TLS 1.2 No TLS 1.1 No TLS 1.0 Yes SSL 3 INSECURE Yes SSL 2 No

lemon
  • 7
  • Welcome to SO. Your answer appears to suggest much the same as the first comment on the OP. See https://stackoverflow.com/help/how-to-answer for guidance. – Nick Jul 16 '18 at 13:45