术→技巧, 研发
目录
当我们使用 HTTP 协议时,传输的数据是不安全的,因为所有在客户端和服务端往来的数据都是明文:
HTTPS 的全称是 Hypertext Transfer Protocol Secure,它用来在计算机网络上的两个端系统之间进行安全的交换信息(secure communication),它相当于在 HTTP 的基础上加了一个 Secure 安全的词眼,那么我们可以给出一个 HTTPS 的定义:HTTPS 是一个在计算机世界里专门在两点之间安全的传输文字、图片、音频、视频等超文本数据的约定和规范。HTTPS 是 HTTP 协议的一种扩展,它本身并不保传输的证安全性,那么谁来保证安全性呢?在 HTTPS 中,使用传输层安全性(TLS)或安全套接字层(SSL)对通信协议进行加密。也就是 HTTP + SSL(TLS) = HTTPS。
SSL(Secure Sockets Layer)中文称作“安全套接层”,TLS(Transport Layer Security),中文称作“传输层安全协议”。SSL是由网景公司(Netscape)设计的主要用于Web的安全传输协议,目的是为网络通信提供机密性、认证性及数据完整性保障。SSL最初的几个版本(SSL 1.0、SSL2.0、SSL 3.0)由网景公司设计和维护,从3.1版本开始,SSL协议由因特网工程任务小组(IETF)正式接管,并更名为TLS(Transport Layer Security),发展至今已有TLS 1.0、TLS 1.1、TLS 1.2、TLS 1.3这几个版本。
由于SSL的2个版本都已经退出历史舞台了,所以后面只用TLS这个名字。读者应该明白,一般所说的SSL就是TLS。
SSL 是一个独立的协议,不只有 HTTP 可以使用,其他应用层协议也可以使用,比如 SMTP(电子邮件协议)、Telnet(远程登录协议) 等都可以使用。
TLS的设计目标是构建一个安全传输层(Transport Layer Security),在基于连接的传输层(如tcp)之上提供:
如何确定网站使用的TSL版本?
浏览器中,按F12调出调试窗口,选择安全(Security) TAB即可查看类似如下内容:
构建软件的常用方式是分层,把问题域抽象为多层,每一层的概念定义为一组原语,上一层利用下一层的组件构造实现,并被上一层使用,层层叠叠即成软件。
密码学通信协议也是分层构造得到。大致可以这么分层:
每一层的情况:
第2层中,密码学算法,常见的有下面几类:
设计一个加密通信协议的过程,就是自顶向下,逐步细化,挑选各类组件,拼装成完整协议的过程
从上述分层的角度看,TLS大致是由3个组件拼成的:
这些组件可以再拆分为5类算法,在TLS中,这5类算法组合在一起,称为一个CipherSuite:
TLS协议设计之初就考虑到了这每一类算法的演变,所以没有定死算法,而是设计了一个算法协商过程,来允许加入新的算法,协商出的一个算法组合即一个CipherSuite TLS CipherSuite 在 iana 集中注册,每一个CipherSuite分配有 一个2字节的数字用来标识 ,可以在 iana的注册页面查看
服务器端支持的CipherSuite列表,如果是用的openssl,可以用 openssl ciphers -V | column -t 命令查看,输出如:
TLS(Transport Layer Security Protocol,传输层安全协议)主要目的是提供隐私和数据两个通信应用之间的完整性。该协议由两层组成:TLS 记录协议(TLS Record)和 TLS 握手协议(TLS Handshake)。
这种认证密钥协商 + 对称加密传输的结构,是绝大多数加密通信协议的通用结构。
在用SSL进行通信之前,首先要使用SSL的Handshake协议在通信两端握手,协商数据传输中要用到的相关安全参数(如加密算法、共享密钥、产生密钥所要的材料等),并对对端的身份进行验证。
SSL的建立过程总共有13个包,第一次建立至少需要9个包。
SSL建立第一阶段:客户端首先发送ClientHello消息到服务端,服务端收到ClientHello消息后,再发送ServerHello消息回应客户端。
ClientHello
握手第一步是客户端向服务端发送 Client Hello 消息,这个消息里包含了一个客户端生成的随机数 Random1、客户端支持的加密套件(Support Ciphers)和 SSL Version 等信息。
ClientHello中涉及到的消息具体如下:
这些是客户端问候的一部分,如果已收到客户端问候,接下来就是服务器的确认,服务器将发送服务器问候。
ServerHello
收到客户端问候之后服务器必须发送服务器问候信息,服务器会检查指定诸如TLS版本和算法的客户端问候的条件,如果服务器接受并支持所有条件,它将发送其证书以及其他详细信息,否则,服务器将发送握手失败消息。
如果接受,第二步是服务端向客户端发送 Server Hello 消息,这个消息会从 Client Hello 传过来的 Support Ciphers 里确定一份加密套件,这个套件决定了后续加密和生成摘要时具体使用哪些算法,另外还会生成一份随机数 Random2。注意,至此客户端和服务端都拥有了两个随机数(Random1+ Random2),这两个随机数会在后续生成对称秘钥时用到。
ServerHello中涉及到的具体参数:
这个阶段之后,客户端服务端知道了下列内容:
SSL建立第二阶段:服务器向客户端发送消息。
服务器启动SSL握手第2阶段,是本阶段所有消息的唯一发送方,客户机是所有消息的唯一接收方。该阶段分为4步:
Certificate消息(可选)—第一次建立必须要有证书
一般情况下,除了会话恢复时不需要发送该消息,在SSL握手的全流程中,都需要包含该消息。消息包含一个X.509证书,证书中包含公钥,发给客户端用来验证签名或在密钥交换的时候给消息加密。
这一步是服务端将自己的证书下发给客户端,让客户端验证自己的身份,客户端验证通过后取出证书中的公钥。
Server Key Exchange(可选)
根据之前在ClientHello消息中包含的CipherSuite信息,决定了密钥交换方式(例如RSA或者DH),因此在Server Key Exchange消息中便会包含完成密钥交换所需的一系列参数。
因为这里是DH算法,所以需要发送服务器使用的DH参数。RSA算法不需要这一步。
在Diffie-Hellman中,客户端无法自行计算预主密钥; 双方都有助于计算它,因此客户端需要从服务器获取Diffie-Hellman公钥。
由上图可知,此时密钥交换也由签名保护。
Certificate Request(可选)——可以是单向的身份认证,也可以双向认证
这一步是可选的,如果在对安全性要求高的常见可能用到。服务器用来验证客户端。服务器端发出Certificate Request消息,要求客户端发他自己的证书过来进行验证。该消息中包含服务器端支持的证书类型(RSA、DSA、ECDSA等)和服务器端所信任的所有证书发行机构的CA列表,客户端会用这些信息来筛选证书。
Server Hello Done
该消息表示服务器已经将所有信息发送完毕,接下来等待客户端的消息。
SSL建立第三阶段:客户端收到服务器发送的一系列消息并解析后,将本端相应的消息发送给服务器。
客户机启动SSL握手第3阶段,是本阶段所有消息的唯一发送方,服务器是所有消息的唯一接收方。该阶段分为3步:
Certificate(可选)
如果在第二阶段服务器端要求发送客户端证书,客户端便会在该阶段将自己的证书发送过去。服务器端在之前发送的Certificate Request消息中包含了服务器端所支持的证书类型和CA列表,因此客户端会在自己的证书中选择满足这两个条件的第一个证书发送过去。若客户端没有证书,则发送一个no_certificate警告。
Client Key exchange
根据之前从服务器端收到的随机数,按照不同的密钥交换算法,算出一个pre-master,发送给服务器,服务器端收到pre-master算出main master。而客户端当然也能自己通过pre-master算出main master。如此以来双方就算出了对称密钥。
如果是RSA算法,会生成一个48字节的随机数,然后用server的公钥加密后再放入报文中。如果是DH算法,这是发送的就是客户端的DH参数,之后服务器和客户端根据DH算法,各自计算出相同的pre-master secret.
本消息在给服务器发送的过程中,使用了服务器的公钥加密。服务器用自己的私钥解密后才能得到pre-master key.(向服务器证明自己的确持有客户端证书私钥。)
Certificate verify(可选)
只有在客户端发送了自己证书到服务器端,这个消息才需要发送。其中包含一个签名,对从第一条消息以来的所有握手消息的HMAC值(用master_secret)进行签名。
SSL建立第四阶段:完成握手协议,建立SSL连接。
客户机启动SSL握手第4阶段,使服务器结束。该阶段分为4步,前2个消息来自客户机,后2个消息来自服务器。
建立起一个安全的连接,客户端发送一个Change Cipher Spec消息,并且把协商得到的CipherSuite拷贝到当前连接的状态之中。然后,客户端用新的算法、密钥参数发送一个Finished消息,这条消息可以检查密钥交换和认证过程是否已经成功。其中包括一个校验值,对客户端整个握手过程的消息进行校验。服务器同样发送Change Cipher Spec消息和Finished消息。握手过程完成,客户端和服务器可以交换应用层数据进行通信。
ChangeCipherSpec
编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送(ChangeCipherSpec是一个独立的协议,体现在数据包中就是一个字节的数据,用于告知服务端,客户端已经切换到之前协商好的加密套件(Cipher Suite)的状态,准备使用之前协商好的加密套件加密数据并传输了)。
Clinet Finished
客户端握手结束通知, 表示客户端的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供服务器校验(使用HMAC算法计算收到和发送的所有握手消息的摘要,然后通过RFC5246中定义的一个伪函数 PRF计算出结果,加密后发送。此数据是为了在正式传输应用数据之前对刚刚握手建立起来的加解密通道进行验证。)
Server Finished
服务端握手结束通知。
根据之前的握手信息,如果客户端和服务端都能对Finish信息进行正常加解密且消息正确的被验证,则说明握手通道已经建立成功,接下来,双方可以使用上面产生的Session Secret对数据进行加密传输了。
TLS记录协议主要负责消息的压缩,加密及数据的认证:
消息首先将会被分段,然后压缩,再计算其消息验证码,然后使用对称密码进行加密,加密使用的是CBC模式,CBC模式的初始向量是通过主密码来生成的。得到密文之后会附加类型,版本和长度等其他信息,最终组成最后的报文数据。
record协议做应用数据的对称加密传输,占据一个TLS连接的绝大多数流量。
Record 协议 – 从应用层接受数据,并且做:
SecurityParameters
record层的上述处理,完全依据下面这个SecurityParameters里面的参数进行:
struct { ConnectionEnd entity; PRFAlgorithm prf_algorithm; BulkCipherAlgorithm bulk_cipher_algorithm; CipherType cipher_type; uint8 enc_key_length; uint8 block_length; uint8 fixed_iv_length; uint8 record_iv_length; MACAlgorithm mac_algorithm; uint8 mac_length; uint8 mac_key_length; CompressionMethod compression_algorithm; opaque master_secret[48]; opaque client_random[32]; opaque server_random[32]; } SecurityParameters;
record 层使用上面的SecurityParameters生成下面的6个参数(不是所有的CipherSuite都需要全部6个,如果不需要,那就是空):
client write MAC key server write MAC key client write encryption key server write encryption key client write IV server write IV
当handshake完成,上述6个参数生成完成之后,就可以建立连接状态,连接状态除了上面的SecurityParameters,还有下面几个参数,并且随着数据的发送/接收,更新下面的参数:
record层分段
如上图所示,对要发送的数据流,首先分段,分段成如下格式:
struct { uint8 major; uint8 minor; } ProtocolVersion; enum { change_cipher_spec(20), alert(21), handshake(22), application_data(23), (255) } ContentType; struct { ContentType type; ProtocolVersion version; uint16 length; opaque fragment[TLSPlaintext.length]; } TLSPlaintext;
record压缩:TLS协议定义了可选的压缩,但是,由于压缩导致了 2012 年被爆出 CRIME攻击,BREACH攻击,所以在实际部署中,一定要禁用压缩。
record层的密码学保护
经过处理后的包格式定义如下:
struct { ContentType type; ProtocolVersion version; uint16 length; select (SecurityParameters.cipher_type) { case stream: GenericStreamCipher; case block: GenericBlockCipher; case aead: GenericAEADCipher; } fragment; } TLSCiphertext;
TLS协议设计目标中的保密(encryption)、完整性(authentication)和防重放就在这里实现。 实现方式有3类:
Block Cipher+HMAC 和 Stream Cipher + HMAC 的各类算法目前(2015年)都已经爆出各种漏洞,目前最可靠的是 3.Authenticated-Encryption 类的算法,主要就是aes-gcm,下一代的TLS v1.3只保留了Authenticated-Encryption,把1和2直接禁止了。
GCM模式是AEAD的,所以不需要MAC算法。 GCM模式是AEAD的一种,AEAD 的 作用类似于 Encrypt-then-HMAC ,例如 Sha256 + Salt + AES + IV
此处需要介绍一个陷阱。 在密码学历史上,出现过3种加密和认证的组合方式:
在TLS协议初定的那个年代,人们还没意识到这3种组合方式的安全性有什么差别,所以TLS协议规定使用 2.MAC-then-Encrypt,即先计算MAC,然后把 “明文+MAC” 再加密(块加密或者流加密)的方式,做流加密+MAC,和块加密+MAC。 但是,悲剧的是,近些年,人们发现 MAC-then-Encrypt 这种结构导致了很容易构造padding oracle 相关的攻击,例如这在TLS中,间接形成被攻击者利用,这间接导致了 BEAST 攻击 , Lucky 13攻击 (CVE-2013-0169), 和 POODLE 攻击 (CVE-2014-3566).
目前因此,学术界已经一致同意: Encrypt-then-MAC 才是最安全的!鉴于这个陷阱如此险恶,学术界有人就提出了,干脆把Encrypt和MAC直接集成为一个算法,在算法内部解决好安全问题,不再让码农选择,避免众码农再被这个陷阱坑害,这就是AEAD(Authenticated-Encryption With Additional data)类的算法,GCM模式就是AEAD最重要的一种。
record层的密码学保护–MAC
TLS record 层 MAC的计算方法:
MAC(MAC_write_key, seq_num + TLSCompressed.type + TLSCompressed.version + TLSCompressed.length + TLSCompressed.fragment);
其中的seq_num是当前record的 sequence number,每条record都会++, 可以看到把 seq_num,以及record header里面的几个字段也算进来了,这样解决了防重放问题,并且保证record的任何字段都不能被篡改。
算完MAC,格式如下:
stream-ciphered struct { opaque content[TLSCompressed.length]; opaque MAC[SecurityParameters.mac_length]; } GenericStreamCipher;
然后根据SecurityParameters.cipher_type,选择对应的对称加密算法进行加密。
record层的密码学保护–stream cipher
stream cipher: 算stream cipher,stream cipher的状态在连续的record之间会复用。 stream cipher的主力是RC4,但是目前RC4已经爆出多个漏洞,所以实际中基本不使用流加密算法。
record层的密码学保护– CBC block cipher
CBC模式块加密 TLS目前靠得住的的块加密cipher也不多,基本就是AES(最靠谱,最主流),Camellia,SEED,(3DES,IDEA之类已经显得老旧,DES请禁用),加密完的格式如下:
struct { opaque IV[SecurityParameters.record_iv_length]; block-ciphered struct { opaque content[TLSCompressed.length]; opaque MAC[SecurityParameters.mac_length]; uint8 padding[GenericBlockCipher.padding_length]; uint8 padding_length; }; } GenericBlockCipher;
这个值得说道说道,因为我们码农平常在业界还能看到很多用AES-CBC的地方,其中的几个参数:
record层的密码学保护– AEAD cipher
AEAD 到了我们重点关注的AEAD,AEAD是新兴的主流加密模式,是目前最重要的模式,其中主流的AEAD模式是 aes-gcm-128/aes-gcm-256/chacha20-poly1305
AEAD加密完的格式是:
struct { opaque nonce_explicit[SecurityParameters.record_iv_length]; aead-ciphered struct { opaque content[TLSCompressed.length]; }; } GenericAEADCipher;
AEAD ciphers的输入是: key,nonce, 明文,和 “additional data”. key是 client_write_key 或者 the server_write_key. 不需要使用 MAC key.
每一个AEAD算法都要指定不同的nonce构造算法,并指定 GenericAEADCipher.nonce_explicit 的长度. 在TLS 1.2中,规定很多情况下,可以按照rfc5116 section 3.2.1的技术来做。其中record_iv_length是nonce的显式部分的长度,nonce的隐式部分从key_block作为 client_write_iv和 and server_write_iv得出,并且把显式部分放在 GenericAEAEDCipher.nonce_explicit 里.
在TLS 1.3 draft中,做了更改:
AEAD输入的明文就是 TLSCompressed.fragment (记得上面的介绍吗?AEAD是MAC和encrypt的集成,所以输入数据不需要在算MAC了).
AEAD输入的additional_data 是:
additional_data = seq_num + TLSCompressed.type + TLSCompressed.version + TLSCompressed.length;
“+” 表示字符串拼接。 可以看到,此处类似上面的MAC计算,算入了seq_num来防重放,type,version,length等字段防止这些元数据被篡改。
AEADEncrypted = AEAD-Encrypt(write_key, nonce, plaintext, additional_data)
解密+验证完整性:
TLSCompressed.fragment = AEAD-Decrypt(write_key, nonce, AEADEncrypted, additional_data)
如果解密/验证完整性失败,就回复一条 fatal bad_record_mac alert 消息.
aes-gcm的iv长度,nonce长度,nonce构成等,后续再深入探讨。
record层的密码学保护– Key扩展
TLS握手生成的master_secret只有48字节,2组encryption key, MAC key, IV加起来,长度一般都超过48,(例如 AES_256_CBC_SHA256 需要 128字节),所以,TLS里面用1个函数,来把48字节延长到需要的长度,称为PRF:
key_block = PRF(SecurityParameters.master_secret, "key expansion", SecurityParameters.server_random + SecurityParameters.client_random);
然后,key_block像下面这样被分割:
client_write_MAC_key[SecurityParameters.mac_key_length] server_write_MAC_key[SecurityParameters.mac_key_length] client_write_key[SecurityParameters.enc_key_length] server_write_key[SecurityParameters.enc_key_length] client_write_IV[SecurityParameters.fixed_iv_length] server_write_IV[SecurityParameters.fixed_iv_length]
TLS使用HMAC结构,和在CipherSuite中指定的hash函数(安全等级起码是SHA256的水平) 来构造PRF,
首先定义P_hash,把(secret,seed)扩展成无限长的字节流:
P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + HMAC_hash(secret, A(2) + seed) + HMAC_hash(secret, A(3) + seed) + ...
其中”+“表示字符串拼接。 A() 定义为:
A(0) = seed A(i) = HMAC_hash(secret, A(i-1))
TLS的 PRF 就是把 P_hash 应用在secret上:
PRF(secret, label, seed) = P_<hash>(secret, label + seed)
其中 label 是一个协议规定的,固定的 ASCII string.
要注意的是,TLS 1.3里面已经废弃了这种方式,改为使用更靠谱的 HKDF,HKDF 也是 html5的WebCryptoAPI的标准算法之一。
数字证书,又称互联网上的”身份证”,用于唯一标识一个组织或一个服务器的,这就好比我们日常生活中使用的”居民身份证”,用于唯一标识一个人。
网站的证书也是同样的道理。一般来说数字证书从受信的权威证书授权机构 (Certification Authority,证书授权机构)买来的。一般浏览器在出厂时就内置了诸多知名CA。
实际应用中,HTTPS并非直接传输公钥信息,而是使用携带公钥信息的数字证书来保证公钥的安全性和完整性。用来保障HTTPS服务端发送给客户端的公钥信息不被篡改。
数字证书和 CA 机构
一个数字证书通常包含了:
数字证书的作用,是用来认证公钥持有者的身份,以防止第三方进行冒充。说简单些,证书就是用来告诉客户端,该服务端是否是合法的,因为只有证书合法,才代表服务端身份是可信的。
我们用证书来认证公钥持有者的身份(服务端的身份),那证书又是怎么来的?又该怎么认证证书呢?为了让服务端的公钥被大家信任,服务端的证书都是由 CA (Certificate Authority证书认证机构)签名的,CA 就是网络世界里的公安局、公证中心,具有极高的可信度,所以由它来给各个公钥签名,信任的一方签发的证书,那必然证书也是被信任的。之所以要签名,是因为签名的作用可以避免中间人在获取证书时对证书内容的篡改。
数字证书签发和验证流程
如下图所示,为数字证书签发和验证流程:
证书链
但事实上,证书的验证过程中还存在一个证书信任链的问题,因为我们向 CA 申请的证书一般不是根证书签发的,而是由中间证书签发的,比如百度的证书,从下图你可以看到,证书的层级有三级:
对于这种三级层级关系的证书的验证过程如下:
在这些步骤中,最开始客户端只信任根证书 GlobalSign Root CA 证书的,然后 “GlobalSign Root CA” 证书信任 “GlobalSign Organization Validation CA – SHA256 – G2” 证书,而 “GlobalSign Organization Validation CA – SHA256 – G2” 证书又信任baidu.com证书,于是客户端也信任baidu.com证书。
总括来说,由于用户信任 GlobalSign,所以由 GlobalSign 所担保的baidu.com可以被信任,另外由于用户信任操作系统或浏览器的软件商,所以由软件商预载了根证书的 GlobalSign 都可被信任。
操作系统里一般都会内置一些根证书:
这样的一层层地验证就构成了一条信任链路,整个证书信任链验证流程如下图所示:
最后一个问题,为什么需要证书链这么麻烦的流程?Root CA 为什么不直接颁发证书,而是要搞那么多中间层级呢?
这是为了确保根证书的绝对安全性,将根证书隔离地越严格越好,不然根证书如果失守了,那么整个信任链都会有问题。
TLS 1.3 是时隔九年对 TLS 1.2 等之前版本的新升级,也是迄今为止改动最大的一次。针对目前已知的安全威胁,IETF(Internet Engineering Task Force,互联网工程任务组) 正在制定 TLS 1.3 的新标准,使其有望成为有史以来最安全,但也最复杂的 TLS 协议。
TLS 1.3 与之前的协议有较大差异,主要在于:
对比旧协议中的不足,TLS 1.3 确实可以称得上是向前迈了一大步。既避免之前版本出现的缺陷,也减少了 TLS 握手的时间。
总结一下,TLS 1.3 与以前的版本相比具有如下两个大的优势,分别是:
为了对比 TLS 1.3 在 TLS 握手阶段的变化, 这里将 TLS 1.2 和 TLS 1.3 在 TLS 握手阶段进行对比。
从上图可以看出,使用 TLS 1.2 需要两次往返( 2-RTT )才能完成握手,然后才能发送请求。
TLS 1.3 的握手不再支持静态的 RSA 密钥交换,这意味着必须使用带有前向安全的 Diffie-Hellman 进行全面握手。从上图可以看出,使用 TLS 1.3 协议只需要一次往返( 1-RTT )就可以完成握手。
上图的右边部分就是 TLS 1.3 的握手过程,可以发现 TLS 1.3 把 Hello 和公钥交换这两个消息合并成了一个消息,于是这样就减少到只需 1 RTT 就能完成 TLS 握手。
怎么合并的呢?具体的做法是,客户端在 Client Hello 消息里带上了支持的椭圆曲线,以及这些椭圆曲线对应的公钥。
服务端收到后,选定一个椭圆曲线等参数,然后返回消息时,带上服务端这边的公钥。经过这 1 个 RTT,双方手上已经有生成会话密钥的材料了,于是客户端计算出会话密钥,就可以进行应用数据的加密传输了。
相比 TLS 1.2,TLS 1.3 的握手时间减半。这意味着访问一个移动端网站,使用 TLS 1.3 协议,可能会减少将近 100ms 的时间。
TLS 的发展有 20 多年的历史,在之前的版本中,TLS 1.2 是高度可配置的,为了更好的兼容旧版本的浏览器,这意味着那些易受攻击的站点始终在运行着不安全的加密算法,这让互联网黑客有可乘之机。
TLS 1.3 在之前版本的基础上删除了那些不安全的加密算法,这些加密算法包括:
TLS1.3 对密码套件进行“减肥”了,对于密钥交换算法,废除了不支持前向安全性的 RSA 和 DH 算法,只支持 ECDHE 算法。对于对称加密和签名算法,只支持目前最安全的几个密码套件,比如 openssl 中仅支持下面 5 种密码套件:
之所以 TLS1.3 仅支持这么少的密码套件,是因为 TLS1.2 由于支持各种古老且不安全的密码套件,中间人可以利用降级攻击,伪造客户端的 Client Hello 消息,替换客户端支持的密码套件为一些不安全的密码套件,使得服务器被迫使用这个密码套件进行 HTTPS 连接,从而破解密文。
总之,TLS 1.3 相比老版本的 TLS 协议将会更加安全,这也代表着互联网安全的一大进步。
TLS 1.3 的改动 值得关注的重大改进有:
TLS 1.3 的各项改进,主要就是针对移动互联网场景的。TLS 1.3 去掉了 ChangeCipherSpec ,这样record之上有3个协议:handshake,alert,application data
record层的密码学保护的改动
由于只保留了aead,所以不需要MAC key了。aead的具体参数用法也有调整,前文有。KDF 换成了标准的HKDF,有2种 tls_kdf_sha256, tls_kdf_sha384
handshake协议的改动
鉴于session ticket如此之好用,简直人见人爱,所以 TLS 1.3 直接把session ticket内置了,并改名叫 PSK。要注意的是,此 PSK 和 tls 1.2中一个很生僻的psk(见 rfc4279 )并不是一回事。
综合考虑了 session resuming ,session ticket后, TLS 1.3 提出了3种handshake模式:
1-RTT 握手
首先,TLS 1.2 的握手有2个rtt,第一个rtt是 ClientHello/ServerHello,第二个rtt是ClientKeyExchange/ServerKeyExchange, 之所以KeyExchange要放在第二个rtt,是由于tls1.2要支持多种密钥交换算法,和各种不同参数(比如 DH还是ECDH还是RSA,ECDHE用什么曲线,DH用什么群生成元,用什么模数,等等),这些算法和参数都依赖第一个rtt去协商出来, TLS1.3大刀阔斧地砍掉了各种自定义DH群,砍掉了ECDH的自定义曲线,砍掉了RSA协商,密钥协商的算法只剩下不多几个,而且其实大家实际应用中基本都用 ECDH P-256,也没啥人用别的,所以干脆让客户端缓存服务器上一次用的是啥协商算法,把 KeyExchange直接和入第一个rtt,客户端在第一个rtt里直接就用缓存的这个算法发KeyExchange的公钥,如果服务器发现客户端发上来的算法不对,那么再告诉正确的,让客户端重试好了。 这样,就引入了 HelloRetryRequest 这个消息。
这样,基本没有副作用,就可以降到 1-RTT。 这是TLS 1.3 的完整握手。
显然,如果一个协议只有一种密钥协商算法,比如定死为 ECDH P-256,那一定可以做到 1-RTT
有副作用的 0-RTT握手
0-RTT应该是受Google的QUIC协议的启发, 如果服务器把自己的 ECDH 公钥长期缓存在客户端,那么客户端就可以用缓存里的ECDHE公钥,构造一个电子信封,在第一个RTT里,直接就发送应用层数据了。 这个长期缓存在客户端的ECDH公钥,称为 半静态 ECDH 公钥( semi-static (EC)DH share ) ECDH公钥通过 ServerConfiguration 消息发送给客户端。
这个0-rtt优化是有副作用的:
服务器在 ServerConfiguration 消息里把半静态 ECDH 公钥发送给客户端。 ServerConfiguration 值得关注一下:
struct { opaque configuration_id<1..2^16-1>; uint32 expiration_date; NamedGroup group; opaque server_key<1..2^16-1>; EarlyDataType early_data_type; ConfigurationExtension extensions<0..2^16-1>; } ServerConfiguration;
其中的 expiration_date 是本 ServerConfiguration 最后的有效期限。 这个值绝对不允许大于7天。 客户端绝对不允许存储 ServerConfiguration 大于7天,不管服务器怎么填这个值。
0-RTT 中的应用数据,放在 EarlyDataIndication 中发送,TLS 1.3 还特意给 EarlyDataIndication 定义了一种 ContentType : early_handshake (共四种 alert(21), handshake(22), application_data(23), early_handshake(25) )
Resumption 和 PSK
TLS 1.3 里面,把session resumption/session ticket 恢复出来的key,和 psk (rfc4279), 统一在一个 handshake PSK 模式下处理。
PSK CipherSuite可以 把PSK和ECDHE结合起来用,这样是有前向安全性的。 也可以仅仅使用PSK,这样就没有前向安全性。
Key Schedule 过程的改动
TLS 1.3 中,综合考虑的 session ticket的各种情况后,提出了 ES,SS 两个概念,统一处理密钥协商的各种情况。 在各种handshake模式下,ES和SS的取值来源不同。
在各种 handshake 模式下:
Key Exchange | Static Secret (SS) | Ephemeral Secret (ES) |
(EC)DHE (完整握手) | Client ephemeral w/ server ephemeral | Client ephemeral w/ server ephemeral |
(EC)DHE (w/ 0-RTT) | Client ephemeral w/ server static | Client ephemeral w/ server ephemeral |
PSK | Pre-Shared Key | Pre-shared key |
PSK + (EC)DHE | Pre-Shared Key | Client ephemeral w/ server ephemeral |
如上表:
可以看到,相比 TLS 1.2 的 session ticket,TLS 1.3 中 的 PSK + ECDHE,是结合了 ES 的,这样就有了前向安全性,相对更安全。
和 TLS 1.2 不同的是,TLS 1.3的 master_secret 是使用 ES和SS 两个得出的。
HKDF-Expand-Label(Secret, Label, HashValue, Length) = HKDF-Expand(Secret, Label + 'HKDF-Expand-Label(Secret, Label, HashValue, Length) = HKDF-Expand(Secret, Label + '\0' + HashValue, Length) 1. xSS = HKDF(0, SS, "extractedSS", L) 2. xES = HKDF(0, ES, "extractedES", L) 3. master_secret = HKDF(xSS, xES, "master secret", L) 4. finished_secret = HKDF-Expand-Label(xSS, "finished secret", handshake_hash, L)' + HashValue, Length) 1. xSS = HKDF(0, SS, "extractedSS", L) 2. xES = HKDF(0, ES, "extractedES", L) 3. master_secret = HKDF(xSS, xES, "master secret", L) 4. finished_secret = HKDF-Expand-Label(xSS, "finished secret", handshake_hash, L)
Traffic Key Calculation
加密流量用的key,在 TLS 1.3 里面称为 Traffic Key,由于多引入了一种ContentType,在不同的ContentType下,Traffic Key 并不相同。 如下表:
Record Type | Secret | Label | Handshake Hash |
Early data | xSS | “early data key expansion” | ClientHello |
Handshake | xES | “handshake key expansion” | ClientHello… ServerKeyShare |
Application | master secret | “application data key expansion” | All handshake messages but Finished |
要关注的是, Early Data 的 Traffic Key 是用 xSS 算出来的。也就是说,是用 Pre-Shared Key决定的。因此是没有前向安全性的。
在一个TLS 连接中,究竟是用哪种 handshake 模式,是由 CipherSuite 协商决定的。
参考链接:
深圳SEO优化公司南联百姓网标王推广多少钱邵阳seo推荐平顶山建站公司襄樊网站建设公司宜昌网站seo优化报价襄阳SEO按天收费推荐惠州seo网站推广公司秦皇岛建站文山企业网站设计多少钱广州设计网站推荐海西企业网站改版报价南昌优秀网站设计邵阳模板制作报价广州关键词按天扣费哪家好襄阳网站搭建价格邵阳seo网站推广多少钱南联阿里店铺运营推荐云浮至尊标王推荐济源网站推广公司莱芜百度标王价格曲靖网站优化软件多少钱信阳高端网站设计公司吕梁网站设计模板公司株洲seo公司永湖SEO按天计费报价南平阿里店铺托管公司黄石网站优化推广报价塔城企业网站改版价格韶关企业网站改版岳阳关键词按天扣费哪家好歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运