• 服务器证书校验主要针对 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;
    }
    });

获取证书两种方法

  1. 服务器组直接给。如测试 12306 网站的时候,进入网页,12306 会提供根证书的下载
  2. 通过网页获取。
    1. Chrome 浏览器,按 F12 选择 Security
    2. 选择 Certificate Error 的 View certificate
    3. 在弹出的证书,选择详细信息
    4. 在详细信息页面,点击复制到文件,一路下一步,然后选择保存证书的地方,就把证书成功导出来了。

证书的读取

  • 从 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 环境

  1. 百度搜索 tomcat 本地搭建

  2. 生成的证书 jks 放到 tomcat 的根目录,如:D:\apache-tomcat

  3. 修改 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"/>
  4. 重启 tomcat ,然后就可以访问 https 的地址了,端口为 8443,如:https://192.168.123.131:8443/

  5. 通过 chrome 可以看到,该网页不安全提醒。

常见问题

  1. 证书有问题,证书来自不信任的来源

    java.security.cert.CertPathValidatorException: Trust anchor for certification path not found
  2. 配置服务器所使用的证书不具有与尝试连接的服务器匹配的主题或主题备用名称字段

    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 自签名安全校验解决方案的更多相关文章

  1. Android WebView的Js对象注入漏洞解决方案

    http://blog.csdn.net/leehong2005/article/details/11808557/ webview调用以下文件,就可以打印sdcard 文件名 <!DOCTYP ...

  2. Android WebView 详解

    相关API 相关类介绍 WebResourceRequest 添加于API21,封装了一个Web资源的请求信息,包含:请求地址,请求方法,请求头,是否主框架,是否用户点击,是否重定向 WebResou ...

  3. Android WebView常见问题及解决方案汇总

    Android WebView常见问题解决方案汇总: 就目前而言,如何应对版本的频繁更新呢,又如何灵活多变地展示我们的界面呢,这又涉及到了web app与native app之间孰优孰劣的争论. 于是 ...

  4. Android WebView访问SSL证书网页(onReceivedSslError)

    Android WebView访问https SSL证书网页,如淘宝,需要在onReceivedSslError添加SSL支持 webview.setWebViewClient(new WebView ...

  5. Android WebView常见问题解决方案汇总

    问题目录: 1.为WebView自定义错误显示界面: 2.WebView cookies清理 3.清理cache 和历史记录 4.判断WebView是否已经滚动到页面底端 5.URL拦截 6.处理We ...

  6. Android之Android WebView常见问题及解决方案汇总

    如有转载,请声明出处: 时之沙: http://blog.csdn.net/t12x3456 Android WebView常见问题解决方案汇总: 就目前而言,如何应对版本的频繁更新呢,又如何灵活多变 ...

  7. Android中破解应用签名校验的后续问题处理方案(闪退和重启现象以及无效问题)

    一.前言 之前已经写了一个爆破签名校验的工具kstools,很多同学也在使用,但是也反馈了不少问题,之前一篇文章也介绍了,关于爆破之后第三方登录问题修复,这篇我们在综合说明一下一些后遗症问题,关于ks ...

  8. webview之如何设计一个优雅健壮的Android WebView?(下)(转)

    转载:https://iluhcm.com/2018/02/27/design-an-elegant-and-powerful-android-webview-part-two/ (这篇文章写得有点晚 ...

  9. 如何设计一个优雅健壮的Android WebView?(下)

    转:如何设计一个优雅健壮的Android WebView?(下) 前言 在上文<如何设计一个优雅健壮的Android WebView?(上)>中,笔者分析了国内WebView的现状,以及在 ...

随机推荐

  1. 《跟孩子学Python》

    1:Python对象之间的赋值是内容赋值而不是引用赋值 a = ["aaa","bbb","ccc"] b = a print a prin ...

  2. JVM中java实例对象在内存中的布局

    普通的Java对象实例 和  Java数组实例.Java数组实例的对象头多了一个数组的长度.Java虚拟机可以通过普通java对象的元数据来确定java对象的大小,但是从数组的元数据中却无法确定数组的 ...

  3. mysql统计函数

    数据记录统计函数: AVG(字段名) 得出一个表格栏平均值 COUNT(*|字段名) 对数据行数的统计或对某一栏有值的数据行数统计 MAX(字段名) 取得一个表格栏最大的值 MIN(字段名) 取得一个 ...

  4. Dom4j完整教程,操作XML教程

    目录 1.DOM4J简介 2.XML文档操作1 2.1.读取XML文档: 2.2.获取根节点 2.3.. 新增一个节点以及其下的子节点与数据 2.4. 写入XML文件 2. 5. 遍历xml节点 2. ...

  5. Qt5中表格处理大数据量

    在Qt中如果是普通项目,GUI处理展现的数据量不大,一般用QTableWidget,QTreeWidget这样的控件就满足了,但是如果数据量行数达到了几万行,那么Widget的展示性能就偏差了. Qt ...

  6. maven Missing artifact com.sun:tools:jar:1.5.0

    转自:http://blog.csdn.net/caolaosanahnu/article/details/7918929 http://zuoshahao.com/work/others/missi ...

  7. Linux下网卡混杂模式设置和取消

    工作中发现一个网卡工作状态不对了,查看了一下,发现这个网卡和正常工作的网卡  信息不一样,它显示的居然是混杂模式,而正常工作的是  running 模式 ,所以来了解下混杂模式怎么取消... 下文来自 ...

  8. Linux 日志切割工具cronolog详解

    一.前言 二.cronolog 简介 三.cronolog 特点 四.cronolog 安装 五.cronolog 使用 六.cronolog 总结 注,操作系统 CentOS 6.4 x86_64, ...

  9. The method getServletContext() is undefined for the type HttpServletRequest

    request.getServletContext().getRealPath("/") 已经加入了 sun runtime library但是还是提示错误 是因为 写法过时了改成 ...

  10. k8s实战之Service

    一.概述 为了适应快速的业务需求,微服务架构已经逐渐成为主流,微服务架构的应用需要有非常好的服务编排支持,k8s中的核心要素Service便提供了一套简化的服务代理和发现机制,天然适应微服务架构,任何 ...