使用 Android WebViewClient 启用特定 SSL 协议
Posted
技术标签:
【中文标题】使用 Android WebViewClient 启用特定 SSL 协议【英文标题】:Enabling specific SSL protocols with Android WebViewClient 【发布时间】:2015-04-04 11:07:13 【问题描述】:我的应用程序使用WebViewClient
与服务器建立 SSL 连接。
服务器配置为仅接受 TLSv1.1 及以上协议。
-
在设备上使用 android
WebViewClient
时,如何检查哪些 SSL 协议 a) 受支持和 b) 默认启用。
如何为我的应用程序中使用的 Android WebViewClient 实例启用特定 SSL 协议。
在其中一台运行 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)] 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
我的应用程序还使用 HttpClient
和 HttpsUrlConnection
类来设置 SSL 连接。在使用这些类时,我能够使用SSLSocket
API 来启用特定协议。
http://developer.android.com/reference/javax/net/ssl/SSLSocket.html#setEnabledProtocols(java.lang.String[])
我需要对 WebViewClient
做同样的事情。
【问题讨论】:
AFAIK 启用的协议 WebView 不能被应用程序更改。支持或不支持哪些协议取决于 Android 版本。对于 TLS 1.1 及更高版本,您需要 Android 4.4 或更高版本(请参阅here)。 有人知道这个吗?我目前正在开展一个项目,我们需要为三个非生产测试服务器从 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 上(这很奇怪,因为它们应该是相同的 webview 代码)。 ssl-version 标志似乎不起作用;从设备捕获的 tcpdump 仍显示正在使用 TLS v1.2。任何帮助都会很棒。 @user802467 您找到问题的解决方案了吗?我也面临同样的问题 @KK_07k11A0585 不,我找不到在 WebViewClient 中配置它的方法。我认为罗伯特的上述评论是正确的。 blog.dev-area.net/2015/08/13/… 【参考方案1】:这是因为 android 4.3 不支持 TSL 1.1 但只支持 TSL1.0 阅读这篇文章 https://www.ssllabs.com/ssltest/clients.html find android 4.3 会看到
协议 TLS 1.3 否 TLS 1.2 否 TLS 1.1 否 TLS 1.0 是 SSL 3 不安全 是 SSL 2 否
【讨论】:
欢迎来到 SO。您的回答似乎与对 OP 的第一条评论大致相同。请参阅***.com/help/how-to-answer 获取指导。【参考方案2】:根据文档,在 Android
查看此图表以了解不同浏览器对 TLS 1.0 的支持:https://en.wikipedia.org/wiki/Transport_Layer_Security#Web_browsers
【讨论】:
您的意思是“...支持 TLS 1.1”(而不是 1.0)? 它说Disabled by default
从 4.1 到 4.4。那么如何开启呢?【参考方案3】:
实际上,我设法让它工作,但你需要 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 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 套接字工厂:
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();
【讨论】:
不幸的是,我的 webView 中只看到纯文本;它可能取决于javascript的执行。还发现了这篇文章artemzin.com/blog/android-webview-io @ar-g 你的问题解决了吗?如果你解决了请告诉我。 @AlexanderZarovniy 问题出在 3rd-party 服务中,我们对 4.4 以下的 android 使用不同的方案(与 android 无关)【参考方案4】:如果您的应用正在使用或愿意使用 Google Play 服务,您可以通过安装 Provider
在旧手机上使用更新的安全功能。它很容易安装,只有一行(加上异常处理等)。如果您还没有它,您还需要将 google play 服务添加到您的 gradle 文件中。 ProviderInstaller
包含在 -base
包中。
try
ProviderInstaller.installIfNeeded(this);
catch (GooglePlayServicesRepairableException e)
// Fix it
catch (GooglePlayServicesNotAvailableException e)
// Skip it
有关完整示例,请参阅来自 Google 的 "Updating Your Security Provider to Protect Against SSL Exploits"。
【讨论】:
注意:这是在主线程(UI 线程)上调用的同步调用。您可以在后台运行的 SyncAdapter 或 AsyncTask 中运行此调用以执行此更新尝试,或使用此调用的 installIfNeededAsync 版本进行调用以避免阻塞主线程 此外,即使在成功安装提供程序之后,您也需要在 [16-20] 之间的 Android 版本中发出 https 请求时显式启用 TLS 1.2/TLS 1.1。我在 KITKAT 设备上验证了这一点。 注意:这似乎无法解决 4.1.2 客户端的问题。 @frank 我设法让它工作的最低版本是 4.4.2。拦截套接字创建和调用setEnabledProtocols
似乎是那里唯一的解决方案。
这仍然适用于任何人吗?我注意到我的 4.4 设备不再加载 TLS 1.2 提供的内容。以上是关于使用 Android WebViewClient 启用特定 SSL 协议的主要内容,如果未能解决你的问题,请参考以下文章
Android WebViewClient 回调调用过于频繁
HTTP状态码webview android, WebViewClient
在 Android WebViewClient 中获取空白页
Android - 在 AsyncTask 中设置 WebViewClient 会导致 NullPointerException