TLS协议

本文是对《HTTPS权威指南:在服务器和Web应用上部署SSL/TLS和PKI》第2章内容的整理,该书的本章内容比较详细的介绍了TLS 1.2的内容。

记录协议

TLS以记录协议(record protocol)实现。

image2017-2-6-RecordProtocol@2x.png

何为记录协议?没有找到比较权威的定义,书中描述了如下特点:

  • 消息传输。记录协议传输由其他协议层提交给它的不透明数据缓冲区。如果缓冲区超过记录的长度限制(16384字节),记录协议会将其切分成更小的片段;反过来,同属一个子协议的多个小缓冲区可以组合成单独的记录
  • 加密以及完整性验证。一旦协商握手完成,记录协议会按照协商对数据进行加密和完整性验证
  • 压缩。记录协议还提供了数据压缩服务,但一般不使用,通常是由上层协议处理压缩
  • 扩展性。记录协议只关注数据传输和加密,而将所有其他特定转交给子协议,可以在此基础上扩展子协议

TLS的RFC定义了4个核心子协议:

  • 握手协议(handshake protocol)
  • 密钥规格变更协议(change cipher spec protocol)
  • 应用数据协议(application data protocol)
  • 警报协议(alert protocol)

下文会重点对这4个核心子协议展开详细描述。

握手协议

谈起握手,难免会想到TCP的握手过程,三次协商,非常清晰。相较而言,TLS的握手过程似乎没那么容易理清楚,一共有几次协商呢?我之前看到的资料有说6次,有说9次等等。实际上,根据使用的功能、配置、扩展的不同,协商的变种非常多,一般来说,通常需要交换6~10次消息。有3种常见的握手流程:

  • 完整的握手,对服务器进行身份验证
  • 恢复之前的会话采用的简短握手
  • 对客户端和服务器都进行身份验证的握手

完整的握手(单向验证)

这是最常见的握手姿势(其他环境下的握手,懒得分析了,都差不多),时序图如下:

image2017-2-7-FullConnection@2x.png

下表是对上述10次协商消息的说明:

序号 消息 必须 作用 说明
1 ClientHello 客户端开始新的握手,将自身支持的功能告诉服务器 包括:Version(指示客户端支持的最佳协议版本)、Random(32byte随机数,用于后续身份验证,可能为空)、Session ID(会话ID,第一次连接时,为空)、Cipher Suites(客户端所支持的密码套件列表)、Compression( 客户端支持的压缩算法,默认为null,一般为默认值)、Extensions(扩展)
2 ServerHello 服务器选择连接参数 包括:Version(服务端所支持的协议版本)、Random(32byte随机数)、Session ID(会话ID)、Cipher Suite(服务端所选用的密码套件)、Extensions(扩展)
3 Certificate 服务器发送其证书链 认证信息,一般是服务器的X.509证书链,也可以是PGP密钥
4 ServerKeyExchange 根据选择密钥交换方式,服务器发送生成master secret的额外信息 携带密钥交换的额外数据。消息内容对于不同的密码套件会存在差异,譬如对于RSA密钥交换算法,根本不需要这个消息
5 ServerHelloDone 服务器告诉客户端自己已完成了协商过程 表明服务器已经将所有预计的握手消息发送完毕
6 ClientKeyExchange 客户端发送生成master secret所需的额外信息 携带密钥交换提供的所有信息。消息内容对于不同的密码套件会存在差异
7 ChangeCipherSpec 客户端切换加密方式并通知服务器 表明客户端已取得用以生成连接参数的所有信息,已经生成加密密钥(master secret),并将切换到加密模式
8 Finished 客户端计算发送和接收到的握手消息的MAC并发送 意味着握手已经完成,消息内容将加密
9 ChangeCipherSpec 服务器切换加密方式并通知客户端 表明服务端已取得用以生成连接参数的所有信息,已经生成加密密钥(master secret),并将切换到加密模式
10 Finished 服务器计算发送和接收到的握手消息的MAC并发送 意味着握手已经完成,消息内容将加密

将上述10次协商分成几部分来阐述。

首先是ClientHello和ServerHello,这一个回合的交互用来建立基本的连接,并协商协议版本、密码套件、压缩方法,产生一个Session ID,ClientHello.Extensions和ServerHello.Extensions还可以用来扩展协商其他信息;ClientHello.Random和ServerHello.Random在计算master secret时会用到。

image2017-2-7-ClientHello@2x.png

P.S: 关于密码套件,下文会有说明。

实际的密钥交换在主要3(Certificate)、4(ServerKeyExchange)、6(ClientKeyExchange)这三个消息中完成。

image2017-2-7-Certificate@2x.png

这几个消息的具体内容对于不同的密码套件会存在差异。

对于Certificate消息,一般都是服务器的X.509证书链(包括证书公钥),Client可以对该证书进行验证,确保Server是合法受信任的,Certificate也可能与PGP密钥有关,对PGP不熟,就不展开了…

对于ServerKeyExchange,它的内容与双方协商的密钥交换方法有关。假如使用的是RSA密钥交换算法,客户端生成master secret后,使用Certificate中的公钥对其进行加密,然后发送给服务端就ok了,无需Server提供更多的密钥生成参数;换句话说,ServerKeyExchange在RAS密钥交换算法中没有什么卵用。

P.S: 关于密钥交换算法,下文会有更多说明。

生成master secret后,Client还得向Server发送两个消息:ChangeCipherSpec和Finished;作为回应,Server也会向Client发送ChangeCipherSpec和Finished消息。

image2017-2-7-ChangeCipherSpec@2x.png

