SSL and TLS (Part 3): Protocol

在前面的文章中,我们从密码学的角度讨论了一些主要的密码原语(cryptographic primitive),最后通过组合这些密码原语构成一个可用的安全通信协议。

这篇文章更进一步,看看TLS是怎么做的。不过TLS的细节太多了也有点难,这里只是在较高的层次上给出一个清晰的认识,不深入复杂的细节。

1. Record Protocol

像网络模型里其它层一样,TLS也需要一个协议来对数据进行封装。这就是Record Protocol,每个TLS record结构如下:

如果数据过长,TLS会将数据分成大小合适的两条record;如果数据少的话也会合并到一起构成一个record。

record中的data就是使用Alice和Bob协商的方法和参数加密后的数据了。这样,只要有一个建立好的TLS链接,后续传输的数据都是安全的了。

那么接下来的问题就是,TLS是怎么建立一条安全的链接的

2. Handshake Protocol

Handshake Protocol是TLS中最重要的一个协议。在这个过程中,通信的双方,Alice和Bob需要商量好一个具体的方法以及参数,用来在接下来的通信过程中对数据进行加密。这就是Handshake Protocol的目的。

根据支持的协议和配置的不同,Handshake Protocol主要有三种变形:

  1. full handshake with server authentication(和一个认证的服务器来一次完整的握手);
  2. handshake with client and server authentication(一个认证的client和一个认证的server之前的握手)
  3. abbreviated handshake that resumes an earlier session(之前握过手,这次就可以简单点了)。

在讨论握手细节之前,看看消息的格式。

2.1 Message

Handshake Protocol的消息格式如下:

struct{
    HandshakeType msg_type;        /* handshake type */
    uint24 length;                /* remaining bytes in message */
    HandshakeMessage message;
} Handshake;

在协商的过程中会有不同的阶段,也有不同类型的消息,每个消息都有自己的格式。message字段的内容根据消息类型而定。

2.2 Full Handshake

好的,两个之前完全陌生的人见面了,需要“完整地握一次手”认识一下。

在一个Full Handshake过程中,client和server主要完成下面的四步操作:

  1. 交换容量(capabilities)并确定其他连接参数;
  2. 认证证书,或者使用其他方式进行认证(真实性);
  3. 协商构造一个master secret,用来对会话进行加密(保密性);
  4. 验证Handshake消息的完整性(完整性)。

其中,第2和第3步在一个操作中完成,叫做秘钥交换(key exchange)。分成这两个是为了强调认证的过程,毕竟真实性是很重要的一部分。

下图展示了一个未认证的client和一个已认证的server之间的Full Handshake流程:

  1. client开启会话并向server发送自己的容量,其中还有很多额外的信息;
  2. server选择连接的参数;
  3. server发送自己的证书链(必要的时候);
  4. 根据选择的参数,server发送生成master secret需要的额外信息;
  5. server发送一条信息标识协商结束;
  6. client发送生成master secret需要的额外信息;
  7. client开启加密模式并通知server;
  8. client对自己发送和收到的Handshake消息生成一个MAC并发送给server;
  9. server开启加密模式并通知client;
  10. server对自己发送和收到的Handshake消息生成一个MAC并发送给client。

到目前为止,如果没有错误的话,那么一个安全的连接就建立了。接下来详细看看Handshake消息的细节。

2.2.1 ClientHello

作为握手的第一个消息,ClientHello包含一些重要的参数和首选项。

毕竟是要和sever协商一个加密的方法,所以client需要告诉server,我都支持哪些加密算法。之外,如果不是第一次建立连接,希望复用之前协商的结果的话,就应该有个字段标识出之前保存的会话(session)。这就大概包含了主要的信息:

字段 含义 备注
Version 版本 比如TLS 1.2
Random 随机信息 包含一个随机字符串(32字节)
Session ID 会话ID 第一次为空,如果希望复用的话就使用对应的ID
Cipher suites 支持的加密算法
Extensions 扩展模块 其他的信息可以放在这个模块中

2.2.2 ServerHello

对于server,当收到一个ClientHello后,就需要作出回应。因为client在ClientHello中列出了支持的算法,这里server需要选择一个,加上其他的参数一起通知给client,这就是ServerHello的作用。

ServerHello和ClientHello的消息格式大致相同。

2.2.3 Certificate

