HTTPS握手协议

简介

在介绍HTTPS握手协议之前,我们首先介绍一些密码学相关的知识,这样才可以更好的了解HTTPS握手协议

密码体制分类

密码体制从原理上可以分为单钥体制和双钥体制

单钥体制

单钥体制使用一把密钥进行加密和解密(也叫对称加密),系统的保密性取决于密钥的安全性,与算法的保密性无关。优点是加密计算量小、速度块,适合对大量数据进行加密的场景,常见的对称加密算法有DES、3DES、Blowfish、IDEA、RC4、RC5、RC6和AES。
对称加密的使用过程:

  1. A使用密钥加密明文发送给B
  2. B使用同一把密钥对密文解密,得到明文

双钥体制

双钥体制分别使用公钥和私钥进行加密解密操作,其中公钥可以公布,私钥是保密的,因此又称为公钥体制(非对称加密)。
公钥密码体制以前的整个密码学史中,所有的密码算法都是基于代换和置换这两个工具。而公钥密码体制则为密码学的发展提供了新的理论和技术基础,一方面,公钥密码算法和基本工具不是代换和置换,而是数学函数;另一方面,公钥密码算法以非对称的形式使用两个密钥,对保密性、密钥分配、认证都有着深刻的意义。
非对称加密的过程:

  • A要向B发送信息,A和B都要产生一对用于加密和解密的公钥和私钥。
  • A的私钥保密,A的公钥告诉B;B的私钥保密,B的公钥告诉A。
  • A要给B发送信息时,A用B的公钥加密信息,因为A知道B的公钥。
  • A将这个消息发给B。
  • B收到这个消息后,用自己的私钥解密,其他所有收到这个消息的人都无法解密,因为只有B才有私钥。
  • 反过来,B向A发送消息也是一样。
    常见的非对称加密算法有:RSA、Elgamal、ECC等。

密钥交换算法

由于公钥加密速度很慢,不适合进行保密通信,通常使用对称加密算法进行保密通信,那么双方如何就密钥达成共识便成了一个问题,密钥交换算法就应运而生。

采用非对称加密交换密钥

虽然非对称较密不适合保密通信,但是可以用于交换密钥,一次交换过程如下

  1. A生成对称加密密钥key,并用B的公钥加密,将密文发送给B
  2. B收到密文后使用自己的私钥解密得到密钥key
  3. 双方采用key进行加密通信

使用这种方式交换密钥时,即使攻击者拿到了密文也无法破解出密钥key,因为只有B的私钥可以解密

Diffie-Hellman密钥交换

交换过程如下

  1. Alice和Bob确定一个素数p以及该素数p的本原根a
  2. Alice选择随机数A计算 Ka = a ^A mod p
  3. Bob选择随机数B计算 Kb = a ^ B mod p
  4. 双方交换计算结果Ka、Kb
  5. A计算K = Kb ^ A mod p
  6. B计算K = Ka ^ B mod p

这样双方就共享同一个K了,而攻击者只能拿到p、a、Ka、Kb,想要得到K则必须得到A、B中的一个,这意味着需要求离散对数,因此破解K是不可行的。

MAC

MAC为消息认证码,指消息被一个密钥控制的公开函数作用后产生的用作认证符的、固定长度的数值,也称密码校验和,用于验证消息的完整性。
假设通信双方AB共享同一把密钥K和公开函数C,那么

  1. A发送消息M的时候计算MAC = C(K, M),将M || MAC发送给B
  2. B收到后采用相同方式计算MAC,如果不一致则认为M被篡改过

由于攻击者没有密钥K,所以在篡改M后无法生成对应的MAC,B就可以依此判断是否被篡改

数字签名

MAC只能保护通信双方被第三方攻击,却不能防止通信双方中的一方被另一方欺骗/伪造,比如

  • B使用和A共享的K伪造消息M生成MAC,随后声称这个消息来自于A
  • 由于B也持有K,A在发送消息M后可以否认是自己发送

性质

数字签名技术可以解决这个问题,数字签名具有以下性质

  1. 能够验签名生产者的身份
  2. 能用于证实被签消息的内容
  3. 可由第三方验证,从而解决通信双方的争议

流程

可以采用非对称加密进行数字签名

  1. 首先A使用Hash函数获得信息M的信息摘要D
  2. A使用私钥加密D得到Signature,将M || Signature发送给B
  3. B收到后使用A的公钥解密Signature得到信息摘要D,同时自己计算M的信息摘要D’,如果D‘和D一致,则证明信息M就是A发送的

因为只有A拥有私钥可以对M加密,所以可以确定该消息为A发送。之所以针对信息摘要D进行签名而不是M是因为M的签名长度和M是一个量级的,这样会增加传输的负载。

证书颁发机构(CA)