接下来以Q & A的形式,将握手流程搞得更清楚一点。

Q: 握手过程中,Server连续向Client发送ServerHello、Certificate、ServerKeyExchange、ServerHelloDone这几个消息,可以把它们揉成一起吗?

A: 当然可以,本文开头描述记录协议的特点时讲到,对于同一个子协议的消息,如果内容过短,记录协议会将它们组合成一条记录;同样,如果消息内容过长,记录协议会将它拆分为多条记录(多次发送);换句话说,是否将这些连续发送的消息揉成一起,还得看缓冲区的大小和消息内容的长度;用户无需操心。

Q: 为毛ChangeCipherSpec是一个独立的协议,而不是握手协议的一部分?

A: StackExchange里有一个相关的问题:Why is change cipher spec an independent protocol content type and not part of Handshake Messages。其中的回答描述得非常清楚,结合这个答案,我的阐述是:ChangeCipherSpec的作用是告诉另一端,接下来的消息将使用已经生成的master secret来加密。因此,需要确保ChangeCipherSpec之后的消息不会和之前的消息组合成一条记录,因为加密方式不一样嘛!「ChangeCipherSpec是一个单独的子协议」这个事实能够解决这个问题,因为记录协议组合多个消息的前提是,这些消息同属一个子协议。

Q: Finished消息有什么用?为啥最后Server还要向Client发送ChangeCipherSpec和Finished消息呢?

A: Finished消息的数据其实非常简单,它包含一个vertify_data字段,该字段值的计算公式可以简单描述为:

vertify_data = PRF(master_secret, finished_label, Hash(handshake_messages))
// PRF(pseudorandom function,伪随机函数),该算法在密码套件中说明
// master_secret,主密钥
// finished_label,标签,一个字符串
// Hash(handshake_messages),即握手过程中所有消息的散列值

两端的PRF算法是一样的,但会使用不同的标签(finished_label),对于server,对应"server finished",对于client,对应"client finished"

vertify_data字段有什么用呢?该字段用于验证密钥交换和认证过程都是ok了;RFC5246的描述是:

Once a side has sent its Finished message and received and validated the Finished message from its peer, it may begin to send and receive application data over the connection.

可以看到,两端只有在发出Finished消息、接收对方的Finished消息并且验证通过后,才可以在信道中发送和接收应用层数据。

P.S: 似乎RFC并没有强调发出Finished消息和接收Finished的次序…

计算Master Secret

握手阶段的所有协商,最终目的是计算得到master secret,后者用于传输应用层数据时的加密算法(对称加密算法)的密钥,粗略描述计算过程很简单:

master_secret = PRF(pre_master_secret, "master secret", ClientHello.random + ServerHello.random)
// 48 bytes

密码套件

之前的第三弹 理解HTTPS对密码套件(Cipher Suite)已有所描述,这一部分再补充一下。下图描述了密码套件的名称构成:

image2017-2-7-Cipher-Suite@2x.png

图中的这个套件在不同版本下的TLS,有不同的解释,对于TLS 1.2而言,它描述的信息包括:

  • 密钥交换:ECDHE_RSA
  • 加密:AES_128_GCM
  • MAC(Message Authentication Code,用于计算消息摘要,与完整性保护有关):SHA256
  • PRF(用于计算master secret):SHA256

密钥交换

TLS 1.2支持的密钥交换算法有很多,譬如RSA、Diffie-Hellman(DH)、椭圆曲线Diffie-Hellman等,对加密而言,我完全是门外汉,本文只是简单介绍RSA和DH这两种密钥交换算法,目的仍然是帮助更好理解TLS协议。

RSA密钥交换

RSA本身是公钥加密算法(非对称加密算法),其密钥交换的核心思想非常简单:客户端产生一个48字节的pre master secret,然后使用server的证书的公钥对其进行加密,然后传给server,server收到后,使用私钥进行解密;之后,双方使用相同的pre master secret,在之前协商的PRF计算得到master secret。RFC的描述是:

When RSA is used for server authentication and key exchange, a 48- byte pre_master_secret is generated by the client, encrypted under the server’s public key, and sent to the server. The server uses its private key to decrypt the pre_master_secret. Both parties then convert the pre_master_secret into the master_secret, as specified above.

RSA非常直接简单,但是存在一个非常严重的弱点。用于加密pre master secret的公钥,一般会保持多年不变。如果私钥被窃取了,那么攻击者就可以恢复得到pre master secret,从而危害会话的安全性。

Diffie-Hellman密钥交换

Diffie-Hellman(DH)密钥交换是一种密钥协定的协议,它使两端在不安全的信道上生成共享密钥称为可能。

抛开算法细节,DH密钥交换需要6个参数,其中包括两个域参数(使用g、p标记),一般由server提供;协商过程中,客户端和服务器各自生成两个参数,对于客户端,使用c和C标记,对于服务器,使用s和S标记,c和s是私密的,C和S分别由c和s计算得来,需要在协商过程中传给对方。对于TLS而言,DH最终产生的目标数即上文提到的pre master secret,如下图:

image2017-2-7-Diffie-Hellman@2x.png

DH密钥交换算法的安全性取决于域参数的质量,域参数由server提供,client对之无能为力。另外,DH算法是计算密集型任务,容易受到阻塞性攻击,即攻击者请求大量的密钥,受攻击者得花费相当多的资源去做没啥意义的计算,因此DH密钥交换通常与身份验证联合使用,以避免中间人攻击。

P.S: 关于Diffie-Hellman的更多内容,参考Diffie-Hellman