Android Https 相关完全解析 当OkHttp遇到Https

709 查看

一、概述

其实这篇文章理论上不限于okhttp去访问自签名的网站,不过接上篇博文了,就叫这个了。首先要了解的事,okhttp默认情况下是支持https协议的网站的,比如https://www.baidu.comhttps://github.com/hongyangAndroid/okhttp-utils等,你可以直接通过okhttp请求试试。不过要注意的是,支持的https的网站基本都是CA机构颁发的证书,默认情况下是可以信任的。

当然我们今天要说的是自签名的网站,什么叫自签名呢?就是自己通过keytool去生成一个证书,然后使用,并不是CA机构去颁发的。使用自签名证书的网站,大家在使用浏览器访问的时候,一般都是报风险警告,好在有个大名鼎鼎的网站就是这么干的,https://kyfw.12306.cn/otn/,点击进入12306的购票页面就能看到了。

如下界面:

9-22-1

大家可以尝试拿okhttp访问下:

会报出如下错误

好了,本篇博文当然不是去说如何去访问12306,而是以12306为例子来说明如何去访问自签名证书的网站。因为部分开发者app与自己服务端交互的时候可能也会遇到自签名证书的。甚至在开发安全级别很高的app时,需要用到双向证书的验证。

那么本篇博文的基本内容包含:

  • https一些相关的知识
  • okhttp访问自签名https网站
  • 如何构建一个支持https的服务器(这里主要为了测试多个证书的时候,如何去加载)
  • 如何进行双向证书验证

二、Https相关知识

关于特别理论的东西大家可以百度下自己去了解下,这里就简单说一下,HTTPS相当于HTTP的安全版本了,为什么安全呢?

因为它在HTTP的之下加入了SSL (Secure Socket Layer),安全的基础就靠这个SSL了。SSL位于TCP/IP和HTTP协议之间,那么它到底能干嘛呢?

它能够:

  • 认证用户和服务器,确保数据发送到正确的客户机和服务器;(验证证书)
  • 加密数据以防止数据中途被窃取;(加密)
  • 维护数据的完整性,确保数据在传输过程中不被改变。(摘要算法)

以上3条来自百度

下面我们简单描述下HTTPS的工作原理,大家就能对应的看到上面3条作用的身影了:

HTTPS在传输数据之前需要客户端(浏览器)与服务端(网站)之间进行一次握手,在握手过程中将确立双方加密传输数据的密码信息。握手过程的简单描述如下:

  1. 浏览器将自己支持的一套加密算法、HASH算法发送给网站。
  2. 网站从中选出一组加密算法与HASH算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。
  3. 浏览器获得网站证书之后,开始验证证书的合法性,如果证书信任,则生成一串随机数字作为通讯过程中对称加密的秘钥。然后取出证书中的公钥,将这串数字以及HASH的结果进行加密,然后发给网站。
  4. 网站接收浏览器发来的数据之后,通过私钥进行解密,然后HASH校验,如果一致,则使用浏览器发来的数字串使加密一段握手消息发给浏览器。
  5. 浏览器解密,并HASH校验,没有问题,则握手结束。接下来的传输过程将由之前浏览器生成的随机密码并利用对称加密算法进行加密。

握手过程中如果有任何错误,都会使加密连接断开,从而阻止了隐私信息的传输。

ok,以上的流程不一定完全正确,基本就是这样,当然如果有明显错误欢迎指出。

根据上面的流程,我们可以看到服务器端会有一个证书,在交互过程中客户端需要去验证证书的合法性,对于权威机构颁发的证书当然我们会直接认为合法。对于自己造的证书,那么我们就需要去校验合法性了,也就是说我们只需要让OkhttpClient去信任这个证书就可以畅通的进行通信了。

当然,对于自签名的网站的访问,网上的部分的做法是直接设置信任所有的证书,对于这种做法肯定是有风险的,所以这里我们不去介绍了,有需要自己去查。

下面我们去考虑,如何让OkHttpClient去信任我们的证书,接下里的例子就是靠12306这个福利站点了。

首先导出12306的证书,这里12306提供了下载地址:12306证书点击下载

下载完成,解压拿到里面的srca.cer,一会需要使用。ps:即使没有提供下载,也可以通过浏览器导出的,自行百度。

三、代码

(一)、访问自签名的网站

首先把我们下载的srca.cer放到assets文件夹下,其实你可以随便放哪,反正能读取到就行。

然后在我们的OkHttpClientManager里面添加如下的方法:

为了代码可读性,我把异常捕获的部分简化了,可以看到我们提供了一个方法传入InputStream流,InputStream就对应于我们证书的输入流。

代码内部,我们:

  • 构造CertificateFactory对象,通过它的generateCertificate(is)方法得到Certificate。
  • 然后讲得到的Certificate放入到keyStore中。
  • 接下来利用keyStore去初始化我们的TrustManagerFactory
  • trustManagerFactory.getTrustManagers获得TrustManager[]初始化我们的SSLContext
  • 最后,设置我们mOkHttpClient.setSslSocketFactory即可。

这样就完成了我们代码的编写,其实挺短的,当客户端进行SSL连接时,就可以根据我们设置的证书去决定是否新人服务端的证书。

记得在Application中进行初始化: