Включение определенных протоколов SSL с помощью Android WebViewClient

Мое приложение использует WebViewClient для подключения SSL к серверу. Сервер настроен на прием только протоколов TLSv1.1 и выше.

1) Как проверить, какие протоколы SSL а) поддерживаются и б) включены по умолчанию при использовании Android WebViewClient на устройстве.
2) Как включить определенные протоколы SSL для экземпляра Android WebViewClient, используемого в моем приложении.

На одном из тестовых устройств под управлением Android 4.3 WebViewClient вызывает обратный вызов onReceivedError с описанием «Не удалось выполнить рукопожатие SSL».
Журналы Chrome выглядят следующим образом:
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)] Возврат к SSLv3, поскольку хост не поддерживает TLS: 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)] рукопожатие не удалось; возвращено 0, код ошибки SSL 5, net_error -107

Мое приложение также использует классы HttpClient и HttpsUrlConnection для настройки соединений SSL. Я смог использовать API SSLSocket для включения определенных протоколов при использовании этих классов. http://developer.android.com/reference/javax/net/ssl/SSLSocket.html#setEnabledProtocols(java.lang.String[])

Мне нужно сделать то же самое с WebViewClient.


person user802467    schedule 04.02.2015    source источник
comment
Насколько я знаю, включенные протоколы WebView не могут быть изменены приложением. Какие протоколы поддерживаются или нет, зависит от версии Android. Для TLS 1.1 и выше требуется Android 4.4 или новее (см. здесь< /а>).   -  person Robert    schedule 04.02.2015
comment
Кто-нибудь когда-нибудь понял это? В настоящее время я работаю над проектом, в котором нам нужно перейти с TLS v1.2 на 1.1 для трех непроизводственных тестовых серверов. Мы пытаемся использовать что-то вроде этого: adb shell 'echo browser --show-fps-counter --ssl-version-min=tls1.1 --ssl-version-max=tls1.1 › /data/local/ tmp/webview-command-line' Счетчик FPS отображается на планшете с Android L, но не на Android TV (что странно, поскольку они должны иметь один и тот же код веб-просмотра). Флаги ssl-версии не работают; захват tcpdump с устройства по-прежнему показывает, что используется TLS v1.2. Любая помощь будет здорово.   -  person cynod    schedule 29.07.2015
comment
@user802467 user802467 Вы нашли решение проблемы ?? Я тоже сталкиваюсь с той же проблемой   -  person KK_07k11A0585    schedule 04.08.2015
comment
@ KK_07k11A0585 Нет, я не смог найти способ настроить это в WebViewClient. Я думаю, что комментарий выше от Роберта правильный.   -  person user802467    schedule 04.08.2015
comment
blog.dev-area.net/2015/08/13/   -  person Ultimo_m    schedule 19.06.2018


Ответы (4)


Согласно документации НЕ возможно поддерживать TLS 1.0 в WebView в Android ‹ 4.3. В Android 4.4 он по умолчанию отключен.

Проверьте эту таблицу для поддержки TLS 1.0 в разных браузерах: https://en.wikipedia.org/wiki/Transport_Layer_Security#Web_browsers

person M. C.    schedule 17.11.2015
comment
Вы имеете в виду... для поддержки TLS 1.1 (вместо 1.0)? - person Pavel Biryukov; 12.02.2016
comment
Там написано Disabled by default​ с 4.1 на 4.4. Итак, как его включить? - person Johnny Five; 24.04.2019

Если ваше приложение использует или вы хотите использовать сервисы Google Play, вы можете использовать новые функции безопасности на старых телефонах, установив их Provider. Его легко установить, всего одна строка (плюс обработка исключений и т. д.). Вам также нужно будет добавить сервисы Google Play в файл Gradle, если у вас его еще нет. ProviderInstaller входит в пакет -base.

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

Полный пример см. в разделе "Обновление поставщика безопасности для защиты от эксплойтов SSL" от Google.

person iagreen    schedule 14.11.2015
comment
Примечание. Это синхронный вызов, который вызывается в основном потоке (потоке пользовательского интерфейса). Вы можете запустить этот вызов в SyncAdapter, который работает в фоновом режиме, или в AsyncTask, чтобы выполнить эту попытку обновления, или вызвать, используя версию этого вызова installIfNeededAsync, чтобы избежать блокировки основного потока. - person Lucas Crawford; 17.11.2015
comment
Кроме того, даже после успешной установки провайдера вам необходимо явно включить TLS 1.2/TLS 1.1 при выполнении запроса https в версиях Android между [16-20]. Я проверил это на устройстве KITKAT. - person ua741; 08.01.2016
comment
Примечание. Похоже, это не решает проблему с клиентами 4.1.2. - person frank; 08.06.2016
comment
@frank Самая низкая версия, которую мне удалось заставить работать, была 4.4.2. Перехват создания сокета и вызов setEnabledProtocols кажется единственным решением. - person Agent_L; 31.01.2017
comment
Это все еще работает для кого-то? Я заметил, что мои устройства 4.4 больше не загружают контент, обслуживаемый TLS 1.2. - person frank; 20.05.2017

На самом деле мне удалось заставить его работать, но для этого вам понадобится библиотека okHttp. Попробуйте это, когда вы настраиваете активность браузера:

    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);

Кроме того, вам понадобится классический манипулятор Trust Manager, фабрика сокетов SSL и ее реализация в вашем классе Application:

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:

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;
    }
}

Класс приложения:

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();
        }
    }
}
person Alex    schedule 27.07.2016
comment
К сожалению, в моем webView я вижу только обычный текст; это, вероятно, зависит от выполнения javaScript. Также нашел этот пост artemzin.com/blog/android-webview-io - person ar-g; 27.07.2016
comment
@ar-g Вы решили свою проблему? Пожалуйста, дайте мне знать, если вы это сделали. - person Alexander Zar; 17.10.2016
comment
@AlexanderZarovniy проблема была в стороннем сервисе, мы используем другую схему для андроида ниже 4.4 (это не связано с андроидом) - person ar-g; 17.10.2016

это потому, что Android 4.3 не поддерживает TSL 1.1, а только TSL1.0, прочитайте эту статью https://www.ssllabs.com/ssltest/clients.html найти Android 4.3 увидит

Протоколы TLS 1.3 Нет TLS 1.2 Нет TLS 1.1 Нет TLS 1.0 Да SSL 3 НЕЗАЩИЩЕННЫЙ Да SSL 2 Нет

person lemon    schedule 16.07.2018
comment
Добро пожаловать в СО. Ваш ответ, кажется, предполагает то же самое, что и первый комментарий к ОП. См. инструкции на stackoverflow.com/help/how-to-answer. - person Nick; 16.07.2018