运用对称加密、密钥交换、MAC验证、数字签名技术来对通信链路进行安全性保护表面上已经很安全了,既能够加密信息,还保证了信息的完整性和可靠性。但是无论采用何种密钥交换协议进行密钥交换,在密钥交换完成前通信链路均不可信,那么攻击者可以接管通信链路,对密钥交换过程做手脚,与双方各进行一次密钥交换,最终接管通信。
想要解决这个问题,就需要解决信任问题,关键在于A如何信任获得的PubKB来自于B,CA可以解决这个问题。
CA即证书颁发机构(Certificate Authority),负责发放和管理数字证书的权威机构。CA的工作如下

  1. 服务器 example.com将从CA请求TLS证书,例如Digicert。
  2. Digicert将为example.com创建证书,证书将包含必要的数据,例如服务器名称,服务器的公钥等。
  3. Digicert创建证书的哈希值,并使用自己的私钥对其进行加密。
  4. 客户端(浏览器和操作系统)自带Digicert等权威机构的公钥。
  5. 客户端收到该签名证书时,它将使用Digicert公钥从签名生成哈希值,它还将使用证书中指定的散列算法生成数据(证书)的散列,如果两个哈希值匹配,则签名验证成功并且证书是可信的。
  6. 现在浏览器可以使用证书中指定的example.com的公钥继续进行身份验证过程。

HTTPS

介绍

我们都知道HTTP请求是明文传输的,存在通信内容被窃听、篡改、无法验证通信对象身份等问题,HTTPS是在HTTP的基础上发展而来,意为HTTP Secure,它就是为了解决HTTP协议安全性不足的问题而诞生的。
HTTPS实际上是在HTTP和TCP之间加入了一层TLS/SSL。

TLS(Transport Layer Security,传输层安全性协议)以及其前身SSL(Secure Sockets Layer,安全套接层)是一种安全协议,为其上层协议提供在不安全信道上的安全传输服务。

在HTTPS中,对发送者而言TLS/SSL用于加密来自HTTP协议的数据,对接收者而言TLS/SSL用于解密来自TCP协议的数据,这样就可以保证数据的安全性。由于客户端/服务端均存在加解密的操作,所以双方需要对加密算法/密钥等信息达成共识,这一过程也就是TLS握手协议所要达成的目的。

TLS握手过程

客户端与服务端连接的时候会进行一次TLS握手过程,就加密算法/密钥等信息达成共识,接下来详细介绍该过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Client                                        Server
------ -----
ClientHello -------->
ServerHello
Certificate*
ServerKeyExchange*
<-------- ServerHelloDone
ClientKeyExchange
[ChangeCipherSpec]
Finished -------->
[ChangeCipherSpec]
<-------- Finished

Application Data <-------> Application Data

ClientHello

第一步为客户端发送hello报文,通过wireshark我们可以看到内容如下

ClientHello报文传递给服务端如下信息

  1. Version: 客户端支持的TLS协议版本

  2. Random: 客户生成的随机数,该随机数会在后面的过程中用到。

  3. Cipher Suites:客户端支持的密码套件,由客户按优先顺序排列,提供给服务端来选择,这里取其中一个来介绍密码套件的组成格式

    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)
    TLS: 使用TLS协议
    ECDHE: 密钥交换算法
    ECDSA: 签名或验证算法
    AES_128_GCM: 加解密算法
    SHA256: 消息验证算法(MAC)

  4. Compression Methods: 客户端支持的压缩算法,由于使用压缩可能导致安全问题,从TLS 1.3开始,协议就禁用了TLS压缩。

  5. Extension:随后就是一系列的扩展参数了,用于扩展功能

ServerHello

服务端接收到客户端hello报文后,会根据报文内容发送服务端hello报文给客户端

服务器会检查客户端支持的TLS版本和密码套件等信息,如果服务端接受这些条件,则回复服务端所选择的密码套件等信息给客户端,反之则发送握手失败信息。

  1. Version:服务端支持的TLS版本
  2. Random:服务端生成的随机数,与客户端的随机数在后面过程会被使用
  3. Cipher Suite:服务端选择的密码套件,后续HTTPS握手/通信均使用该密码套件

Certificate

接下来服务端将会把证书发送给客户端

证书包含以下信息

  1. signature:签名算法
  2. encrypted:该证书的签名
  3. issuuer:该证书的颁布机构,用于配合签名检验该证书的合法
  4. validity:证书的有效期
  5. subject:持有该证书组织的信息
  6. subjectPublicKeyInfo:带公钥和用于生成公钥的算法。此密钥用于交换密钥,我们将在稍后讨论。

ServerKeyExchange

