• 服务器证书校验主要针对 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. golang之log rotate

    操作系统: CentOS 6.9_x64 go语言版本: 1.8.3 问题描述 golang的log模块提供的有写日志功能,示例代码如下: /* golang log example E-Mail : ...

  2. 配置Jenkins的slave节点的详细步骤适合windows等其他平台

    @  新建一个slave节点在Jenkins服务器上 1,进入Jenkins的主界面,进入"Manage Jenkins" 页面: 2,点击如下图中的"Manage  N ...

  3. 【物联网智能网关-17】.NET Micro Framework之MDK C++二次开发

    .NET Micro Framework虽然好学易用,但是在一些需要实时,需要高性能的应用领域,却有些勉为其难.毕竟.NET Micro Framework上层应用程序由底层CLR(TinyCLR)解 ...

  4. ppt整体配色方案

        背景色建议以灰色或者被色为主.       在百度云盘也有大量的ppt模板,还是非常不错的.http://pan.baidu.com/s/1bpDf7Fh    

  5. DLib压缩解压程序示例

    /* 这是一个示例程序,使用了Dlib库的compress_stream和cmd_line_parser组件. 这个示例实现了一个简单实用的命令行压缩程序. 当使用-h选项时候,程序输出如下: 使用: ...

  6. 转error while loading shared libraries的解決方法

    error while loading shared libraries的解決方法 者 icq 21:03 | 靜態連結網址 | 迴響 (0) | 引用 (1) | 點閱次數 (270) | Prog ...

  7. 程序员必备!Sonar代码质量管理工具

    Sonar 是一个用于代码质量管理的开放平台.通过插件机制,Sonar 可以集成不同的测试工具,代码分析工具,以及持续集成工具. Sonar 概述 Sonar 是一个用于代码质量管理的开放平台.通过插 ...

  8. 关于 Dropout 防止过拟合的问题

    关于 Dropout 可以防止过拟合,出处:深度学习领域大神 Hinton,在2012年文献:<Improving neural networks by preventing co-adapta ...

  9. java struts2入门学习---常用标签学习总结

    jsp页面中引入标签: <%@ taglib uri="/struts-tags" prefix="s"%> 常用标签知识点总结: <s:fi ...

  10. B. Eight Point Sets

    B. Eight Point Sets http://codeforces.com/contest/334/problem/B   time limit per test 1 second memor ...