Enabling specific SSL protocols with Android WebViewClient

20,193

Solution 1

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

Solution 2

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.

Solution 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();
        }
    }
}
Share:
20,193

Related videos on Youtube

user802467
Author by

user802467

Updated on July 09, 2022

Comments

  • user802467
    user802467 almost 2 years

    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.

    • Robert
      Robert over 9 years
      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).
    • cynod
      cynod almost 9 years
      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.
    • KK_07k11A0585
      KK_07k11A0585 almost 9 years
      @user802467 Did you find solution for the issue ?? I am also facing the same issue
    • user802467
      user802467 almost 9 years
      @KK_07k11A0585 No, I couldn't find a way to configure this in WebViewClient. I think comment above from Robert is correct.
    • Ultimo_m
      Ultimo_m almost 6 years
  • Lucas Crawford
    Lucas Crawford over 8 years
    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
  • ua741
    ua741 over 8 years
    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.
  • Pavel Biryukov
    Pavel Biryukov about 8 years
    Do you mean "...to support TLS 1.1" (instead of 1.0) ?
  • frank
    frank almost 8 years
    Note: This doesn't seem to resolve the issue with 4.1.2 clients.
  • ar-g
    ar-g almost 8 years
    Unfortunately, I only see plain text in my webView; it probably depends on the execution of javaScript. Also found this post artemzin.com/blog/android-webview-io
  • Alexander Zar
    Alexander Zar over 7 years
    @ar-g Have you solved your problem?Please let me know if you done it.
  • ar-g
    ar-g over 7 years
    @AlexanderZarovniy the problem was in 3rd-party service, we use different scheme for android below 4.4(it's not related to android)
  • Agent_L
    Agent_L over 7 years
    @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.
  • frank
    frank almost 7 years
    Does this still work for anyone? I've noticed my 4.4 devices no longer load TLS 1.2 served content.
  • Nick
    Nick almost 6 years
    Welcome to SO. Your answer appears to suggest much the same as the first comment on the OP. See stackoverflow.com/help/how-to-answer for guidance.
  • Johnny Five
    Johnny Five about 5 years
    It says Disabled by default​ from 4.1 to 4.4. So how to enable it?