紧接着服务端发送ServerKeyExchange报文与客户端进行密钥交换流程,仅当服务端的证书不足以完成和客户端交换预主密钥(后面会介绍)的情况下才会发送,比如Diffie-Hellman算法需要通信双方协作才能进行密钥交换。需要发送ServerKeyExchange的交换算法包括以下几种

ECDH_ECDSA
ECDHE_ECDSA
ECDH_RSA
ECDHE_RSA
ECDH_anon


从之前的ServerHello报文可以看出服务端选择了ECDHE算法来做密钥交换,RSA做签名校验。所以Server Key Exchange报文就包含了ECDHE交换服务端需要提供的相关参数,其中这些参数通过RSA签名,客户端收到后通过服务端证书上的公钥来进行签名验证,防止中间人攻击。

Server Hello Done

服务端昨晚上述操作后就会发送ServerHelloDone报文告知服务端已经发送完毕。

ClientKeyExchange

ClientKeyExchange和ServerKeyExchange不同,并不是可选步骤,而是必要步骤,这一步将生成预主密钥并与服务端就预主密钥达到共识,即双方得到共同的预主密钥。
如果采用Diffie-Hellman算法进行交换,则还需要客户端提供相应参数给服务端,双方各自计算出预主密钥(由Diffie-Hellman算法保证双方得到的密钥是一致的)。如果是RSA交换则并不需要ServerKeyExchange,只需要客户端自己随机生成预主密钥,使用服务端的公钥加密传递给服务端即可。

生成主密钥

到目前为止双方共享三个值

  1. 客户端随机数ClientHello.random,公开信息
  2. 服务端随机数ServerHello.random,公开信息
  3. 预主密钥pre_master_secret,保密信息

计算主密钥的方式由RFC 5356规定:

1
master_secret = PRF(pre_master_secret,“master secret”,ClientHello.random + ServerHello.random)[0..47];

其中,PRF是协议约定的伪随机函数。
得到主密钥后也不能开始加解密操作,而是需要根据主密钥生成四把密钥,两把用来加解密,两把用来生成MAC(因为部分对称加密算使用相同的密钥加密并不安全)。

  • 客户端写入加密密钥:客户端用来加密数据,服务器用来解密数据。
  • 服务器写入加密密钥:服务器用来加密数据,客户端用来解密数据。
  • 客户端写入MAC密钥:客户端用来创建MAC,服务器用来验证MAC。
  • 服务器写入MAC密钥:服务器用来创建MAC,客户端用来验证MAC。

由于以上步骤可以保证明文不会被破解,但是没法保证不会被篡改,所以在通信的时候会针对明文生成MAC与密文一起发送,接收方解密出明文后计算出MAC进行比对,以保证通信内容没有被篡改。

测试密钥

双方就密钥达成共识后将会进行一次测试阶段,客户端将测试文本使用客户端密钥加密,发送给服务端,服务端解密后使用自己的密钥加密后再发回给客户端,客户端使用服务端密钥解密后对比文本是否一致,如果一致则测试通过。

性能优化

HTTPS相比HTTP增加的耗时主要为两个点

  1. TLS握手协议增加的耗时
  2. 加解密数据的耗时

SessionId

在实际请求中,并不是每次都需要进行TLS握手的。如果之前已经完成过一次TLS握手协议,服务端会生成一个sessionId发送给客户端,客户端将该sessionId和当前的通信密钥信息保存起来。下次客户端连接同一个服务端的时候将会带上sessionId,由于双方都保存有该sessionId对应的密钥信息,所以就无须进行后续握手流程,直接使用sessionId对应的密钥信息进行通信。

Session Ticket

以上方案会导致服务端需要很大的空间去存储sessionId和对应的密钥信息,所以产生了SessionTicket方案。服务端将密钥信息加密后生成一个SessionTicket发送给客户端,客户端进行保存,在后续的连接中客户端在ClientHello中带上SessionTicket,服务端解密验证后直接采用对应的密钥信息进行通信。

Charles代理原理

通过上述分析的TLS握手过程,我们可以知道HTTPS可以有效防止中间人攻击。但是用过Charles的人都知道,我们可以抓取HTTPS的通信明文且进行更改,那么Charles是如何做到的呢?
HTTPS的安全性本质上基于证书的可靠性,默认情况下客户端内置可信的CA信息,但是如果用户手动将不可信的CA加入信任列表,那么就无法保证安全性了,Charles就是这样做的。在使用Charles抓包的时候,我们需要安装Charles提供的证书并加入信任列表,这样Charles就可以针对端上的HTTPS请求发起中间人攻击,示意图如下所示

由于Charles的证书已经在信任名单,所以客户端在校验伪造证书的时候会使用Charles的CA证书提供的公钥进行签名验证,验证通过后继续后续连接流程。通过浏览器查看证书也可以证实这一点

参考资料