OkHttp配置HTTPS访问+服务器部署

编程

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访问+服务器部署 的全部内容, 来源链接: utcz.com/z/514887.html

回到顶部