1 概述

OkHttp配置HTTPS访问,核心为以下三个部分:

  • sslSocketFactory()
  • HostnameVerifier
  • X509TrustManager

第一个是ssl套接字工厂,第二个用来验证主机名,第三个是证书信任器管理类.通过OkHttp实现HTTPS访问需要自己实现以上三部分.另外还简单提及了服务器端的部署,用的是Tomcat9,最后是一些常见问题的可能解决方案.

2 OkHttp介绍

OkHttp是一款开源的处理网络请求的轻量级框架,有Square公司贡献,用于替代HttpUrlConnection与Apache HttpClient,目前Github上有36.4k的star.优点有

  • 共享socket,HTTP/2支持所有连接到同一个主机的请求共享socket
  • 连接池可以减少请求延迟
  • 缓存响应数据减少重复的网络请求
  • 自动处理gzip压缩

总的来说OkHttp是一款支持get/post请求,支持文件上传/下载的优秀的HTTP框架.

3 准备工作

  • 一台服务器
  • 一个域名
  • 一个证书

什么?都没有?买!

当然证书可以不用买,可以使用openssl之类的工具生成,不过自签名的证书后面验证的时候会有点麻烦,建议还是购买.

4 OkHttp部分

4.1 暴力方案

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;
}

这是一种暴力的方案,看类名就知道了,信任所有的证书与主机:

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,这样还不如不加密,不推荐使用.

4.2 推荐方案

从两方面入手修改,一是从X509TrustManager入手,二是从HostnameVerifier入手.

4.2.1 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);
}
}
};

这里验证主机名是www.test.com就返回true,实现得比较简单,业务复杂的话可以结合配置中心,黑/白名单等动态校验.

4.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(); // 这里可以使用任意密码
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证书数组.

这种方法笔者没有试过,仅供参考.

5 服务器部署

服务器用的是Tomcat,简单介绍一下部署.

5.1 上传工程

后端处理用的Spring Boot的工程,就不演示了,使用IDEA打成war包后上传到webapps下即可.

5.2 Tomcat配置

重点说一下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

进行测试



这样就成功了.

6 验证与源码

这个因为没有完整的Demo很难做验证,具体来说前端用的OkHttp核心都介绍了,后端的话服务器Tomcat也介绍了,用Spring Boot做个Demo应该不难.

这里只给出了工具类OKHTTP的源码:

github

7 常见问题

7.1 Tomcat HTTPS无法访问

  • 证书文件错误,不过这个可能性比较少.
  • 配置错误,请检查配置文件是否正确,可以ps -ef | grep tomcat查看Tomcat是否开启以及查看logs/catalina.out日志.
  • 端口错误,访问的端口需要与<Connector>中的端口对应(Tomcat默认的HTTPS端口为8443,笔者居然看成了8433,然后netstat 无数次都没有看到被监听...)
  • 安全组/防火墙问题,云服务器的话需要在安全组配置中开启相应端口,同时应查看有没有把某个ip列入黑名单导致无法访问.防火墙的话这里主要指iptables,如果没有开启的话不需要理会,如果开启的话需要开放对应端口.

7.2 OkHttp HTTPS无法访问

  • 无法读取证书文件:需要把证书文件放在工程对应路径下读取,比如AS中放在assets下然后使用getAssets().open("xxx.xxx")获取,Maven工程的话放在resources下直接使用FileInputStream获取.
  • singed fields invalid:



    证书文件格式错误,使用.crt/.pem等证书.
  • Signature does not match:这个有可能是使用openssl自生成证书在验证的时候出现的异常,可能的解决办法是转换证书的格式,如果不行就重新生成一次证书.

8 参考链接

1.苹果核 - Android App 安全的HTTPS 通信

2.Android OkHttp实现HTTPS访问,支持Android 4.X系统HTTPS访问

3.Android使用OkHttp请求自签名的https网站

