What is HTTPS?
HTTPS = HTTP + SSL/TLS 。
SSL/TLS基于TCP,工作在OSI七层模型中的表示层,SSL不是简单地单个协议,而是两层协议:SSL记录协议和高层协议(SSL握手协议,SSL修改密码参数协议,SSL报警协议)。
HTTP与SSL/TLS协议组合使用的话,就可以加密HTTP的通信内容,解决HTTP通信不安全的问题。
怎样才是一次安全的通信?
- 传输的报文信息应是被加密的。
- 通信两端的身份应是被验证过的,而不是攻击者伪装的。
- 报文的完整性应得到验证,以防止中间人篡改报文信息。
所以,HTTPS需要做到:
- 服务器认证(客户端知道它们是在与真正的而不是伪造的服务器通话);
- 客户端认证(服务器知道它们是在与真正的而不是伪造的客户端通话);
- 完整性(客户端和服务器的数据不会被修改);
- 加密(客户端和服务器的对话是私密的,无需担心被窃听);
- 效率(一个运行的足够快的算法,以便低端的客户端和服务器使用);
- 普适性(基本上所有的客户端和服务器都支持这些协议);
HTTPS协议的主要功能基本都依赖于TLS/SSL协议,提供了身份验证、信息加密和完整性校验的功能,可以解决HTTP存在的安全问题。
本小记主要记录一下TLS/SSL协议的握手过程。
最复杂的地方:TLS/SSL握手
TLS握手主要可以分成三种流程:
- 单向验证,也就是只对服务器的身份进行验证。
- 双向验证,对客户端和服务器都进行身份验证的握手。
- 简短握手,用于恢复之前的回话。
单向验证握手过程:
握手过程图,来自腾讯Bugly:
详细步骤分析:
1.ClientHello:
客户端发起请求,这条消息将客户端的功能和首选项传送给服务器。包含以下信息:
1. 客户端支持的SSL的指定版本;
2. 加密组件(Cipher Suite)列表(所使用的加密算法密钥长度等);
3. 支持的压缩算法 compression methods 列表,用于后续的信息压缩传输;
4. 随机数 random_C(随机数:1/3 共3个,第1个),用于后续的密钥的生成;
5. 扩展字段 extensions,支持协议与算法的相关参数以及其它辅助信息等;
抓包信息图片,来自腾讯Bugly:
2. ServerHello
ServerHello消息将服务器选择的连接参数传送回客户端。也就是服务端在客户端发送过来的加密组件列表压缩方法列表选择出了一种加密组件和压缩方法,发送回客户端确认,还有一个随机数 random_S (随机数 2/3)。
抓包信息图片,来自腾讯Bugly:
3.Server_Certificate 与 Sever_Hello_Done
服务器给客户端发送证书报文,报文中包含了一个公钥,这个公钥稍被在客户端用于加密一个随机数。报文中还有证书的签名信息,用于确认证书的合法性。(客户端读取证书中的相关的明文信息,采用相同的散列函数计算得到信息摘要,然后,利用对应CA的公钥解密签名数据,对比证书的签名信息,如果一致,则可以确认证书的合法性,即公钥合法;)除了公钥和签名,证书报文还包含了申请者的组织信息和个人信息、签发机构CA的信息、有效时间、证书序列号等信息的明文。证书=公钥+申请者与颁发者信息+签名;
server_hello_done,通知客户端 server_hello 信息发送结束;
4.证书校验
客户端接收到服务器发来的证书报文,进行证书校验,合法性验证包括如下:
- [证书链]的可信性 trusted certificate path;
- 证书是否吊销 revocation,有两类方式离线 CRL 与在线 OCSP,不同的客户端行为会不同;
- 有效期 expiry date,证书是否在有效时间范围;
- 域名 domain,核查证书域名是否与当前的访问域名匹配。
证书校验通过之后,继续下一步:
5. Client_Key_Exchange + Change_Cipher_Spec + Finished
- client_key_exchange:合法性验证通过之后,客户端计算产生随机数字 Pre-master(随机数 3/3),并用证书公钥加密,发送给服务器;
此时客户端已经获取全部的计算协商密钥需要的信息:两个明文随机数 random_C 和 random_S 与自己计算产生的 Pre-master,计算得到协商密钥;
enc_key=Fuc(random_C, random_S, Pre-Master)
- ChangeCipherSpec:消息表明发送端已取得用以生成连接参数的足够信息,已生成加密密钥,并且将切换到加密模式。客户端和服务器在条件成熟时都会发送这个消息。注意:ChangeCipherSpec不属于握手消息,它是另一种协议,只有一条消息,作为它的子协议进行实现。
- Finished:消息意味着握手已经完成。结合之前所有通信参数的 hash 值与其它相关信息生成一段数据,采用刚刚计算出来的还热乎协商密钥与之前约定算法进行加密,然后发送给服务器用于数据与握手验证;
6. 服务端的ChangeCipherSpec + Finish
- 服务器用私钥解密加密的 Pre-master 数据,基于之前交换的两个明文随机数 random_C 和 random_S,计算得到协商密钥:enc_key=Fuc(random_C, random_S, Pre-Master);
- 用这个刚计算出来的协商密钥解密客户端发送的 Finsh,验证数据和密钥正确性;
- 验证通过之后,服务器同样发送 ChangeCipherSpec 以告知客户端后续的通信都采用协商的密钥与算法进行加密通信;
- 发送Finsh;
最后客户端接收并计算所有接收信息的 hash 值,并采用协商密钥解密 Finsh,验证服务器发送的数据和密钥,验证通过则握手完成;
7.握手结束,开始使用协商密钥与算法进行加密的HTTP通信。
双向握手过程:
双向验证相比单向验证,多了一个验证客户端合法性的步骤。这个步骤有两条消息:
- Certificate Request :这条消息是服务器发送给客户端,要求客户端提供一份合法证书。服务器应该在ServerKeyExchange之后立即发送CertificateRequest消息。
- CertificateVerify : 这条消息是客户端发送给服务器,来证明自己确实拥有客户端证书的私钥。CertificateVerify紧跟在ClientKeyExchange之后被发送。
Android中的HTTPS
以OkHttp3.0为例
- 当服务器的CA不被系统信任时,就会发生 SSLHandshakeException。这时可通过自定义TrustManager以实现自己的信任证书集合。
- 如域名验证失败,则自定义HostnameVerifier实现域名白名单。
- 最后通过OkHttpClient.Builder()构建相应的配置。
参考代码如下:
先记到这,待完善。。。。