1.2.SSL协议组成
- 上层:
- SSL握手协议(SSL handshake protocol)
- SSL密码变化协议(SSL change cipher spec protocol)
- SSL警告协议(SSL alert protocol)
- 底层:
- SSL记录协议(SSL record protocol)。
- 添加对CBC攻击的保护:
- 隐式IV被替换成一个显式的IV。
- 更改分组密码模式中的填充错误。
- 支持IANA登记的参数。
- 可使用密码组合选项指定伪随机函数使用SHA-256替换MD5-SHA-1组合。
- 可使用密码组合选项指定在完成消息的哈希认证中使用SHA-256替换MD5-SHA-1算法,但完成消息中哈希值的长度仍然被截断为96位。
- 在握手期间MD5-SHA-1组合的数字签名被替换为使用单一Hash方法,默认为SHA-1。
- 增强服务器和客户端指定Hash和签名算法的能力。
- 扩大经过身份验证的加密密码,主要用于GCM和CCM模式的AES加密的支持。
- 添加TLS扩展定义和AES密码组合。所有TLS版本在2011年3月发布的RFC 6176中删除了对SSL的兼容,这样TLS会话将永远无法协商使用的SSL 2.0以避免安全问题。
- 将密钥协商和认证算法从密码包中分离出来。
- 移除脆弱和较少使用的命名椭圆曲线支持(参见椭圆曲线密码学)。
- 移除MD5和SHA-224密码散列函数的支持。
- 请求数字签名,即便使用之前的配置。
- 集成HKDF和半短暂DH提议。
- 替换使用PSK和票据的恢复。
- 支持1-RTT握手并初步支持0-RTT。
- 通过在(EC)DH密钥协议期间使用临时密钥来保证完善的前向安全性。
- 放弃许多不安全或过时特性的支持,包括数据压缩、重新协商、非AEAD密码本、静态RSA和静态DH密钥交换、自定义DHE分组、点格式协商、更改密码本规范的协议、UNIX时间的Hello消息,以及长度字段AD输入到AEAD密码本。
- 禁止用于向后兼容性的SSL和RC4协商。
- 集成会话散列的使用。
- 弃用记录层版本号和冻结数以改进向后兼容性。
- 将一些安全相关的算法细节从附录移动到标准,并将ClientKeyShare降级到附录。
- 添加带有Poly1305消息验证码的ChaCha20流加密。
- 添加Ed25519和Ed448数字签名算法。
- 添加x25519和x448密钥交换协议。
- 将支持加密服务器名称指示(EncryptedServerNameIndication, ESNI)。
- TLS 记录协议(TLS Record):通过使用客户端和服务端协商后的秘钥进行数据加密传输。
- TLS 握手协议(TLS Handshake):客户端和服务端进行协商,确定一组用于数据传输加密的秘钥串。
- 更安全的MAC算法
- 更严密的警报
- “灰色区域”规范的更明确的定义
- 只验证服务器的握手过程
- 验证服务器和客户端的握手过程
- 恢复原有会话的握手过程
第一步,客户端给出协议版本号、一个客户端生成的随机数(Client random),以及客户端支持的加密方法。
第二步,服务端确认双方使用的加密方法,并给出数字证书、以及一个服务器生成的随机数(Server random)。
第三步,客户端确认数字证书有效,然后生成一个新的随机数(Premaster secret),并使用数字证书中的公钥,加密这个随机数,发给服务端。
第四步,服务端使用自己的私钥,获取客户端发来的随机数(即Premaster secret)。
第五步,客户端和服务端根据约定的加密方法,使用前面的三个随机数,生成"对话密钥"(session key),用来加密接下来的整个对话过程。
步骤 2. ServerHello – 服务器端收到客户端信息后,选定双方都能够支持的 SSL/TLS 协议版本和加密方法及压缩方法,返回给客户端。
(可选)步骤 3. SendCertificate – 服务器端发送服务端证书给客户端。
(可选)步骤 4. RequestCertificate – 如果选择双向验证,服务器端向客户端请求客户端证书。
步骤 5. ServerHelloDone – 服务器端通知客户端初始协商结束。
(可选)步骤 6. ResponseCertificate – 如果选择双向验证,客户端向服务器端发送客户端证书。
步骤 7. ClientKeyExchange – 客户端使用服务器端的公钥,对客户端公钥和密钥种子进行加密,再发送给服务器端。
(可选)步骤 8. CertificateVerify – 如果选择双向验证,客户端用本地私钥生成数字签名,并发送给服务器端,让其通过收到的客户端公钥进行身份验证。
步骤 9. CreateSecretKey – 通讯双方基于密钥种子等信息生成通讯密钥。
步骤 10. ChangeCipherSpec – 客户端通知服务器端已将通讯方式切换到加密模式。
步骤 11. Finished – 客户端做好加密通讯的准备。
步骤 12. ChangeCipherSpec – 服务器端通知客户端已将通讯方式切换到加密模式。
步骤 13. Finished – 服务器做好加密通讯的准备。
步骤 14. Encrypted/DecryptedData – 双方使用客户端密钥,通过对称加密算法对通讯内容进行加密。
步骤 15. ClosedConnection – 通讯结束后,任何一方发出断开 SSL 连接的消息。
(1)生成对话密钥一共需要三个随机数。
(2)握手之后的对话使用"对话密钥"加密(对称加密),服务器的公钥和私钥只用于加密和解密"对话密钥"(非对称加密),无其他作用。
(3)服务器公钥放在服务器的数字证书之中。
为了保障随机性,采用会话密钥(session key),每次会话产生一对对称密钥,密钥的种子通过三次随机数(readom)形成,在握手协商(handshake)中的premaster secret(第三个密钥)。用三个随机数的主要目的是降低伪随机的概率,以提高安全性(被猜测的可能性)。
虽然理论上,只要服务器的公钥足够长(比如2048位),那么Premaster secret可以保证不被破解。但是为了足够安全,考虑把握手阶段的算法从默认的RSA算法,改为 Diffie-Hellman算法(简称DH算法)。
采用DH算法后,Premaster secret不需要传递,双方只要交换各自的参数,就可以算出这个随机数。
3)握手过程中各种消息抓包情况
下面介绍最常见的握手规则,一种不需要验证客户端身份但需要验证服务器身份的握手:
这条消息将客户端的功能和首选项传送给服务器。
- Version: 协议版本(protocol version)指示客户端支持的最佳协议版本
- Random: 一个 32 字节数据,28 字节是随机生成的 (图中的 Random Bytes);剩余的 4 字节包含额外的信息,与客户端时钟有关 (图中使用的是 GMT Unix Time)。在握手时,客户端和服务器都会提供随机数,客户端的暂记作 random_C (用于后续的密钥的生成)。这种随机性对每次握手都是独一无二的,在身份验证中起着举足轻重的作用。它可以防止 重放攻击,并确认初始数据交换的完整性。
- Session ID: 在第一次连接时,会话 ID(session ID)字段是空的,这表示客户端并不希望恢复某个已存在的会话。典型的会话 ID 包含 32 字节随机生成的数据,一般由服务端生成通过 ServerHello 返回给客户端。
- Cipher Suites: 密码套件(cipher suite)块是由客户端支持的所有密码套件组成的列表,该列表是按优先级顺序排列的
- Compression: 客户端可以提交一个或多个支持压缩的方法。默认的压缩方法是 null,代表没有压缩
- Extensions: 扩展(extension)块由任意数量的扩展组成。这些扩展会携带额外数据
b)ServerHello
是将服务器选择的连接参数传回客户端。
图中的 Cipher Suite
是后续密钥协商和身份验证要用的加密套件,此处选择的密钥交换与签名算法是 ECDHE_RSA,对称加密算法是 AES-GCM,后面会讲到这个
还有一点默认情况下 TLS 压缩都是关闭的,因为 CRIME 攻击会利用 TLS 压缩恢复加密认证 cookie,实现会话劫持,而且一般配置 gzip 等内容压缩后再压缩 TLS 分片效益不大又额外占用资源,所以一般都关闭 TLS 压缩
c)Certificate
典型的 Certificate 消息用于携带服务器 X.509 证书链。
服务器必须保证它发送的证书与选择的算法套件一致。比方说,公钥算法与套件中使用的必须匹配。除此以外,一些密钥交换算法依赖嵌入证书的特定数据,而且要求证书必须以客户端支持的算法签名。所有这些都表明服务器需要配置多个证书(每个证书可能会配备不同的证书链)。
d) ServerKeyExchange
携带密钥交换需要的额外数据。ServerKeyExchange 是可选的,消息内容对于不同的协商算法套件会存在差异。部分场景下,比如使用 RSA 算法时,服务器不需要发送此消息。
ServerKeyExchange 仅在服务器证书消息(也就是上述 Certificate 消息)不包含足够的数据以允许客户端交换预主密钥(premaster secret)时才由服务器发送。
比如基于 DH 算法的握手过程中,需要单独发送一条 ServerKeyExchange 消息带上 DH 参数:
表明服务器已经将所有预计的握手消息发送完毕。在此之后,服务器会等待客户端发送消息。
f) verify certificate
客户端验证证书的合法性,如果验证通过才会进行后续通信,否则根据错误情况不同做出提示和操作,合法性验证内容包括如下:
- 证书链的可信性 trusted certificate path;
- 证书是否吊销 revocation,有两类方式 - 离线 CRL 与在线 OCSP,不同的客户端行为会不同;
- 有效期 expiry date,证书是否在有效时间范围;
- 域名 domain,核查证书域名是否与当前的访问域名匹配;
由 PKI 体系
的内容可知,对端发来的证书签名是 CA 私钥加密的,接收到证书后,先读取证书中的相关的明文信息,采用相同的散列函数计算得到信息摘要,然后利用对应 CA 的公钥解密签名数据,对比证书的信息摘要,如果一致,则可以确认证书的合法性;然后去查询证书的吊销情况等
g)ClientKeyExchange
合法性验证通过之后,客户端计算产生随机数字的预主密钥(Pre-master),并用证书公钥加密,发送给服务器并携带客户端为密钥交换提供的所有信息。这个消息受协商的密码套件的影响,内容随着不同的协商密码套件而不同。
此时客户端已经获取全部的计算协商密钥需要的信息: 两个明文随机数 random_C 和 random_S 与自己计算产生的 Pre-master,然后得到协商密钥(用于之后的消息加密)
h)ChangeCipherSpec
通知服务器后续的通信都采用协商的通信密钥和加密算法进行加密通信
i)Finished (Encrypted Handshake Message)
Finished 消息意味着握手已经完成。消息内容将加密,以便双方可以安全地交换验证整个握手完整性所需的数据。
这个消息包含 verify_data 字段,它的值是握手过程中所有消息的散列值。这些消息在连接两端都按照各自所见的顺序排列,并以协商得到的主密钥 (enc_key) 计算散列。这个过程是通过一个伪随机函数(pseudorandom function,PRF)来完成的,这个函数可以生成任意数量的伪随机数据。
两端的计算方法一致,但会使用不同的标签(finished_label):客户端使用 client finished,而服务器则使用 server finished。
m)Server
服务器用私钥解密加密的 Pre-master 数据,基于之前交换的两个明文随机数 random_C 和 random_S,同样计算得到协商密钥
同样计算之前所有收发信息的 hash 值,然后用协商密钥解密客户端发送的 verify_data_C,验证消息正确性;
n)change_cipher_spec
服务端验证通过之后,服务器同样发送 change_cipher_spec 以告知客户端后续的通信都采用协商的密钥与算法进行加密通信(图中多了一步 New Session Ticket,此为会话票证,会在会话恢复中解释);
0)Finished (Encrypted Handshake Message)
服务器也结合所有当前的通信参数信息生成一段数据 (verify_data_S) 并采用协商密钥 session secret (enc_key) 与算法加密并发送到客户端;
p)握手结束
客户端计算所有接收信息的 hash 值,并采用协商密钥解密 verify_data_S,验证服务器发送的数据和密钥,验证通过则握手完成;
q)加密通信
开始使用协商密钥与算法进行加密通信。
1.记录协议
四、session的恢复
握手阶段用来建立连接。如果出于某种原因,对话中断,就需要重新握手。
有两种方法可以恢复原来的session:一种叫做session ID,另一种叫做session ticket。
1)采用session ID 方式
session ID的思想很简单,就是每一次对话都有一个编号(session ID)。如果对话中断,下次重连的时候,只要客户端给出这个编号,且服务器有这个编号的记录,双方就可以重新使用已有的"对话密钥",而不必重新生成一把。
客户端给出session ID,服务器确认该编号存在,双方就不再进行握手阶段剩余的步骤,而直接用已有的对话密钥进行加密通信。
session ID是目前所有浏览器都支持的方法,但是它的缺点在于session ID往往只保留在一台服务器上。所以,如果客户端的请求发到另一台服务器,就无法恢复对话。
2)采用session ticket方式
客户端不再发送session ID,而是发送一个服务器在上一次对话中发送过来的session ticket。这个session ticket是加密的,只有服务器才能解密,其中包括本次对话的主要信息,比如对话密钥和加密方法。当服务器收到session ticket以后,解密后就不必重新生成对话密钥了。
使用这种方式,除了所有的状态都保存在客户端(与 HTTP Cookie 的原理类似)之外,其消息流与服务器会话缓存是一样的。
这种方法有可能使扩展服务器集群更为简单,因为如果不使用这种方式,就需要在服务集群的各个节点之间同步会话。
Session ticket 需要服务器和客户端都支持,属于一个扩展字段,占用服务器资源很少。
<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">