连接参数和加密算法商量好之后,server需要发送证书给client,以便client来做认证。这就是Certificate消息的作用。

server需要保证发送的证书和之前协商好的加密算法匹配。常用的证书是X.509格式的。

大多数情况下使用的是公钥加密算法来进行认证(RSA或ECDSA)。

Certificate消息是可选的,因为有的认证方法不需要证书。

2.2.4 SeverKeyExchange

好的,发送完Certificate之后,client认证了server。接下来就可以开始秘钥交换了(key exchange)。这就是ServerKeyExchange消息的作用。随着使用的秘钥交换协议的不同,这个消息的格式也不一样。知道这个消息的作用就好了。

ServerKeyExchange也是可选的。

2.2.5 ServerHelloDone

到这里,server要做的就暂时结束了,接下来就发送一个ServerHelloDone通知一下client,然后等着client的进一步消息。

2.2.6 ClientKeyExchange

秘钥交换是需要通信的双方一起的。server发送完ServerKeyExchange之后,需要client发送自己的消息,这就是ClientKeyExchange。

这里对秘钥交换多说一些。在TLS中,会话的安全依赖于一个48位长的秘钥,叫做master secret。但是在秘钥交换过程中交换的不是这个master secret,而是另一个值,premaster secret,通过这个值client和server能够计算出master secret。

TLS支持多种秘钥交换算法比如RSA、DHE_RSA、ECDHE_RSA和ECDHE_ECDSA等。不用管细节了,这些算法都挺复杂的。

2.2.7 ChangeCipherSpec

当client接收了ServerKeyExchange并生成自己的ClientKeyExchange之后,那就完成了秘钥交换(具体是怎么做的就不展开了)。根据协商好的秘钥,就可以对后序的数据加密了。同时,对于server来说,收到ClientKeyExchange之后也可以获得通信的秘钥了,然后就可以是所有这个秘钥加密通信了。

这就是ChangeCipherSpec的作用。不过ChangeCipherSpec不是Handshake Message。

2.2.8 Finished

秘钥交换完了,那握手就可以结束了。不过,前面的过程我们实现了保密性和真实性,还少了完整性,这就是Finished达到的目标。

Finished中有一个verify_data字段,这个字段是通过下面的方法计算得到的,可以用来保证数据的完整性,并在有中间人修改时可以检测到:

verify_data = PRF(master_secret, finished_label, Hash(handshake_message))

其中PRF()是为随机函数(pseudorandom function),可以生成任意长度的随机数;finished_label对于不同的消息值也不同(client发送的就是”client finished”,server发送的就是”server finished”);Hash()是协商好的哈希函数。

2.3 Client Authentication

server是必须认证的,client的认证是可选的。不过server可以通过发送一个CertificateRequest来请求对client进行认证。然后client发送自己的Certificate消息。整体的流程如下:

2.3.1 CertificateRequest

server通过发送这个消息来请求对client进行认证。

2.3.2 CertificateVerify

client发送这个消息来对自己的身份进行认证。这个消息包含对到目前为止收到的Handshake消息的签名。这样server就可以进行认证了。

2.4 Session Resumption

由于加密算法的协商和参数的选择,一个完整的握手需要两个RTT(round-trip time)。同时,握手期间的加解密操作也很耗时,如果能复用之间协商的结果的话,那就可以减轻负担了。

在一个完整握手过程中,如果server希望复用,那么就可以在ServerHello中返回一个session ID给client,然后将数据保存起来。如果client希望复用,在ClientHello中加上那个session ID就可以了:

这样只有一个RTT就完成了握手。

除了session ID,还可以使用session tickets的方式来恢复会话。在TLS 1.3中,使用的是PSK(Pre-Shared Key)。

这里简单描述了一个握手的整个过程。握手完成之后,就可以安全地通信了。

3. 加密

完成了认证和秘钥交换,就可以进行加密操作了。TLS支持多种加密算法,常用的是AES,有三种不同类型的加密,stream,block和authenticated。在TLS中,数据的完整性校验也包含在加密过程中。

3.1 Stream Encryption

流密码的加密过程如下:

有两个步骤:

  1. 将序列号(Sequence Number)、头部Header和明文数据组合在一起生成MAC,由于Header数据没有被加密,在计算MAC过程中包含Header可以防止Header被篡改;包含序列号可以防止重放攻击;
  2. 明文数据和第一步计算出来的MAC放在一起加密得到密文。

