java 使用 Android WebViewClient 启用特定的 SSL 协议

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28329652/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 13:24:28  来源:igfitidea点击:

Enabling specific SSL protocols with Android WebViewClient

javaandroidsslhttpswebviewclient

提问by user802467

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

我的应用程序使用 WebViewClient 与服务器建立 SSL 连接。服务器配置为仅接受 TLSv1.1 及以上协议。

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.

1) 如何检查哪些 SSL 协议是 a) 支持和 b) 在设备上使用 Android WebViewClient 时默认启用。
2) 如何为我的应用程序中使用的 Android WebViewClient 实例启用特定的 SSL 协议。

On one of the test devices running Android 4.3, WebViewClient throws onReceivedError callback with 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

在运行 Android 4.3 的其中一台测试设备上,WebViewClient 抛出 onReceivedError 回调,描述为“无法执行 SSL 握手”
Chrome 日志如下:
01-29 15:58:00.073 5486 5525 Wchromium_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:105.85/external Chromium E-105/85 chromium/net/socket/ssl_client_socket_openssl.cc:792: [0129/155800:ERROR:ssl_client_socket_openssl.cc(792)] 握手失败;返回 0,SSL 错误代码 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[])

我的应用程序还使用 HttpClient 和 HttpsUrlConnection 类来设置 SSL 连接。在使用这些类时,我能够使用 SSLSocket API 来启用特定的协议。 http://developer.android.com/reference/javax/net/ssl/SSLSocket.html#setEnabledProtocols(java.lang.String[])

I need to do the same with WebViewClient.

我需要对 WebViewClient 做同样的事情。

回答by M. C.

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.

根据文档,Android < 4.3 中的 WebView 不可能支持 TLS 1.0。对于 Android 4.4,默认情况下它是禁用的。

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

查看此图表以了解不同浏览器对 TLS 1.0 的支持:https: //en.wikipedia.org/wiki/Transport_Layer_Security#Web_browsers

回答by iagreen

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. ProviderInstalleris included in the -basepackage.

如果您的应用正在使用,或者您愿意使用 Google Play 服务,您可以通过安装旧手机的Provider. 很容易安装,只有一行(加上异常处理等)。如果您还没有谷歌播放服务,您还需要将其添加到您的 gradle 文件中。 ProviderInstaller包含在-base包中。

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.

有关完整示例,请参阅Google 的“更新您的安全提供程序以防止 SSL 漏洞”

回答by Alex

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

实际上,我设法使它工作,但是您需要 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);

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

此外,您还需要经典的 Trust Manager Manipulator、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 Socket Factory:

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

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

回答by lemon

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

这是因为 android 4.3 不支持 TSL 1.1 但只有 TSL1.0 阅读这篇文章 https://www.ssllabs.com/ssltest/clients.htmlfind android 4.3 会看到

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

协议 TLS 1.3 否 TLS 1.2 否 TLS 1.1 否 TLS 1.0 是 SSL 3 INSECURE 是 SSL 2 否