TLS 1.0-1.3版本握手流程具体细节如何解析?
- 内容介绍
- 文章标签
- 相关推荐
本文共计4720个文字,预计阅读时间需要19分钟。
概述:TLS(传输层安全)全称为Transport Layer Security(传输层安全),其前身是SSL(Secure Sockets Layer,安全套接层)。TLS的作用是为上层应用协议提供安全的通信,例如常见的HTTP+TLS。
概述TLS 全称为 Transport Layer Security(传输层安全),其前身是 SSL,全称为 Secure Sockets Layer(安全套接字层),它的作用是为上层的应用协议提供安全的通信,比如众所周知的 HTTP + TLS = HTTPS。
SSL 2.0 是该协议的第一个公开发布的版本,由于其存在的安全问题很快被升级到了 SSL 3.0,并且在 1999 年,IETF 小组将该协议标准化,因此 TLS 1.0 诞生了。
本文将介绍 TLS 1.0 到 TLS 1.3 的握手流程,你只需要一点点的 SSL\TLS 或 HTTPS 的前置知识与了解过简单的密码学上的概念,如对称加密、非对称加密、哈希算法等名词。
从此开始
我们先从一个最简化的 TLS 握手流程开始:
- Client 向 Server 请求建立连接。
- Server 将自己的证书发送给 Client。
- Client 验证证书,然后使用证书中的公钥加密接下来要用来通信的密钥,将加密结果发送给 Server。
- Server 收到后进行响应,且将用该密钥来对需要发送或接收的上层数据进行加解密。
- 自此 TLS 握手完成,接下来开始使用密钥进行通信。
以下为简单的流程图:
在上面中,我们使用的是 RSA 算法来进行密钥的交换。
RSA 应该是最多人了解的非对称加密算法,其在这里用于密钥协商(交换)和证书验证,这分别用到了它的两个特性:
- 公钥加密,对应私钥才能解密
- 私钥加密,对应公钥可以解密
证书机制用到了第二个机制。
证书实际上就是一段符合指定标准的文本,其大致上记录了:
- 证书所属的域名
- 域名所有者的公钥
- 有效日期
- 使用的加密算法
CA(证书颁发者)会对这些内容进行使用哈希函数导出一个值(简单来讲就是用 md5 类似的算法计算一下),该值将被 CA 的私钥进行加密(这样的操作也叫做"签名"),同时签名也将被记录到证书上。
而"证书验证"这个行为,当然指的就是使用证书颁发者公布的公钥,来对被加密的值进行解密,然后对证书使用同样的哈希函数进行计算,并使用计算出的值与解密出的值进行对比,如果相等则说明没有被篡改过,可以放心的使用证书上的公钥进行通信。
而在上文的握手过程中,由于通信密钥在发送给 Server 时被公钥所加密了,而用来解密的私钥自始自终都只被证书申请者持有,所以即使有人截取了所有通信的信息,都不能得到通信的密钥(当然这是在假定密钥永远不会泄露的情况下)。
这样,我们就建立了一个安全的"信道"。
嗯...到这里还算是比较常规,接下来就是实际上的 TLS 协议了,不过规范定义中的流程和上面的版本有一点点变化
TLS 1.0 握手流程-
Client 向 Server 发送一个随机数、客户端可使用的加密组件、压缩算法和可能的 Session ID。
-
Server 接收到消息后返回证书、Session ID、准备使用的加密组件和可能使用的压缩方法,同时还会生成一个随机数并返回。
-
Client 收到后进行证书的验证,然后根据密钥协商协议向 Server 发送规定的密钥交换信息。
-
Server 和 Client 一起使用 \(pre\ master\ secret\) 与之前的随机数,通过协商好的加密算法生成 \(master\ secret\)。
然后,Server 和 Client 将开始使用 \(master\ secret\) 作为通信密钥进行通信。
-
紧接着,Client 和 Server 互相向对方发送本次握手中所有报文生成的 摘要(Hash 算法的计算结果),同时也是生成了 \(master\ secret\) 后发送的第一条加密信息。
-
当 Client 收到 Server 发来的摘要后,就可以正式开始发送应用数据。
如果你是第一次了解到以上的握手流程,那在看完后多半会发出一个“?”,所以我们接下来将详细的介绍各个步骤。
RSA 加密组件
首先需要介绍的是加密组件,TLS 1.0 中的非对称加密的算法包含 RSA 和 DH/DHE,在密钥协商阶段我们可以任意选用一种,我们先来了解使用我们最熟悉的 RSA 作为密钥协商算法的情况。
第一步Client 向 Server 发送一个随机数、自己支持的加密组件、压缩算法和可能的 Session ID。
这一步很好理解,Client 需要告知 Server 自己支持的加密算法,让 Server 进行选择,接下来才能进行通信。同时我们可以告知 Server 我们希望使用的压缩算法,如果 Server 也支持,那在之后传输上层的应用数据时将会使用该压缩算法进行解压缩。
需要提醒的是,压缩功能 TLS 不建议启用,甚至在 TLS 1.3 时相关功能被直接禁用。
这一方面是由于压缩功能并不是 TLS 协议的本职,同时也是因为压缩功能带来了安全上的问题。
"可能的 Session ID"则是在 Client 在重连 Server 时减少握手成本的一个机制,即在重连时并不需要重新走一次握手流程,只需要带着 Session ID,就能重新连接,具体的使用我们会在后面再详细介绍。
第二步
在 Server 收到 Client 的连接建立的请求后,将返回自己的证书;并且如果启用了 Session ID 的话则会分配一个 Session ID 并返回,这样的话当下次建立连接时,Client 将会带上 Session ID,就不需要走完整的握手过程。
当然 Session ID 也可以为空,这样的话代表 Server 不希望缓存会话。
并且还会根据自身的情况返回接下来将使用的加密套件名称和压缩算法的名称,同时还会生成一个随机数并返回。
第三步
Client 在收到证书后,将进行"验证证书",证书合法才会继续下一步。
然后,Client 将生成 \(pre\ master\ secret\) ,具体是啥我们看一下它的结构就知道了:
struct {
ProtocolVersion client_version;
opaque random[46];
} PreMasterSecret;
第一个字段是客户端版本,用来检测 降级攻击;第二个字段则是一个随机数。
降级攻击(downgrade attack)攻击者故意欺骗 Server 和 Client 使用旧的、不安全的协议,即使它们都支持最新的协议。常用于中间人攻击中,由于握手阶段是明文的,所以攻击者可以任意的篡改,故需要防止这种攻击。
比如美国在早期时候禁止超过指定长度的算法出口,所以 TLS 带有了一些不安全的算法。攻击者可以尝试修改明文报文来欺骗双方使用不安全的 TLS 版本(比如现在正在介绍的 TLS 1.0),以此可以更简单的破解中间使用的不安全的算法加密的应用数据。
但由于 TLS 在这里加上了 client_version 字段,并且本条数据将会被加密发送,所以实际上攻击者无法篡改。
而 \(pre\ master\ secret\) 是用来生成 \(master\ secret\) 的重要参数,所以接下来将使用证书上的公钥对 \(pre\ master\ secret\) 加密,并发送给 Server
第四步
接下来,Client 和 Server 双方都将使用(客户端随机数)+(服务器端随机数)+( \(pre\ master\ secret\) )来生成 \(master\ secret\) 。
\(master\ secret\) 将被作为通信的密钥。不过,我们为什么要这么做呢?
为什么不直接使用公钥加密 \(master\ secret\) 的方式来做密钥交换?
仔细一想,这不是挺好的吗?虽然握手流程一次也少不了,但至少少了好几个"莫名其妙"的参数?
不过实际上,如果不考虑其他密钥协商算法的话,这确实是一个不错的选择。但是别忘了,在我们思考的上面的密钥协商的流程中,使用的加密套件是 RSA,而对于 TLS 协议来讲,还具有其他密钥协商算法,例如在 TLS 1.0 中就具有 RSA 和 DH/DHE 两种,所以这其实是一个协议设计上的问题。
TLS 协议加入了 \(pre\ master\ secret\) 来生成 \(master\ secret\) 的流程,最主要还是为了使得协议能更好的模块化(应该算是一种"模板方法"的思想?)
为什么要使用到这么多随机数?
对于 \(master\ secret\) ,TLS 1.0 中的生成公式为:
\[\begin{aligned} master\_secret=PRF(&pre\_master\_secret,\\ &\text{"master secret"},\\ &ClientHello.random + ServerHello.random) [0..47]; \end{aligned} \]其中 \(PRF\) 我们可以简单理解为一个 Hash 函数。然后我们首先来考虑 \(pre\ master\ secret\) ,这是可以去掉的随机数吗?
应该不是,对于 RSA ,它是属于由客户端生成的随机数;但对于 DH/DHE 来讲,其是通过密钥协商得到的密钥(具体会在后面介绍),所以不属于随机数,不能够被去掉。
那服务器端随机数呢?对于服务器端随机数,它最大的用途就是用来防止 重放攻击。
重放攻击(replay attack)该攻击指的是在通信的时候,攻击者截取通信的一部分,然后在日后的某时重新向服务器发送该段被阶段的报文。
最重要的是,即使通信的信道是保密的(通信信息被加密),攻击者也可以使用该方法来进行捣蛋,即使它并不能理解通信的信息。
比如在下单付款时,攻击者截取了你的从下单到付款的 HTTPS 的流量,然后进行重放,使得你多下单付款了好几个订单。(当然这个例子不够恰当,因为对于付款这种流程几乎都是要多因素身份验证(MF)的,比如使用支付宝付款时手机至少得接收个验证码吧,更何况支付的时候肯定是有对应的幂等性方案的)
所以,由于我们在通信密钥的生成中加入了来自 Server 的随机数,所以即使攻击者收集了 Client 发向 Server 的报文,但由于每次的服务端随机数都不一样,所以两次握手协商出的 \(master\ secret\) 必然是不一样的,这样的话攻击者最后会在发送第一次消息的阶段就会露馅。
最后再看看客户端随机数,同样的这里的随机数也可以用来防止攻击者收集 Server 发向 Client 的数据进行重放(不过这样做大多数情况下意义不大)。并且客户端随机数还可以为最后的 \(master\ secret\) 的生成贡献"熵"(相当于盐(salt)一样)。
第五步
在得到了 \(master\ secrrt\) 后,双方会计算出本次握手中的包的摘要,并直接发送给对方(已被加密),当一方收到来自另一方的发来摘要后,将正式进行上层应用数据的通信。
其流程图大概是这样:
DH 加密组件
在介绍 DH 加密组件的握手流程之前,我们先了解下 DH 算法具体是干啥的
DH 算法介绍DH 算法也属于非对称加密算法,其在此用于密钥协商(和 RSA 相比,RSA 更像是一个密钥交换算法),其目的是在不安全的信道下协商出一个密钥以加密未来的通信信息使得建立一个安全的信道(不可监听、不可篡改)。
对于 DH 算法的具体内容,由于我们要讨论的不是算法的实现,所以为了避免陷入过多的数学细节,我们将其具体算法抽象为一个函数(你也可以点击这里了解 DH 算法的具体实现):
\[f(p,g,n)=N \]该函数中,\(p\) 与 \(g\) 在一次密钥的协商的过程中是不变的,我们只需要关心 \(n\)
该函数的 \(n\) 我们称为私钥,计算出的 \(N\) 称为公钥,且根据 \(N\) 难以推出 \(n\) (就像是哈希函数一样);而对于使用同一个 \(p\) ,\(g\) 计算出的多个密钥对(\(a\), \(A\))、(\(b\), \(B\)),具有一个特殊的性质:
\[p(a,B)=K=p(b,A) \]其中 \(K\) 为协商出来的密钥。但这又有什么用呢?我们再来重新回顾下握手流程,这次使用 DH 作为密钥协商算法。
握手流程
第一步与使用的密码套件无关,因此客户端的行为是相同的。
我们具体来看第二步,这一步中主要的不同在于返回的证书,这里的证书分两种,一种是在 RSA 中见到的,记录的是 RSA 的公钥,而另一种记录的是 DH 算法中的 \(p\) 、\(g\) 、\(B\) (\(B\) 是由 Server 的私钥 \(b\) 通过 \(f(p,g,n)\) 函数计算得到的公钥)。
在 DH 密码组件中,DHE 算法会使用前者,而 DH 算法会使用后者,这两者之间的区别在于是否具有"前向安全性"(后文会详细介绍)。
在第二步中,如果使用的是 DH 算法,将会直接把证书发送过去;而如果是 DHE,则除了发送证书外,还会使用证书上的 RSA 密钥对来对新生成的 \(p\)、\(g\) 、\(B\) 进行签名,并和这几个参数一起向 Client 发送。
在 TLS 1.2 时,除了对 \(p\) 、\(g\) 、\(B\) 签名,还会对客户端随机数和服务器端随机数进行签名。
在第三步中,当 Client 验证完证书后,接着会向 Server 发送进行密钥协商所需要的信息。对于 RSA 来讲,这一步要向 Server 发送 \(pre\ master\ secret\) ,而对于 DH 算法,则需要发送的是 \(A\) ,\(A\) 是由客户端生成的私钥 \(a\) 通过 \(p\) 、\(g\) 计算所得出的公钥。
其中客户端得到的 \(p\) 、\(g\) 来自于证书或为直接收到的参数。
接下来 \(A\) 被发送给 Server 后,Client 接着使用 \(p(a,B)\) 计算出 \(K\) 作为 \(pre\ master\ secret\) ,参与最终的 \(master\ secert\) 的计算。
而对于 Server,也可以使用 \(p(b,A)\) 生成 \(K\) 作为 \(pre\ master\ secret\) ,由于我们在上面介绍的特殊的性质,这两个 \(K\) 一定是相等的。
之后的过程则是和使用其他密钥组件相同。
以下为简单的流程图:
我们再来思考一下,这种方法安全吗?
首先考虑我们作为中间人能获得到哪些信息,在整个握手的流程中,我们可以截获 \(A\) 、\(B\) 、\(p\) 、\(g\) 、证书和两个随机数,但是参与 \(K\) 的计算的 \(a\) 或 \(b\) 都没有被泄露,且不可能根据 \(A\) 计算出 \(a\) 或根据 \(B\) 计算出 \(b\) ,所以最后用来通信的 \(master\ secert\) 也不可能被知道。
那中间人攻击呢?如果我们能将 \(B\) 和 \(b\) 替换为作为攻击者的我们的 \(C\) 和 \(c\) ,那么也是可以得到 \(master\ secret\) 的。但是, \(B\) 要么是直接被证书所保护(DH 算法),要么是被证书上的 RSA 密钥对签名(DHE算法),所以篡改也变得不可能了。
当然以上只是简单的推理,实际上在密码学中会进行严格的证明
缺点
到这里,你发现了 TLS 1.0 的几个缺点了吗?
最显著的,就是"慢",每建立一次 TLS 连接都至少需要 \(2\cdot RTT\) ,如果 www.cnblogs.com/enoc/p/tls-handshake.html
本文共计4720个文字,预计阅读时间需要19分钟。
概述:TLS(传输层安全)全称为Transport Layer Security(传输层安全),其前身是SSL(Secure Sockets Layer,安全套接层)。TLS的作用是为上层应用协议提供安全的通信,例如常见的HTTP+TLS。
概述TLS 全称为 Transport Layer Security(传输层安全),其前身是 SSL,全称为 Secure Sockets Layer(安全套接字层),它的作用是为上层的应用协议提供安全的通信,比如众所周知的 HTTP + TLS = HTTPS。
SSL 2.0 是该协议的第一个公开发布的版本,由于其存在的安全问题很快被升级到了 SSL 3.0,并且在 1999 年,IETF 小组将该协议标准化,因此 TLS 1.0 诞生了。
本文将介绍 TLS 1.0 到 TLS 1.3 的握手流程,你只需要一点点的 SSL\TLS 或 HTTPS 的前置知识与了解过简单的密码学上的概念,如对称加密、非对称加密、哈希算法等名词。
从此开始
我们先从一个最简化的 TLS 握手流程开始:
- Client 向 Server 请求建立连接。
- Server 将自己的证书发送给 Client。
- Client 验证证书,然后使用证书中的公钥加密接下来要用来通信的密钥,将加密结果发送给 Server。
- Server 收到后进行响应,且将用该密钥来对需要发送或接收的上层数据进行加解密。
- 自此 TLS 握手完成,接下来开始使用密钥进行通信。
以下为简单的流程图:
在上面中,我们使用的是 RSA 算法来进行密钥的交换。
RSA 应该是最多人了解的非对称加密算法,其在这里用于密钥协商(交换)和证书验证,这分别用到了它的两个特性:
- 公钥加密,对应私钥才能解密
- 私钥加密,对应公钥可以解密
证书机制用到了第二个机制。
证书实际上就是一段符合指定标准的文本,其大致上记录了:
- 证书所属的域名
- 域名所有者的公钥
- 有效日期
- 使用的加密算法
CA(证书颁发者)会对这些内容进行使用哈希函数导出一个值(简单来讲就是用 md5 类似的算法计算一下),该值将被 CA 的私钥进行加密(这样的操作也叫做"签名"),同时签名也将被记录到证书上。
而"证书验证"这个行为,当然指的就是使用证书颁发者公布的公钥,来对被加密的值进行解密,然后对证书使用同样的哈希函数进行计算,并使用计算出的值与解密出的值进行对比,如果相等则说明没有被篡改过,可以放心的使用证书上的公钥进行通信。
而在上文的握手过程中,由于通信密钥在发送给 Server 时被公钥所加密了,而用来解密的私钥自始自终都只被证书申请者持有,所以即使有人截取了所有通信的信息,都不能得到通信的密钥(当然这是在假定密钥永远不会泄露的情况下)。
这样,我们就建立了一个安全的"信道"。
嗯...到这里还算是比较常规,接下来就是实际上的 TLS 协议了,不过规范定义中的流程和上面的版本有一点点变化
TLS 1.0 握手流程-
Client 向 Server 发送一个随机数、客户端可使用的加密组件、压缩算法和可能的 Session ID。
-
Server 接收到消息后返回证书、Session ID、准备使用的加密组件和可能使用的压缩方法,同时还会生成一个随机数并返回。
-
Client 收到后进行证书的验证,然后根据密钥协商协议向 Server 发送规定的密钥交换信息。
-
Server 和 Client 一起使用 \(pre\ master\ secret\) 与之前的随机数,通过协商好的加密算法生成 \(master\ secret\)。
然后,Server 和 Client 将开始使用 \(master\ secret\) 作为通信密钥进行通信。
-
紧接着,Client 和 Server 互相向对方发送本次握手中所有报文生成的 摘要(Hash 算法的计算结果),同时也是生成了 \(master\ secret\) 后发送的第一条加密信息。
-
当 Client 收到 Server 发来的摘要后,就可以正式开始发送应用数据。
如果你是第一次了解到以上的握手流程,那在看完后多半会发出一个“?”,所以我们接下来将详细的介绍各个步骤。
RSA 加密组件
首先需要介绍的是加密组件,TLS 1.0 中的非对称加密的算法包含 RSA 和 DH/DHE,在密钥协商阶段我们可以任意选用一种,我们先来了解使用我们最熟悉的 RSA 作为密钥协商算法的情况。
第一步Client 向 Server 发送一个随机数、自己支持的加密组件、压缩算法和可能的 Session ID。
这一步很好理解,Client 需要告知 Server 自己支持的加密算法,让 Server 进行选择,接下来才能进行通信。同时我们可以告知 Server 我们希望使用的压缩算法,如果 Server 也支持,那在之后传输上层的应用数据时将会使用该压缩算法进行解压缩。
需要提醒的是,压缩功能 TLS 不建议启用,甚至在 TLS 1.3 时相关功能被直接禁用。
这一方面是由于压缩功能并不是 TLS 协议的本职,同时也是因为压缩功能带来了安全上的问题。
"可能的 Session ID"则是在 Client 在重连 Server 时减少握手成本的一个机制,即在重连时并不需要重新走一次握手流程,只需要带着 Session ID,就能重新连接,具体的使用我们会在后面再详细介绍。
第二步
在 Server 收到 Client 的连接建立的请求后,将返回自己的证书;并且如果启用了 Session ID 的话则会分配一个 Session ID 并返回,这样的话当下次建立连接时,Client 将会带上 Session ID,就不需要走完整的握手过程。
当然 Session ID 也可以为空,这样的话代表 Server 不希望缓存会话。
并且还会根据自身的情况返回接下来将使用的加密套件名称和压缩算法的名称,同时还会生成一个随机数并返回。
第三步
Client 在收到证书后,将进行"验证证书",证书合法才会继续下一步。
然后,Client 将生成 \(pre\ master\ secret\) ,具体是啥我们看一下它的结构就知道了:
struct {
ProtocolVersion client_version;
opaque random[46];
} PreMasterSecret;
第一个字段是客户端版本,用来检测 降级攻击;第二个字段则是一个随机数。
降级攻击(downgrade attack)攻击者故意欺骗 Server 和 Client 使用旧的、不安全的协议,即使它们都支持最新的协议。常用于中间人攻击中,由于握手阶段是明文的,所以攻击者可以任意的篡改,故需要防止这种攻击。
比如美国在早期时候禁止超过指定长度的算法出口,所以 TLS 带有了一些不安全的算法。攻击者可以尝试修改明文报文来欺骗双方使用不安全的 TLS 版本(比如现在正在介绍的 TLS 1.0),以此可以更简单的破解中间使用的不安全的算法加密的应用数据。
但由于 TLS 在这里加上了 client_version 字段,并且本条数据将会被加密发送,所以实际上攻击者无法篡改。
而 \(pre\ master\ secret\) 是用来生成 \(master\ secret\) 的重要参数,所以接下来将使用证书上的公钥对 \(pre\ master\ secret\) 加密,并发送给 Server
第四步
接下来,Client 和 Server 双方都将使用(客户端随机数)+(服务器端随机数)+( \(pre\ master\ secret\) )来生成 \(master\ secret\) 。
\(master\ secret\) 将被作为通信的密钥。不过,我们为什么要这么做呢?
为什么不直接使用公钥加密 \(master\ secret\) 的方式来做密钥交换?
仔细一想,这不是挺好的吗?虽然握手流程一次也少不了,但至少少了好几个"莫名其妙"的参数?
不过实际上,如果不考虑其他密钥协商算法的话,这确实是一个不错的选择。但是别忘了,在我们思考的上面的密钥协商的流程中,使用的加密套件是 RSA,而对于 TLS 协议来讲,还具有其他密钥协商算法,例如在 TLS 1.0 中就具有 RSA 和 DH/DHE 两种,所以这其实是一个协议设计上的问题。
TLS 协议加入了 \(pre\ master\ secret\) 来生成 \(master\ secret\) 的流程,最主要还是为了使得协议能更好的模块化(应该算是一种"模板方法"的思想?)
为什么要使用到这么多随机数?
对于 \(master\ secret\) ,TLS 1.0 中的生成公式为:
\[\begin{aligned} master\_secret=PRF(&pre\_master\_secret,\\ &\text{"master secret"},\\ &ClientHello.random + ServerHello.random) [0..47]; \end{aligned} \]其中 \(PRF\) 我们可以简单理解为一个 Hash 函数。然后我们首先来考虑 \(pre\ master\ secret\) ,这是可以去掉的随机数吗?
应该不是,对于 RSA ,它是属于由客户端生成的随机数;但对于 DH/DHE 来讲,其是通过密钥协商得到的密钥(具体会在后面介绍),所以不属于随机数,不能够被去掉。
那服务器端随机数呢?对于服务器端随机数,它最大的用途就是用来防止 重放攻击。
重放攻击(replay attack)该攻击指的是在通信的时候,攻击者截取通信的一部分,然后在日后的某时重新向服务器发送该段被阶段的报文。
最重要的是,即使通信的信道是保密的(通信信息被加密),攻击者也可以使用该方法来进行捣蛋,即使它并不能理解通信的信息。
比如在下单付款时,攻击者截取了你的从下单到付款的 HTTPS 的流量,然后进行重放,使得你多下单付款了好几个订单。(当然这个例子不够恰当,因为对于付款这种流程几乎都是要多因素身份验证(MF)的,比如使用支付宝付款时手机至少得接收个验证码吧,更何况支付的时候肯定是有对应的幂等性方案的)
所以,由于我们在通信密钥的生成中加入了来自 Server 的随机数,所以即使攻击者收集了 Client 发向 Server 的报文,但由于每次的服务端随机数都不一样,所以两次握手协商出的 \(master\ secret\) 必然是不一样的,这样的话攻击者最后会在发送第一次消息的阶段就会露馅。
最后再看看客户端随机数,同样的这里的随机数也可以用来防止攻击者收集 Server 发向 Client 的数据进行重放(不过这样做大多数情况下意义不大)。并且客户端随机数还可以为最后的 \(master\ secret\) 的生成贡献"熵"(相当于盐(salt)一样)。
第五步
在得到了 \(master\ secrrt\) 后,双方会计算出本次握手中的包的摘要,并直接发送给对方(已被加密),当一方收到来自另一方的发来摘要后,将正式进行上层应用数据的通信。
其流程图大概是这样:
DH 加密组件
在介绍 DH 加密组件的握手流程之前,我们先了解下 DH 算法具体是干啥的
DH 算法介绍DH 算法也属于非对称加密算法,其在此用于密钥协商(和 RSA 相比,RSA 更像是一个密钥交换算法),其目的是在不安全的信道下协商出一个密钥以加密未来的通信信息使得建立一个安全的信道(不可监听、不可篡改)。
对于 DH 算法的具体内容,由于我们要讨论的不是算法的实现,所以为了避免陷入过多的数学细节,我们将其具体算法抽象为一个函数(你也可以点击这里了解 DH 算法的具体实现):
\[f(p,g,n)=N \]该函数中,\(p\) 与 \(g\) 在一次密钥的协商的过程中是不变的,我们只需要关心 \(n\)
该函数的 \(n\) 我们称为私钥,计算出的 \(N\) 称为公钥,且根据 \(N\) 难以推出 \(n\) (就像是哈希函数一样);而对于使用同一个 \(p\) ,\(g\) 计算出的多个密钥对(\(a\), \(A\))、(\(b\), \(B\)),具有一个特殊的性质:
\[p(a,B)=K=p(b,A) \]其中 \(K\) 为协商出来的密钥。但这又有什么用呢?我们再来重新回顾下握手流程,这次使用 DH 作为密钥协商算法。
握手流程
第一步与使用的密码套件无关,因此客户端的行为是相同的。
我们具体来看第二步,这一步中主要的不同在于返回的证书,这里的证书分两种,一种是在 RSA 中见到的,记录的是 RSA 的公钥,而另一种记录的是 DH 算法中的 \(p\) 、\(g\) 、\(B\) (\(B\) 是由 Server 的私钥 \(b\) 通过 \(f(p,g,n)\) 函数计算得到的公钥)。
在 DH 密码组件中,DHE 算法会使用前者,而 DH 算法会使用后者,这两者之间的区别在于是否具有"前向安全性"(后文会详细介绍)。
在第二步中,如果使用的是 DH 算法,将会直接把证书发送过去;而如果是 DHE,则除了发送证书外,还会使用证书上的 RSA 密钥对来对新生成的 \(p\)、\(g\) 、\(B\) 进行签名,并和这几个参数一起向 Client 发送。
在 TLS 1.2 时,除了对 \(p\) 、\(g\) 、\(B\) 签名,还会对客户端随机数和服务器端随机数进行签名。
在第三步中,当 Client 验证完证书后,接着会向 Server 发送进行密钥协商所需要的信息。对于 RSA 来讲,这一步要向 Server 发送 \(pre\ master\ secret\) ,而对于 DH 算法,则需要发送的是 \(A\) ,\(A\) 是由客户端生成的私钥 \(a\) 通过 \(p\) 、\(g\) 计算所得出的公钥。
其中客户端得到的 \(p\) 、\(g\) 来自于证书或为直接收到的参数。
接下来 \(A\) 被发送给 Server 后,Client 接着使用 \(p(a,B)\) 计算出 \(K\) 作为 \(pre\ master\ secret\) ,参与最终的 \(master\ secert\) 的计算。
而对于 Server,也可以使用 \(p(b,A)\) 生成 \(K\) 作为 \(pre\ master\ secret\) ,由于我们在上面介绍的特殊的性质,这两个 \(K\) 一定是相等的。
之后的过程则是和使用其他密钥组件相同。
以下为简单的流程图:
我们再来思考一下,这种方法安全吗?
首先考虑我们作为中间人能获得到哪些信息,在整个握手的流程中,我们可以截获 \(A\) 、\(B\) 、\(p\) 、\(g\) 、证书和两个随机数,但是参与 \(K\) 的计算的 \(a\) 或 \(b\) 都没有被泄露,且不可能根据 \(A\) 计算出 \(a\) 或根据 \(B\) 计算出 \(b\) ,所以最后用来通信的 \(master\ secert\) 也不可能被知道。
那中间人攻击呢?如果我们能将 \(B\) 和 \(b\) 替换为作为攻击者的我们的 \(C\) 和 \(c\) ,那么也是可以得到 \(master\ secret\) 的。但是, \(B\) 要么是直接被证书所保护(DH 算法),要么是被证书上的 RSA 密钥对签名(DHE算法),所以篡改也变得不可能了。
当然以上只是简单的推理,实际上在密码学中会进行严格的证明
缺点
到这里,你发现了 TLS 1.0 的几个缺点了吗?
最显著的,就是"慢",每建立一次 TLS 连接都至少需要 \(2\cdot RTT\) ,如果 www.cnblogs.com/enoc/p/tls-handshake.html