3.2 Block Encryption

使用分组密码的流程如下:

  1. 计算序列号、header和明文的MAC(和流密码一样);
  2. 由于使用分组密码,可能需要填充padding操作(通常分组是16字节);
  3. 生成初始向量(initialization vector,IV),长度和分组一样长;
  4. 使用CBC模式加密明文,MAC和padding;
  5. 发送IV和密文。

如图:

这里有个问题就是,padding没有放在计算MAC之内。

3.3 Authenticated Encryption

认证密码把加密和完整性校验放在了一起,全称叫做authenticated encryption with associated data(AEAD)。过程如下:

  1. 生成一个64位的nonce;
  2. 对明文加密,同时使用认证算法将序列号和header添加进来;
  3. 将密文和nonce一起发送。

4. 密码学操作

上面是在较高的层次的层次上讨论TLS是怎么做的。接下来从密码学的角度看看一些操作。

4.1 伪随机函数

前面我们说过了伪随机函数PRF(pseudorandom function),用来产生任意长度的伪随机数。从TLS 1.2开始,所有的算法都需要指定自己的PRF。TLS 1.2中,PRF定义在P_hash函数上的:

PRF(secret, label, seed) = P_hash(secret, label + seed)

三个参数,secretlabelseed

P_hash函数是基于HMAC的:

P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
                       HMAC_hash(secret, A(2) + seed) +
                       HMAC_hash(secret, A(3) + seed) + ...

其中A(i)函数定义如下:

A(1) = HMAC_hash(secret, seed)
A(2) = HMAC_hash(secret, A(1))
...
A(i) = HMAC_hash(secret, A(i-1))

由于使用了seedlabel,同一个secret可以使用多次。

4.2 Master Secret

之前说过,秘钥交换的不是master secret,而是premaster secret,然后生成master secret。通过下面的方法可以生成48字节长的master secret:

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

4.3 生成秘钥

有了master secret之后,就可以生成秘钥了:

key_block = PRF(SecurityParamters.master_secret,
                "key expansion",
                SecurityParameters.server_random + SecurityParameters.client_random)

生成的key_block可以分成六部分:两个MAC秘钥,两个加密秘钥还有两个初始化变量(IV)。不同的秘钥用于不同的操作,这样可以防止秘钥的重复使用。

5. Cipher Suites

最后,介绍一下加密算法。一个cipher suite就是一组密码原语以及定义实现的参数的组合。主要包括下面的东西:

  • 认证方法(Authentication method)
  • 秘钥交换方法(Key exchange method)
  • 加密算法(Encryption algorithm)
  • 加密秘钥长度(Encryption key size)
  • 密码模式(Cipher mode)
  • MAC算法(MAC algorithm)
  • PRF
  • Finished消息使用的哈希函数
  • verify_data的长度

一个cipher suite的名字就指出了上面那些内容的具体实现:

下表列出了一些常见的cipher suite:

Cipher Suite Name Auth KX Cipher MAC PRF
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 RSA ECDHE AES-128-GCM - SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 ECDSA ECDHE AES-256-GCM - SHA384
TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA RSA DHE 3DES-EDE-CBC SHA1 Protocol
TLS_RSA_WITH_AES_128_CBC_SHA RSA RSA AES-128_CBC SHA1 Protocol
TLS_ECDHE_ECDSA_WITH_AES_128_CCM ECDSA ECDHE AES-128-CCM - SHA256

好了,到现在为止我们对TLS是如何操作的有了一个大概的认识。

To Be Continued…


 Previous
Roman Republic Roman Republic
由于疫情,有了一个漫长的假期。不玩游戏不看电影不追剧,看书成了唯一的消遣。十几天的时间看完了盐野七生的15卷本《罗马人的故事》,对古罗马的脉络有了一个大体的了解。在这篇文章中,对罗马王政时期和共和国时期的历史做一下简单的梳理。 1. 王
2020-03-02
Next 
SSL and TLS (Part 2): Cryptography SSL and TLS (Part 2): Cryptography
在SSL and TLS (Part 1): Brief Intro中简单介绍了一下SSL/TLS,并以需要解决的三个问题结尾。这三个问题是: 保密性(Confidentiality) 真实性(Authenticity) 完整性(Inte
2020-01-19
  You Will See...