OkHttp配置HTTPS访问,核心为如下三个部分:html
第一个是ssl套接字工厂,第二个用来验证主机名,第三个是证书信任器管理类.经过OkHttp实现HTTPS访问须要本身实现以上三部分.另外还简单说起了服务器端的部署,用的是Tomcat9,最后是一些常见问题的可能解决方案.前端
OkHttp是一款开源的处理网络请求的轻量级框架,有Square公司贡献,用于替代HttpUrlConnection与Apache HttpClient,目前Github上有36.4k的star.优势有java
总的来讲OkHttp是一款支持get/post请求,支持文件上传/下载的优秀的HTTP框架.git
什么?都没有?买!
固然证书能够不用买,可使用openssl之类的工具生成,不过自签名的证书后面验证的时候会有点麻烦,建议仍是购买.github
public static String test() { OkHttpClient client = new OkHttpClient.Builder() .sslSocketFactory(createSSLSocketFactory(), new TrustAllCerts()) .hostnameVerifier(new TrustAllHostnameVerifier()).build(); String url = "https://xxxxxxx"; //修改为本身的url Request request = new Request.Builder().url(url).build(); Call call = build.newCall(request); Response response = call.execute(); if(response.body() != null) { String result = response.body().string(); //处理result } } 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; }
这是一种暴力的方案,看类名就知道了,信任全部的证书与主机:web
public boolean verify(String hostname, SSLSession session) { return true; }
这个方法直接返回true,也就是信任全部的主机.后端
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {} public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
这里两个check函数没有作任何的工做,表示接受任意的客户端与服务端的证书.这样写的话至关因而使用了一个没用的TrustManager,这样还不如不加密,不推荐使用.数组
从两方面入手修改,一是从X509TrustManager入手,二是从HostnameVerifier入手.缓存
先说个简单的,这里主要是验证主机名,简单的话,能够以下实现:tomcat
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); } } };
这里验证主机名是www.test.com就返回true,实现得比较简单,业务复杂的话能够结合配置中心,黑/白名单等动态校验.
这里其实有两种方式,一种是以流的方式添加信任证书:
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(); // 这里可使用任意密码 KeyStore keyStore = newEmptyKeyStore(password); int index = 0; 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]; }
完整代码见文末.这里把工具类的方法实现成了静态,调用时能够直接:
OKHTTP.send("https://xxxxx");
另外一种方式是直接自定义一个TrustManager,重写里面的三个方法:
SSLContext context = 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) { // Make sure that it hasn't expired. 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);
第一个方法为
@Override public void checkClientTrusted(X509Certificate[] chain,String authType) throws CertificateException {}
该方法检查客户端的证书,因为不须要对客户端进行认证,默认便可.
第二个方法为
@Override public void checkServerTrusted(X509Certificate[] chain,String authType)
该方法检查服务器的证书,若不信任该证书则抛出异常,经过本身实现该方法能够信任任何本身指定的证书,不作任何处理的话,不会抛出任何异常,至关于信任全部证书.这里检查了证书是否过时以及证书的签名是否匹配.
第三个方法为
@Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
返回受信任的X509证书数组.
这种方法笔者没有试过,仅供参考.
服务器用的是Tomcat,简单介绍一下部署.
后端处理用的Spring Boot的工程,就不演示了,使用IDEA打成war包后上传到webapps下便可.
重点说一下Tomcat的配置,首先须要一个域名,修改conf/server.xml文件,找到默认的名叫localhost的Host:
而后直接复制Host标签,把name修改为本身的域名便可.
而后是证书的配置,笔者的证书在某某云上购买的,这里提供了几种格式的证书下载:
Tomcat的是两个文件,一个是pfx文件,一个是密码文件,把pfx文件上传到服务器的Tomcat后,继续修改server.xml,大约87行左右的位置(Tomcat版本9.0.33):
修改以下:
添加了scheme,secure,keystoreFile,keystoreType,keystorePass,clientAuth,sslProtocol配置,同时去掉里面的<SSLHostConfig>,keystoreFile是刚才的pfx文件,采用绝对路径,keystorePass是密码.
另外默认的端口为8443,这里修改为了8123.
重启Tomcat后输入
https://www.test.com:port
进行测试
这样就成功了.
这个由于没有完整的Demo很难作验证,具体来讲前端用的OkHttp核心都介绍了,后端的话服务器Tomcat也介绍了,用Spring Boot作个Demo应该不难.
这里只给出了工具类OKHTTP的源码:
github
1.苹果核 - Android App 安全的HTTPS 通讯
2.Android OkHttp实现HTTPS访问,支持Android 4.X系统HTTPS访问
3.Android使用OkHttp请求自签名的https网站