Android Okhttp 配置HTTPS

Posted chunqiuwei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Okhttp 配置HTTPS相关的知识,希望对你有一定的参考价值。

阅读本篇博文,博主假设你已经按顺序读过下面三篇文章。
Android okhttp https TrustManager简单总结
Android CertificateSource 简单说明
Android AndroidNSSP的简单说明
Android RootTrustManager 证书校验简单分析
Okhttp configuration HTTPS access + server deployment

网上关于Okhttp配置HTTPS的内容有很多,现在简单整理下。

1、暴力方法,HTTPS形同虚设,不建议这么写。

public static String test() {
    OkHttpClient client = new OkHttpClient.Builder()
        .sslSocketFactory(createSSLSocketFactory(), new TrustAllCerts())
        .hostnameVerifier(new TrustAllHostnameVerifier()).build();
}

//顾名思义,所有的证书都信任
private static class TrustAllCerts implements X509TrustManager {
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
    public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}
}

//对域名也不做校验
private static class TrustAllHostnameVerifier implements HostnameVerifier {
    public boolean verify(String hostname, SSLSession session) { return true; }
}

private static SSLSocketFactory createSSLSocketFactory() {
    SSLSocketFactory ssfFactory = null;
    try {
        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null, new TrustManager[]{new TrustAllCerts()}, new SecureRandom());
        ssfFactory = sc.getSocketFactory();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return ssfFactory;
}

上面这段代码就是个暴力不负责任的典型代码,从类的名字就可以看出来信任左右的证书和域名。TrustAllHostnameVerifierverify方法直接返回了true。表明了对于任何hosts来说,我们都毫无保留的信任它门!再来看看checkClientTrustedcheckServerTrusted,一个是用来校验客户端的,一个是用来校验服务端证书的。对android开发来说我们就看checkClientTrusted,上面的代码中checkClientTrusted什么工作都没有做,这就意味着不论是正规服务端还是中间人,我们都无条件信任。所以这种编码方式不可取。

2、推荐方案

从两个方面入手,一个是来自x509信任管理器X509TrustManager,另一个是来自主机名验证器HostnameVerifier

2.1 修改HostnameVerifier

 首先让我们重写上面的`HostnameVerifier`的对象,该类的主要作用就是来验证主机名,可以改造成如下形式:
HostnameVerifier hnv = new HostnameVerifier() {
    @Override
    public boolean verify(String hostname, SSLSession session) {
        if("www.test.com".equals(hostname)){  
            return true;  
        } 
        else {  
            HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
            return hv.verify(hostname, session);
        }
    }
};

我们可以根据上面代码的写法,根据自己的业务配置一个白名单机制,比如讲受信任的host放在一个集合里,当然要及时更新自己的白名单列表。

2.2 配置X509TrustManager

实际上有两种方式来添加受信任的的证书,其中一种代码如下(建议使用这一种):


private static X509TrustManager trustManagerForCertificates(InputStream in)
        throws GeneralSecurityException
{
    CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
    //通过读取证书文件的输入流创建证书对象
    Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(in);
    if (certificates.isEmpty()) {
        throw new IllegalArgumentException("expected non-empty set of trusted certificates");
    }

    Char [] password = "password". Tochararray(); // any password can be used here
    KeyStore keyStore = newEmptyKeyStore(password);
    int index = 0;
    //遍历证书对象,将certificate添加到KeyStore里面
    for (Certificate certificate : certificates) {
        String certificateAlias = Integer.toString(index++);
        keyStore.setCertificateEntry(certificateAlias, certificate);
    }

    // Use it to build an X509 trust manager.
    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(
            KeyManagerFactory.getDefaultAlgorithm());
    keyManagerFactory.init(keyStore, password);
    
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
            TrustManagerFactory.getDefaultAlgorithm());
    trustManagerFactory.init(keyStore);
    TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
    if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager))
    {
        throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
    }
    return (X509TrustManager) trustManagers[0];
}

按照这种方式写的话,对于Android端来说,我们需要事先将证书内置到APP里面,一般放在assets文件夹下,通过 getassets(). Open(“ xxx.xxx “).获取证书文件的输入流即可。注意证书是有有效期的,对于失效的证书我们要及时发版提醒客户更新app

第二种方法是自定义TrustManager,代码如下,详见Android okhttp https TrustManager简单总结

/获取本地证书
  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();
  }


SSLContextcontext = SSLContext.getInstance("TLS");
context.init(null, new TrustManager[]{
    new X509TrustManager() {
        @Override
        public void checkClientTrusted(X509Certificate[] chain,String authType) throws CertificateException {}
        
        @Override
        public void checkServerTrusted(X509Certificate[] chain,String authType) throws CertificateException {
            for (X509Certificate cert : chain) {
                // 确保证书没有过期
                cert.checkValidity();
                // Verify the certificate's public key chain.
                try {
                    cert.verify(((X509Certificate) ca).getPublicKey());
                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                } catch (InvalidKeyException e) {
                    e.printStackTrace();
                } catch (NoSuchProviderException e) {
                    e.printStackTrace();
                } catch (SignatureException e) {
                    e.printStackTrace();
                }
            }
        }

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

以上是关于Android Okhttp 配置HTTPS的主要内容,如果未能解决你的问题,请参考以下文章

Android Okhttp 配置HTTPS

Android Okhttp 配置HTTPS

OkHttpAndroid 项目导入 OkHttp ( 配置依赖 | 配置 networkSecurityConfig | 配置 ViewBinding | 代码示例 )

Android--okhttp与php交互,文件上传下载

android okhttp https请求

Android OkHttp + Retrofit 下载文件与进度监听