OkHttp配置HTTPS访问+服务器部署的更多相关文章

  1. 配置HTTPS网站服务器

                                     配置HTTPS网站服务器    案例1:配置安全Web服务 1.1问题 本例要求为站点http://server0.example.c ...

  2. Linux Apache配置https访问

    配置https访问 该环境是rh254课程配套的一个环境,不过配置方法步骤相同. 要求: 使用虚拟主机技术部署两个网站: 网站1: 绑定域名 www0.example.com 目录在 /srv/www ...

  3. 使用docker搭建最新版本的gitea,并配置HTTPS访问

    使用docker搭建最新版本的gitea,并配置HTTPS访问 博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢! 简介 之前有搭建 ...

  4. lamp之apache配置https访问

    配置apache 使用https 注:怕其他人由于路径的原因出问题,首先声明一下,本人apache的安装目录为 : /usr/local/httpd2.4.25,如果不是,请参考进行配置 注: 对于如 ...

  5. 本地测试Tomcat配置Https访问

    一.tomcat开启HTTPS配置 1) 准备证书 使用jdk工具keytool生成一个ssl测试用证书, 一路按照提示操作输入即可 keytool -genkey -alias tomcat -ke ...

  6. 阿里云 rails nginx 配置https访问

    1.申请免费型dv ssl证书:https://common-buy.aliyun.com/?spm=a2c4e.11155515.0.0.7zzvOZ&commodityCode=cas#/ ...

  7. nginx配置https访问

    一.准备 环境:centos6.8 nginx:1.13.6 二.开始       首先安装依赖包: yum install -y gcc gcc-c++ autoconf automake make ...

  8. 基于openresty配置https访问

    安装方法:http://openresty.org/cn/linux-packages.html 1. openssl的版本信息 [root@localhost conf]# openssl vers ...

  9. Apache 配置 HTTPS访问

    将需要配置的项目移动到另一根目录下,作为https访问位置. 修改bitnami配置文件..\Bitnami\wampstack-5.6.19-0\apache2\conf\bitnami\bitna ...

随机推荐

  1. Java 线程间通信 —— 等待 / 通知机制

    本文部分摘自<Java 并发编程的艺术> volatile 和 synchronize 关键字 每个处于运行状态的线程,如果仅仅是孤立地运行,那么它产生的作用很小,如果多个线程能够相互配合 ...

  2. js 一元运算符

    一元运算符还有一个常用的用法就是将自执行函数的function从函数声明变成表达式. 常用的有 + - - ! void + function () { } - function () { } ~ f ...

  3. 分布式事务 SEATA-1.4.1 AT模式 配合NACOS 应用

    SEATA 配置 目录 SEATA 配置 TC (Transaction Coordinator) - 事务协调者 配置参数 nacos bash 脚本 同步 config 配置到 nacos 使用 ...

  4. 将VMware工作站最小化到托盘栏

    目录 前言 将VMware最小化到托盘栏的方法 1.下载 Trayconizer 2.解压 trayconizerw.zip 3.创建 VMware 快捷方式 4.修改 VMware 快捷方式 5.运 ...

  5. Java数组之二分查找

    简单的二分查找 package com.kangkang.array; public class demo03 { public static void main(String[] args) { / ...

  6. msfconsole 常用命令记录

    Metasploit是一款开源的渗透测试框架,它现在还在逐步发展中,下面介绍的一些功能和命令,可能会在未来失效. Metasploit框架提供了多种不同方式的使用接口: msfgui msfconso ...

  7. [MIT 18.06 线性代数]Intordution to Vectors向量初体验

    目录 1.1. Vectors and Linear Combinations向量和线性组合 REVIEW OF THE KEY IDEAS 1.2 Lengths and Dot Products向 ...

  8. 2020年12月-第02阶段-前端基础-CSS Day07

    CSS Day07 CSS高级技巧 理解 能说出元素显示隐藏最常见的写法 能说出精灵图产生的目的 能说出去除图片底侧空白缝隙的方法 应用 能写出最常见的鼠标样式 能使用精灵图技术 能用滑动门做导航栏案 ...

  9. 设计模式(二十四)——职责链模式(SpringMVC源码分析)

    1 学校 OA 系统的采购审批项目:需求是 采购员采购教学器材 1) 如果金额 小于等于 5000,  由教学主任审批 (0<=x<=5000) 2) 如果金额 小于等于 10000,   ...

  10. 几大BSD 区别

    OpenBSD 侧重于安全,软件包较少,较陈旧,比如 KDE 才 3.5,为了安全舍弃了 sudo 和 linux 兼容层: FreeBSD 是开发者最多用户最多软件包最多的,有 ZFS 和 Linu ...