zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

Android okhttp https TrustManager简单总结

AndroidHTTPS 简单 总结 okHttp
2023-09-14 09:12:10 时间

okhttp源码解析中详细的分析了下其内部原理,现在就okhttp配置https的东东做一个简单的笔记。

网上查询的一些Okhttp中忽略HTTPS验证的代码如下所示。

  TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
                @Override
                public void checkClientTrusted(java.security.cert.X509Certificate[] chain,
                                               String authType) throws CertificateException {
                    //该方法在服务端使用,用来校验客户端的安全性
                }

                @Override
                public void checkServerTrusted(java.security.cert.X509Certificate[] chain,
                                               String authType) throws CertificateException {
                            //该方法在客户端使用,用来校验服务端的安全性                       
                }

                @Override
                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            }};

忽略验证这种做法肯定不可取,对于客户端APP来说,在TrustManager中有一个方法需要我们关注,就是checkServerTrusted。顾名思义,就是用来验证服务器是否可靠。其中第一个参数是证书链,往往数组中第一个X509Certificate对象就是我们APP服务器的证书对象。通过该对象,我们可以获取证书的序列号,公钥等各种信息。

默认如果什么都不写的话,就是默认所有服务器都可靠。直接一路绿灯,畅通无阻。

注意该方法抛出一个异常CertificateException,如果自己实现这个方法,并且抛出这个异常的话,那么就说明服务器认证不通过,此时https请求返回的状态码会报600错误。比如X509Certificate对象的checkValidity方法就会抛出CertificateException的两个之类异常:一个是证书过期,一个是证书失效。

 public abstract void checkValidity(Date date)
        throws CertificateExpiredException, CertificateNotYetValidException;

所以我们如果简单的将checkServerTrusted如下写法的话,如果证书校验失败,HTTPS请求就会访问不通。比如我将手机系统时间往后设置30年,此时证书已经过期,运行后就会抛出CertificateExpiredException异常:


public void checkServerTrusted(java.security.cert.X509Certificate[] chain,
                                               String authType) throws CertificateException {
      //服务器验失败会抛异常             
     chain[0].checkValidity();                     
 }

如果我们证书校验失败,我们想要给用户提示的话,就可以先捕获了CertificateException异常,在catch里面处理:


public void checkServerTrusted(java.security.cert.X509Certificate[] chain,
                                               String authType) throws CertificateException {
      try{
          chain[0].checkValidity();       
      }catch(CertificateExpiredException e1){
         //给用户弹出安全性提示
      }catch(CertificateNotYetValidException e2){
       //给用户弹出安全性提示
      }            
                 
 }

其实可以这么做,这部分可以参考Android App 安全的HTTPS 通信
1、打包一份到证书到 app 内部,通过CertificateFactory 加载证书输入流,获取Certificate对象
2、自定义一个TrustManager,根据服务端传过来的证书链如步骤1生成的Certificate做对比,自己实现校验逻辑,代码如下:

try {
//获取本地证书
  CertificateFactory cf = CertificateFactory.getInstance("X.509");
  InputStream caInput = new BufferedInputStream(getAssets().open("certName.crt"));
  final Certificate ca;
  try {
      ca = cf.generateCertificate(caInput);
  } finally {
      caInput.close();
  }
  SSLContext context = SSLContext.getInstance("TLSv1","AndroidOpenSSL");
  context.init(null, new TrustManager[]{
          new X509TrustManager() {
           
              @Override
              public void checkServerTrusted(X509Certificate[] chain,
                      String authType)
                      throws CertificateException {
                  for (X509Certificate cert : chain) {
                      cert.checkValidity();
                      //省略try catch
                      cert.verify(((X509Certificate) ca).getPublicKey());
                     
                  }
              }

              @Override
              public X509Certificate[] getAcceptedIssuers() {
                  return new X509Certificate[0];
              }
          }
  }, null);

关于TrustManager暂且写这么多。其具体的实现逻辑可以移步博主的Android RootTrustManager 证书校验简单分析了解更多。