在前面的文章中,我们从密码学的角度讨论了一些主要的密码原语(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主要有三种变形:
- full handshake with server authentication(和一个认证的服务器来一次完整的握手);
- handshake with client and server authentication(一个认证的client和一个认证的server之前的握手)
- 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主要完成下面的四步操作:
- 交换容量(capabilities)并确定其他连接参数;
- 认证证书,或者使用其他方式进行认证(真实性);
- 协商构造一个master secret,用来对会话进行加密(保密性);
- 验证Handshake消息的完整性(完整性)。
其中,第2和第3步在一个操作中完成,叫做秘钥交换(key exchange)。分成这两个是为了强调认证的过程,毕竟真实性是很重要的一部分。
下图展示了一个未认证的client和一个已认证的server之间的Full Handshake流程:
- client开启会话并向server发送自己的容量,其中还有很多额外的信息;
- server选择连接的参数;
- server发送自己的证书链(必要的时候);
- 根据选择的参数,server发送生成master secret需要的额外信息;
- server发送一条信息标识协商结束;
- client发送生成master secret需要的额外信息;
- client开启加密模式并通知server;
- client对自己发送和收到的Handshake消息生成一个MAC并发送给server;
- server开启加密模式并通知client;
- 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
流密码的加密过程如下:
有两个步骤:
- 将序列号(Sequence Number)、头部Header和明文数据组合在一起生成MAC,由于Header数据没有被加密,在计算MAC过程中包含Header可以防止Header被篡改;包含序列号可以防止重放攻击;
- 明文数据和第一步计算出来的MAC放在一起加密得到密文。
3.2 Block Encryption
使用分组密码的流程如下:
- 计算序列号、header和明文的MAC(和流密码一样);
- 由于使用分组密码,可能需要填充padding操作(通常分组是16字节);
- 生成初始向量(initialization vector,IV),长度和分组一样长;
- 使用CBC模式加密明文,MAC和padding;
- 发送IV和密文。
如图:
这里有个问题就是,padding没有放在计算MAC之内。
3.3 Authenticated Encryption
认证密码把加密和完整性校验放在了一起,全称叫做authenticated encryption with associated data(AEAD)。过程如下:
- 生成一个64位的nonce;
- 对明文加密,同时使用认证算法将序列号和header添加进来;
- 将密文和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)
三个参数,secret
,label
和seed
。
而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))
由于使用了seed
和label
,同一个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…