Android Webview SSL 自签名安全校验解决方案
- 服务器证书校验主要针对 WebView 的安全问题。
- 在 app 中需要通过 WebView 访问 url,因为服务器采用的自签名证书,而不是 ca 认证,使用 WebView 加载 url 的时候会显示为空白,出现无法加载网页的情况。
- 使用 ca 认证的证书,在 WebView 则可以直接显示出来,不需要特殊处理。
- 以往针对自签名证书的解决方案是继承 WebViewClient 重写 onReceivedSslError 方法,然后直接使用 handler.proceed(),该方案其实是忽略了证书,存在安全隐患。
- 安全的方案是当出现了证书问题的时候,读取 asserts 中保存的的根证书,然后与服务器校验,假如通过了,继续执行 handler.proceed(),否则执行 handler.cancel()。
WebViewClient 源码
- 当证书出现问题的时候,有 2 种情况。系统默认不加载该网页
handler.cancel()
handler.proceed()
public class WebViewClient { public void onReceivedSslError(WebView view, SslErrorHandler handler,
SslError error) {
handler.cancel();
}
... }
以往不安全的解决方案
当出现 ssl error 的时候,直接忽略,依旧打开网页。
继承系统的 WebViewClient , 重写 onReceiverSslError() ,改为 handler.process()
public class UnSafeWebViewClient extends WebViewClient
{
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
handler.proceed();
}
}
安全的解决方案
把服务器的证书放置在 assert 文件夹,当出现 ssl error 的时候进行读取,然后与服务器校验,校验通过了就加载该网页。校验不通过,不打开网页,进行安全提醒。
public class WebviewClient3 extends WebViewClient {
private Context context; public WebviewClient3(Context context) {
this.context = context;
} @Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
test12306(handler, view.getUrl());
} // 以 12306 的证书为例,因为 12306 的证书是自签名的
private void test12306(final SslErrorHandler handler, String url) {
OkHttpClient.Builder builder;
try {
builder = setCertificates(new OkHttpClient.Builder(), context.getAssets().open(MainActivity.cer_protal_root));
} catch (IOException e) {
builder = new OkHttpClient.Builder();
}
Request request = new Request.Builder().url(url)
.build();
builder.build().newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("12306 error", e.getMessage());
handler.cancel();
} @Override
public void onResponse(Call call, Response response) throws IOException {
Log.e("12306 ", response.body().string());
handler.proceed();
}
});
} private OkHttpClient.Builder setCertificates(OkHttpClient.Builder client, InputStream... certificates) {
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");
keyStore.load(null);
int index = 0;
for (InputStream certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate)); try {
if (certificate != null)
certificate.close();
} catch (IOException e) {
}
}
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
X509TrustManager trustManager = Platform.get().trustManager(sslSocketFactory);
client.sslSocketFactory(sslSocketFactory, trustManager);
} catch (Exception e) {
e.printStackTrace();
}
return client;
}
}
以上代码可以针对规范的自签名证书进行校验了。但是呢,我们的证书不规范,会出现 Hostname xxx not verified 的情况。这种情况需要对 Hostname 进行校验。需要在 client 上添加如下代码
client.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
String peerHost = session.getPeerHost();//服务器返回的域名
try {
X509Certificate[] peerCertificates = (X509Certificate[]) session.getPeerCertificates();
for (X509Certificate c : peerCertificates) {
X500Principal subjectX500Principal = c.getSubjectX500Principal();
String name = new X500p(subjectX500Principal).getName();
String[] split = name.split(",");
for (String s : split) {
if (s.startsWith("CN")) {
if (s.contains(hostname) && s.contains(peerHost)) {
return true;
}
}
}
}
} catch (SSLPeerUnverifiedException e) {
e.printStackTrace();
}
return false;
}
});
获取证书两种方法
- 服务器组直接给。如测试 12306 网站的时候,进入网页,12306 会提供根证书的下载
- 通过网页获取。
- Chrome 浏览器,按 F12 选择 Security
- 选择 Certificate Error 的 View certificate
- 在弹出的证书,选择详细信息
- 在详细信息页面,点击复制到文件,一路下一步,然后选择保存证书的地方,就把证书成功导出来了。
证书的读取
从 assert 中读取文件
InputStream is = getAssets().open("root.cer");
生成 jks 、 cer 证书的 keytool 命令
生成 jks
keytool -genkey -alias li_server -keyalg RSA -keystore li_server.jks -validity 3600 -storepass 123456
使用 jks 生成 cer
keytool -export -alias li_server -file li_server.cer -keystore li_server.jks -storepass 123456
基础知识
- java 平台默认识别 jks 格式的证书文件, 但是 Android 平台只识别 bks 格式的证书文件
注意:
- 使用本地服务器测试的时候,使用的 IP 地址,如:192.168.2.22,生成的服务器证书需要添加(-ext san=ip:192.168.2.22),否则会出现 Hostname xxx not verified 的问题:
keytool -genkey -alias li_server -keyalg RSA -keystore li_server.jks -validity 3600 -storepass 123456 -ext san=ip:192.168.2.22
Tomcat 搭建 SSL 环境
百度搜索 tomcat 本地搭建
生成的证书 jks 放到 tomcat 的根目录,如:D:\apache-tomcat
修改 server.xml 文件,在 Service 节点,添加如下代码
<Connector SSLEnabled="true" acceptCount="100" clientAuth="false"
disableUploadTimeout="true" enableLookups="true" keystoreFile="li_server.jks" keystorePass="123456" maxSpareThreads="75"
maxThreads="200" minSpareThreads="5" port="8443"
protocol="org.apache.coyote.http11.Http11NioProtocol" scheme="https"
secure="true" sslProtocol="TLS"/>
重启 tomcat ,然后就可以访问 https 的地址了,端口为 8443,如:https://192.168.123.131:8443/
通过 chrome 可以看到,该网页不安全提醒。
常见问题
- 证书有问题,证书来自不信任的来源
java.security.cert.CertPathValidatorException: Trust anchor for certification path not found - 配置服务器所使用的证书不具有与尝试连接的服务器匹配的主题或主题备用名称字段
Hostname xxx not verified:
参考:
Android 安全之 Https 中间人攻击漏洞:http://yaq.qq.com/blog/13
Android HostName 强验证:http://www.cnblogs.com/fengchuxiaodai/p/5962760.html
Android WebView 手动校验 https 证书: http://blog.csdn.net/lsyz0021/article/details/54669914
Android HTTPS : http://blog.csdn.net/lmj623565791/article/details/48129405
Android HostName XXX not verified : https://developer.android.com/training/articles/security-ssl.html#CommonHostnameProbs
SSLPeerUnverifiedException:HostName not verified: https://stackoverflow.com/questions/30745342/javax-net-ssl-sslpeerunverifiedexception-hostname-not-verified
jks 转 bks :http://blog.csdn.net/bigboysunshine/article/details/54134382
jks 转 bks : http://www.cnblogs.com/darkdog/p/4281555.html
Retrofit 使用 HTTPS: http://blog.csdn.net/dd864140130/article/details/52625666
解析证书乱码问题:http://blog.csdn.net/suntongo/article/details/38864413
Android Webview SSL 自签名安全校验解决方案的更多相关文章
- Android WebView的Js对象注入漏洞解决方案
http://blog.csdn.net/leehong2005/article/details/11808557/ webview调用以下文件,就可以打印sdcard 文件名 <!DOCTYP ...
- Android WebView 详解
相关API 相关类介绍 WebResourceRequest 添加于API21,封装了一个Web资源的请求信息,包含:请求地址,请求方法,请求头,是否主框架,是否用户点击,是否重定向 WebResou ...
- Android WebView常见问题及解决方案汇总
Android WebView常见问题解决方案汇总: 就目前而言,如何应对版本的频繁更新呢,又如何灵活多变地展示我们的界面呢,这又涉及到了web app与native app之间孰优孰劣的争论. 于是 ...
- Android WebView访问SSL证书网页(onReceivedSslError)
Android WebView访问https SSL证书网页,如淘宝,需要在onReceivedSslError添加SSL支持 webview.setWebViewClient(new WebView ...
- Android WebView常见问题解决方案汇总
问题目录: 1.为WebView自定义错误显示界面: 2.WebView cookies清理 3.清理cache 和历史记录 4.判断WebView是否已经滚动到页面底端 5.URL拦截 6.处理We ...
- Android之Android WebView常见问题及解决方案汇总
如有转载,请声明出处: 时之沙: http://blog.csdn.net/t12x3456 Android WebView常见问题解决方案汇总: 就目前而言,如何应对版本的频繁更新呢,又如何灵活多变 ...
- Android中破解应用签名校验的后续问题处理方案(闪退和重启现象以及无效问题)
一.前言 之前已经写了一个爆破签名校验的工具kstools,很多同学也在使用,但是也反馈了不少问题,之前一篇文章也介绍了,关于爆破之后第三方登录问题修复,这篇我们在综合说明一下一些后遗症问题,关于ks ...
- webview之如何设计一个优雅健壮的Android WebView?(下)(转)
转载:https://iluhcm.com/2018/02/27/design-an-elegant-and-powerful-android-webview-part-two/ (这篇文章写得有点晚 ...
- 如何设计一个优雅健壮的Android WebView?(下)
转:如何设计一个优雅健壮的Android WebView?(下) 前言 在上文<如何设计一个优雅健壮的Android WebView?(上)>中,笔者分析了国内WebView的现状,以及在 ...
随机推荐
- 关于0xFFFFFFFF和alpha,温故而知新
做图像处理都好多年了,今天随手做个小画板的时候,发现一个挺有趣的小坑.而其实这个小坑,以前也坑过自己,不过太久没处理了,又踩到坑里了. 先来看看:0xFFFFFFFF>>24 这个结果是什 ...
- jackson 中JsonFormat date类型字段的使用
为了便于date类型字段的序列化和反序列化,需要在数据结构的date类型的字段上用JsonFormat注解进行注解具体格式如下 @JsonFormat(pattern = "yyyy-MM- ...
- Ubuntu18.04下的 Android Studio 3.1.2
Android Studio安装 参考官网上的安装说明 # 安装依赖 :i386 lib32z1 libbz2-1.0:i386 安装openjdk (Update 2018-08-21: 这次重装U ...
- 【TP3.2.X】(同样适用于OT) 设置单入口index.php文件,区分PC/WAP/Wechat 三个终端
1.目的:本教程适用于 OneThink1.0.或者TP3.2.X 系列,达到单入口index.php文件,区分PC/WAP/Wechat 三个终端 2.启发至 : http://www.thinkp ...
- Javascript 思维导图 绘制基础内容(值得一看)
来源于:http://www.cnblogs.com/coco1s/p/3953653.html javascript变量 javascript运算符 javascript数组 javascript流 ...
- firefox插件卸载
1.根据插件名进行搜索,搜索到相关dll后删除,重启firefox. 2.about:config--plugin.expose_full_path:true,然后about:plugins去查看插件 ...
- [SQL] 请教一下 count里面有case when 一般情况下啥时候用
http://www.itpub.net/forum.php?mod=viewthread&tid=1810967 问题: 比如 count(case when pday_id=${deal ...
- 如何写UI及屏幕适配的一些技巧
总结一下关于UI布局及屏幕适配的一些实战技巧,尤其使用纯代码,会对提升效率及代码易于维护等方面有明显帮助,这里提到的没有使用任何Xib, 如果不是在外包公司,也推荐大家多使用甚至完全使用纯代码布局UI ...
- I/O Completion Ports学习
表示还是自己看MSDN最直接,别人的介绍都是嚼剩下,有木有? IO完成端口为在多处理器系统处理多个异步IO请求提供一个高效的线程模型.当一个进程新建一个完成端口,操作系统新建一个目的为服务这些请求的队 ...
- apache kafka系列之Producer处理逻辑
最近研究producer的负载均衡策略,,,,我在librdkafka里边用代码实现了partition 值的轮询方法,,,但是在现场验证时,他的负载均衡不起作用,,,所以来找找原因: 下文是一篇描 ...