上海交通大学密码与计算机安全实验室(LoCCS)软件安全小组(GoSSIP)版权所有,转载请与作者取得联系!
在今年的GoSSIP软件安全小组面向全国优秀的本科同学开放的暑期实习中,来自上海交通大学的胡嘉尚同学深入研究了SSL/TLS协议,并撰写了这篇TLS 1.3的科普文章,希望能够帮助国内的安全研究人员和企业开发人员更加深入地理解和部署TLS 1.3协议。
======================================================================
时隔九年之后的新升级,TLS 1.3备受关注。针对目前已知的漏洞和安全威胁,IETF小组对TLS协议进行了一次大的更新,从2014年4月,第0份TLS 1.3草案公开,到2017年7月第21份草案发布,TLS 1.3的编写工作已经进入尾声,跨时3年的编写,让该协议成为有史以来最安全、也是最复杂的TLS协议。
正式的RFC虽然尚未发布,TLS 1.3已经开始被国内外一些网站使用,Chrome、Firefox、OpenSSL、Nginx等均提供了相应支持,TLS 1.3已经悄然进入我们的生活。借着这篇文章先来了解一下吧~
20年——从开始到现在
对于安全加密通信,传输层安全协议(SSL/TLS)的重要性不言而喻。
起初,网景公司针对传输层协议(TCP/UDP)并没有对传输的数据进行加密保护的缺陷,为自家浏览器设计了SSL协议,于1995年公开协议的2.0版本。1999年,IETF小组基于SSL 3.0设计了与SSL协议独立的TLS 1.0协议,正式成为互联网传输层加密的标准。此后,TLS协议于2006年、2008年再次经历更新,分别命名为TLS 1.1和TLS 1.2。
如今的TLS协议不仅被用于传输层通讯,更作为一个标准的加密保护协议被广泛应用于 FTP, 电子邮件和VPN等领域,时刻保护着我们网络通信的安全。
当前的TLS协议存在问题
老版本的SSL协议被公认在完整性校验、密钥协商过程中有重大缺陷,因此,2011年与2015年IETF小组相继声明禁止使用SSL 2.0、3.0。
TLS协议针对此前披露的漏洞做了相应的处理,但是因为其复杂性,还没有一个版本能真正保证绝对的安全。
目前最新版本的TLS 1.2发布距今已有九年时间,在此期间,许多SSL/TLS协议的新漏洞被发现,比如针对其压缩机制的CRIME漏洞,针对CBC块加密模式的BEAST漏洞(主要是针对SSL 3.0和TLS 1.0),早已不再是当初设计者认为的那么安全,人们迫切需要新的协议将其代替。
TLS1.3与之前的协议有较大差异
SSL/TLS协议为网络通信提供了以下两种功能:
上述功能在以往的TLS协议中是这样实现的:
因为RSA密钥交换过程安全性完全依赖于服务器私钥的安全性,TLS 1.3彻底废弃了RSA密钥交换算法。
因为先计算MAC再加密的方法存在相当的安全缺陷,TLS 1.3废弃了使用MAC的块加密和流加密机制,仅采用AEAD类对称加密算法作为唯一的加密选项。
此外,TLS 1.3引入了一种新的密钥协商机制——PSK。
其他新协议大的改变还包括:
- 支持0-RTT数据传输
- 废弃了3DES、RC4、AES-CBC等加密组件。废弃了SHA1、MD5等哈希算法。
- 不再允许对加密报文进行压缩、不再允许双方发起重协商,密钥的改变不再需要发送change_cipher_spec报文给对方。
- 握手阶段的报文可见明文大大减少。
对比以往的协议RFC中不足一页的Major Differences from Previous。TLS 1.3确实可以称得上是向前的一大步。
TLS 1.3包括3个子协议——alert、handshake、record
handshake协议负责协商使用的TLS版本、加密算法、哈希算法、密钥材料和其他与通信过程有关的信息,对服务器进行身份认证,对客户端进行可选的身份认证,最后对整个握手阶段信息进行完整性校验以防范中间人攻击,是整个TLS协议的核心。
record协议负责对接收到的报文进行加密解密,将其分片为合适的长度后转发给其他协议层。
alert协议负责处理消息传输与握手阶段中的异常情况。
此前的协议中还定义了一个子协议change_cipher_spec来决定何时对传递的数据进行加密,TLS 1.3取消了这一机制,密钥的使用和改变随着服务器和客户端状态的改变自然进行。
PSK(pre_shared_key)——新的密钥交换暨身份认证机制
ps: 0-RTT的实现有一定的安全缺陷,自身没有抗重放攻击的机制
在TLS 1.3草案中提出了几个对性能消耗比较大的可能的解决方法,感兴趣的话可以找来读一读。
HKDF(HMAC_based_key_derivation_function)——新的密钥导出函数
AEAD(Authenticated_Encrypted_with_associated_data)——唯一保留的加密方式
TLS 1.3完整握手工作流
TLS 1.3握手执行步骤
TLS 1.3握手按照严格的顺序发送不同的报文,各个报文包含标识自己种类的数据以及其他与握手协商有关的扩展数据(extension)。任何时候收到不按顺序发出的报文种类,服务器会报错,并转交给alert协议层处理。
一个正常情况下的TLS 1.3握手应该按照以下顺序组织报文:
所有握手阶段的报文都是由record协议层加解密、分片、填充、转发的。
在这个过程中,如果发生了任何错误(如:服务端证书验证失败、完整性校验错误),则会发送一个alert报文,转交给alert协议层进行错误处理。
其他没有提及到的报文种类
除了正常的情况之外,还有其他一些报文可能出现在握手过程中。
TLS 1.3定义了12种握手报文
使用Hello传递的信息进行密钥协商——选择加密组件
经过两个Hello报文后,双方就明确了计算密钥的初始材料和最终使用的加密算法。加密算法是通过协商加密组件获知的。
因为只使用AEAD加密机制,且彻底禁止了所有不安全的加密算法,TLS 1.3目前支持的加密组件只有以下五种:
以TLS_AES_128_CCM_SHA256为例,TLS表明该加密组件用于TLS协议,AES表明使用AES对称加密算法,128表示密钥长度为128位,CCM表明分组加密模式,SHA256是HKDF过程使用的哈希算法。
协商加密组件时,双方只需要传递相应的value传递即可。由客户端传递一个所有自己支持的加密组件的列表,由服务器将最终选定的加密组件值返还给对方完成协商。
TLS 1.2中定义了多达37种的加密组件,大量使用了MD5、RC4、3DES等被证明不安全或者效率低下的加密算法。
TLS 1.3仅支持速度快安全性强的加密标准算法AES,以及08年才提出的对性能消耗极低的CHACHA20。使用CHACHA20进行AEAD运算时,CHACHA20本身不能提供完整性校验的功能,因此使用POLY1305——一种同样不耗费性能的MAC算法来提供完整性校验的功能。这些算法,包括协议中随着算法使用的分组加密模式CCM和GCM,目前都是理论上安全的算法,不容易被攻击者破解。
如何得出最终密钥——PSK密钥协商机制
协商出来加密算法,下一步则是协商出加密密钥,TLS 1.3支持DH、PSK两种密钥协商机制,也支持同时使用两者进行密钥协商。
如果双方持有未过有效期的PSK键值对,则可以使用PSK进行密钥协商。双方传递一个结构体PSK_entry。该结构体包括的内容有:PSK对应名字(PSK_name)、用该PSK对之前的握手报文进行的HMAC计算结果( PSK_identity)。
具体的实施过程如下:
如果使用了PSK,则客户端可以向服务端发送early_data,客户端会选择发送的PSK_entry数组中的第一个PSK计算early_trffic_key,因此,服务端也必须选择第一个PSK,如果服务端拒绝接受early data,则返回其他的PSK_entry,客户端丢弃已发送的ED报文。
使用PSK密钥协商,已经对双方的身份做了一定的认证,不得再使用公钥证书的认证方式,即CV/CR/CT报文都不会再发送。
如何得出最终密钥——DHE密钥协商机制
使用DHE扩展首先需要选定DH参数,对于有限域DH来说是g和p的值,对于椭圆域DH是椭圆曲线和基点的值,同选定加密组件一样,TLS 1.3定义了几组gp值,双方只需要协商想要使用的pg对即可。
在TLS 1.2中,g和p的值由双方自己生成。TLS 1.3则定义了几组g、p对,双方只需要选择想要使用的DH组即可。使用确定的参数值保证了DH密钥协商过程具有足够的安全性。
具体实施过程与PSK的实施过程类似,由客户端生成一个列表包含所有自己支持的DH组,为每个组生成一个DH密钥交换的参数,将其组名和参数值封装在key_share扩展中,服务端选定DH组后,返回一个封装好的key_share,双方根据交换的公钥参数和自己持有的私钥参数计算出DH最终密钥。
理论上,客户端应该将所有与密钥协商有关的扩展(pre_shared_key、shared_key)都发送给服务端,服务端选定哪一种,再将对应选定的扩展返还给客户端,如果服务端同时使用两种密钥协商,则返还所有扩展,如果客户端没有提供足够的密钥扩展,服务端发送HRR报文要求客户端重新发送CH。
使用密钥协商结果计算实际使用密钥
TLS 1.3最大的特点就是对于不同的报文使用多种不同的密钥。
在TLS 1.2中只使用了两种密钥,一个用于完整性校验,一个用于报文加密,同一连接不同方向使用的加密密钥不一样。
TLS 1.3因为使用AEAD机制,不再需要使用MAC_key来进行完整性校验,同时由于其他各种用途的加密需要,TLS 1.3的实施过程还可能计算或者使用以下几种key:
- handshake_key
- early_traffic_key
- resumption_key
- exporter_key(导出密钥,用于用户自定义的其他用途)
这些密钥都是由之前协商的密钥材料计算而出,区别在于HKDF的计算次数不同,HKDF计算使用的哈希值不同。以会话密钥application_key为例,以整个握手阶段的报文作为输入,计算四次HKDF导出最终使用的密钥。
同时,当加密的报文达到一定长度后,双方发送KU报文重新计算application_key。
post-authentication机制
ost-authentication机制方便用户确认是否向服务端提供自己的身份信息。
TLS 1.3支持服务端在握手结束之后再对客户端发起身份的校验。
CH报文中有一个扩展字段post_handshake_anth。如果客户端发送了此字段,则允许服务端在握手阶段结束后再发起Certificate Request,客户端会在收到CR之后再发送CT、 CV报文给服务端进行身份认证。
p
TLS发送的报文由record层处理:
关于随机数的获取:
双方维护一个64位的sequence number,连接建立和每一次密钥变更时,sequence number置0,此后随传递的字节数增加
加密时将sequence扩充到write_iv(也是由write_key导出的一个初始向量)的长度,再与write_iv做异或计算得到nonce
最后将数据传递出去时,record层会在密文头部附加一小段明文信息来标识解密后明文长度等信息。
对方的record层收到该消息后,通过逆过程解密密文后转发给上层协议。
3. TLS 1.3允许TLS对消息报文填充来阻止攻击者获知传送消息的长度等信息。
填充时在末尾附上八个字节整数倍的全为0的二进制数据,对方收到该消息后,解密后从末尾 开始去掉0,当搜索到第一个不全为0的八字节数据,则结束。
alert层负责处理TLS连接过程中的各种异常情况,对每种情况发送一个alert报文,报文中附加一些错误处理需要的必要信息,TLS 1.3中定义了30种alert报文。
举例来讲:alert层的close_notify报文标志发送方打算关闭TLS连接,不再使用该加密信道传递任何信息,bad_record_mac可能表示AEAD解密时完整性校验失败。
TLS 1.3使用了复杂的密钥导出过程,增强了最终使用的密钥的安全性。同时简化了所使用的加密算法,废弃了RC4、3DES、MD5、SHA1、AES-CBC等加密算法,删除了压缩、重协商等具有漏洞的机制,大大精简了协议。
因此,TLS 1.3如果能够得到普及,网络数据的传递将会变得更加安全、隐秘,TLS 1.3的推广需要每一位开发者、运营者的认可和支持。
目前TLS 1.3虽然还在草案阶段,但是其基本原理和思想已经应用在了实际生活中,chrome等浏览器都已准备好了对其的支持,期待TLS 1.3正式成为一个协议规范的那一天。
以下读物可以帮助读者对TLS 1.3有一些更深的理解
1. 写的很好的一篇博文,介绍了TLS协议的历史和核心思想
2. 微信基于TLS 1.3较早期草案设计的mmtls协议概述
3. cloud flare公司对TLS 1.3的介绍博文合集(英文)
4. TLS 1.3草案合集,可以从这里检索到最新版的TLS1.3草案(英文)
5. 16年NDSS会议研究TLS 1.3的论文合集(英文)