Thanks
HTTPS理论基础及其在Android中的最佳实践
聊聊HTTPS和SSL/TLS协议
理解HTTPS
HTTPS概述
HTTPS是建立在HTTP的基础上,Http是一个网络协议,用于传输内容,我们知道HTTP的通信过程大致如下:
![](https://img.haomeiwen.com/i1876233/7c8503e9fe0668af.png)
Http基于4层网络模型:
┌────------────┐┌─┬─┬─-┬─┬─-┬─┬─-┬─┬─-┬─┬─-┐
│ ││D│F│W│F│H│G│T│I│S│U│ │
│ ││N│I│H│T│T│O│E│R│M│S│其│
│第四层,应用层 ││S│N│O│P│T│P│L│C│T│E│ │
│ ││ │G│I│ │P│H│N│ │P│N│ │
│ ││ │E│S│ │ │E│E│ │ │E│它│
│ ││ │R│ │ │ │R│T│ │ │T│ │
└───────------─┘└─┴─┴─-┴─┴─-┴─┴─-┴─┴─-┴─┴-─┘
┌───────-----─┐┌─────────-------┬──--------─────────┐
│第三层,传输层 ││ TCP │ UDP │
└───────-----─┘└────────-------─┴──────────--------─┘
┌───────-----─┐┌───----──┬───---─┬────────-------──┐
│ ││ │ICMP│ │
│第二层,网络层 ││ └──---──┘ │
│ ││ IP │
└────────-----┘└────────────────────-------------─-┘
┌────────-----┐┌─────────-------┬──────--------─────┐
│第一层,网络接口││ARP/RARP │ 其它 │
└────────------┘└─────────------┴─────--------──────┘
TCP/IP四层参考模型
从层级上我们可以发现,应用层产生的数据,直接经由传输层直接传输,存在一个很大的问题,安全性。原本的Http就是传输明文的,数据一经拦截就很容易被别人盗取信息。那怎么解决呢?HTTPS的方法是,对传输的数据进行加密,在应用层和传输层的中间加一层,S层: SSL/TLS,Secure Sockets Layer 安全套接层 / Transport Layer Security 传输层安全协议,这一层使用的主要是 SSL/TLS 技术,这两个协议其实就是安全加密算法的不同:
┌────------────┐┌─┬─┬─-┬─┬─-┬─┬─-┬─┬─-┬─┬─-┐
│ ││D│F│W│F│H│G│T│I│S│U│ │
│ ││N│I│H│T│T│O│E│R│M│S│其│
│第四层,应用层 ││S│N│O│P│T│P│L│C│T│E│ │
│ ││ │G│I│ │P│H│N│ │P│N│ │
│ ││ │E│S│ │ │E│E│ │ │E│它│
│ ││ │R│ │ │ │R│T│ │ │T│ │
└───────------─┘└─┴─┴─-┴─┴─-┴─┴─-┴─┴─-┴─┴-─┘
┌───────-----─┐
│SSL/TLS │
└───────-----─┘
┌───────-----─┐┌─────────-------┬──--------─────────┐
│第三层,传输层 ││ TCP │ UDP │
└───────-----─┘└────────-------─┴──────────--------─┘
┌───────-----─┐┌───----──┬───---─┬────────-------──┐
│ ││ │ICMP│ │
│第二层,网络层 ││ └──---──┘ │
│ ││ IP │
└────────-----┘└────────────────────-------------─-┘
┌────────-----┐┌─────────-------┬──────--------─────┐
│第一层,网络接口││ARP/RARP │ 其它 │
└────────------┘└─────────------┴─────--------──────┘
TCP/IP四层参考模型
对称加密算法
既然加密,那怎么一个规则呢?加密,在我们的理解是这样的:
![](https://img.haomeiwen.com/i1876233/37b87b880e9bcd07.png)
用一个密码/密钥,用某种算法对明文进行加密,得到密文,解密也容易:
![](https://img.haomeiwen.com/i1876233/f6b5ca0e05b17c0c.png)
像上面这样,用同一个密码/密钥进行加密和解密的,就是对称加密算法,因为其密码/密钥是一样的,所以名曰为对称。但如果HTTPS中使用这种算法的话,就需要通信双方知道密钥,这样就必须在通信的时候,先把密钥发给对方,但这样,黑客若截获这密码,等于没用。
非对称加密算法
“非对称加密技术”,意思就是说:“加密”和“解密”使用不同的密钥。其基本原理,就是大数的因式分解。这里就不展开了,密码学的东西,很神奇。因为有两个不同的密钥,我们命名为公钥和私钥,公钥是可以对外公开的,私钥是自己保管的,用公钥或私钥中的任何一个进行加密,用另一个进行解密。 加密和解密就变成这样:
明文 + 加密算法 + 公钥 => 密文,
密文 + 解密算法 + 私钥 => 明文
![](https://img.haomeiwen.com/i1876233/1615c220e09e7140.png)
或者是这样:
明文 + 加密算法 + 私钥 => 密文,
密文 + 解密算法 + 公钥 => 明文
![](https://img.haomeiwen.com/i1876233/b397961bd09e4f0c.png)
总结来说,两个密钥皆可加密,加密后只有由另一个密钥解密。
HTTPS中加密算法
非对称加密很棒,但是呢,效率不高,速度慢,对称加密虽然安全性不高,但是效率高啊。怎么选择呢?小孩子才做选择,大人全都要。HTTPS结合了两种加密算法的优势,加密解密变成这样:
![](https://img.haomeiwen.com/i1876233/6a0a6800795f5ee5.png)
这个过程涉及到四个密钥:私钥,公钥,对称加密的Key(这里称为KeyX),公钥加密KeyX后的KeyY。首先客户端会得到服务器发来的公钥,然后,客户端会随机生成一个对称加密用的Key,这里称为KeyX,用KeyX对我们要传输的明文进行对称加密,因为明文可能很多,所以这里用对称加密,就效率高很多,得到对称加密的密文,然后用公钥对KeyX进行非对此加密得到KeyY,然后一并传输密文和KeyY给服务器。想要解释密文,就得先得到KeyX,想要得到KeyX,就得有密钥,完美。所以只能拥有密钥的服务器能得到明文。
具体的HTTPS请求实际可细分为一下步骤:(摘自大神博客)
- 客户端向服务器发起HTTPS请求,连接到服务器的443端口。
- 服务器端有一个密钥对,即公钥和私钥,是用来进行非对称加密使用的,服务器端保存着私钥,不能将其泄露,公钥可以发送给任何人。
- 服务器将自己的公钥发送给客户端。
- 客户端收到服务器端的公钥之后,会对公钥进行检查,验证其合法性。如果发现发现公钥有问题,那么HTTPS传输就无法继续。严格的说,这里应该是验证服务器发送的数字证书的合法性。如果公钥合格,那么客户端会生成一个随机值,这个随机值就是用于进行对称加密的密钥,我们将该密钥称之为client key,即客户端密钥,这样在概念上和服务器端的密钥容易进行区分。然后用服务器的公钥对客户端密钥进行非对称加密,这样客户端密钥就变成密文了,至此,HTTPS中的第一次HTTP请求结束。
- 客户端会发起HTTPS中的第二个HTTP请求,将加密之后的客户端密钥发送给服务器。
- 服务器接收到客户端发来的密文之后,会用自己的私钥对其进行非对称解密,解密之后的明文就是客户端密钥,然后用客户端密钥对数据进行对称加密,这样数据就变成了密文。
- 然后服务器将加密后的密文发送给客户端。
- 客户端收到服务器发送来的密文,用客户端密钥对其进行对称解密,得到服务器发送的数据。这样HTTPS中的第二个HTTP请求结束,整个HTTPS传输完成。
数字证书
我们拿到的公钥怎么确保真的是服务器的公钥呢?黑客有可能中途篡改公钥,将其改成黑客自己的。摘个例子:
假设一个镇里面有两个人A和B,A是个富豪,B想向A借钱,但是A和B不熟,怕B借了钱之后不还。这时候B找到了镇长,镇长给B作担保,告诉A说:“B人品不错,不会欠钱不还的,你就放心借给他吧。” A听了这话后,心里想:“镇长是全镇最德高望重的了,他说B没问题的话那就没事了,我就放心了”。 于是A相信B的为人,把钱借给了B。
类似地,公钥需要一个担保人:证书认证中心(Certificate Authority),简称CA。CA本身有一对公钥和私钥,CA会用CA自己的私钥对要进行认证的公钥进行非对称加密,此处待认证的公钥就相当于是明文,加密完之后,得到的密文再加上证书的过期时间、颁发给、颁发者等信息,就组成了数字证书。
Android-Retrofit 配置证书 访问HTTPS
-
首先,如果证书是由CA颁发的或者是CA授权的机构颁发的,直接可以使用,因为Android有CA的根证书,所以默认支持
-
如果是阿里云申请的证书,可能会有一个 pem 证书,需要先转换格式 cer 参考:https://blog.csdn.net/qq_33315813/article/details/73532846,
关于格式的说明,可以参考 https://blog.csdn.net/qq_30698633/article/details/77895151 -
retrofit的写法参考于:https://blog.csdn.net/qq_20521573/article/details/79233793
https://blog.csdn.net/lmj623565791/article/details/48129405
先把转换后的cer文件放到 R.raw.
下
private SSLContext initCertificates(InputStream... certificates) {
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
for (InputStream certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
try {
if (certificate != null)
certificate.close();
} catch (IOException ignored) { }
}
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
return sslContext;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public void init(Context context) {
InputStream inputStream = context.getResources().openRawResource(R.raw.https);
sslContext = initCertificates(inputStream);
}
调用:
private HttpsContract getBaseHttpProtocol(SSLContext sslContext, String baseServer) {
OkHttpClient client=new OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory())
.hostnameVerifier(new SafeHostnameVerifier())
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseServer)
.addConverterFactory(ScalarsConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(client)
.build();
return retrofit.create(HttpsContract.class);
}
网友评论