https原理--ECDHE密钥协商算法
HTTPS 常用的密钥交换算法有两种,分别是 RSA 和 ECDHE 算法。
其中,RSA 是比较传统的密钥交换算法,它不具备前向安全的性质,因此现在很少服务器使用的。而 ECDHE 算法具有前向安全,所以被广泛使用。
我在上一篇已经介绍了 https原理--RSA密钥协商算法,这一篇就介绍 ECDHE 算法。
一、离散对数
ECDHE 密钥协商算法是 DH 算法演进过来的,所以我们先从 DH 算法说起。
DH 算法是非对称加密算法, 因此它可以用于密钥交换,该算法的核心数学思想是离散对数。这里简单提一下它的数学公式。
离散对数是「离散 + 对数」的两个数学概念的组合,所以我们先来复习一遍对数。
要说起对数,必然要说指数,因为它们是互为反函数,指数就是幂运算,对数是指数的逆运算。
举个栗子,如果以 2 作为底数,那么指数和对数运算公式,如下图所示:
那么对于底数为 2 的时候, 32 的对数是 5,64 的对数是 6,计算过程如下:
对数运算的取值是可以连续的,而离散对数的取值是不能连续的,因此也以「离散」得名,
离散对数是在对数运算的基础上加了「模运算」,也就说取余数,对应编程语言的操作符是「%」,也可以用 mod 表示。离散对数的概念如下图:
上图的,底数 a 和模数 p 是离散对数的公共参数,也就说是公开的,b 是真数,i 是对数。知道了对数,就可以用上面的公式计算出真数。但反过来,知道真数却很难推算出对数。
特别是当模数 p 是一个很大的质数,即使知道底数 a 和真数 b ,在现有的计算机的计算水平是几乎无法算出离散对数的,这就是 DH 算法的数学基础。
1.DH 密钥协商算法
认识了离散对数,我们来看看 DH 算法原理。
Diffie-He llman 算法,简称DH 算法, 是Whitfield Diffie 和Martin Hellman 在1976 年公布的一个公开密钥算法,它的历史比RSA 公开密钥算法更悠久。
使用RSA 密钥协商算法传输会话密钥的时候,会话密钥完全由客户端控制,并没有服务器的参与,所以叫作密钥传输。而DH 算法确切地说, 实现的是密钥交换或者密钥协商, DH 算法在进行密钥协商的时候,通信双方的任何一方无法独自计算出一个会话密钥,通信双方各自保留一部分关键信息,再将另外一部分信息告诉对方,双方有了全部信息才能共同计算出相同的会话密钥。客户端和服务器端协商会话密钥的时候,需要互相传递消息, 消息即使被挟持,攻击者也无法计算出会话密钥,因为攻击者没有足够的信息(通信双方各自保留的信息〉计算出同样的会话密钥。DH 算法的主要原理和优势大概就是如此, 理解起来可能抽象了一点,下面看看DH算法的内部结构和原理,和RSA 算法一样, 不用过于理解算法的内部实现。
1 )参数文件
在使用DH 算法之前, 先要生成一些公共参数,这些参数是公开的,无须担心攻击者能够看到这些参数值,这些参数可以由客户端或者服务器端生成, 一般由服务器端生成。参数在协商密钥之前必须发给对端。
参数有两个,分别是p 和g, p 是一个很大的质数,建议长度在1024 比特以上,这个长度也决定了DH 算法的安全程度, g 表示为一个生成器,这个值很小,可以是2 或者5。
通过参数,服务器端和客户端会生各自生成一个DH 密钥对,私钥需要保密。
2) DH 密钥协商算法密钥交换过程
根据私钥生成的方式,DH 算法分为两种实现:
- static DH 算法,这个是已经被废弃了;
- DHE 算法,现在常用的,下图描述了DHE 算法处理过程。
-
通信双方的任何一方可以生成公共参数p 和g,这两个数是公开的,被截获了也没有任何关系, 一般情况下由通信双方的服务器端计算。
客户端连接服务器端,服务器端将参数发送给客户端。 -
客户端根据公开参数生成一个随机数a ,这个随机数是私钥,只有客户端知道,且不会进行发送, 然后计算Yc= (g ^a) mod p , Yc 就是公钥,需要发送给服务器端。
-
服务器端根据公开参数生成一个随机数b ,这个随机数是私钥,需要服务器端保密,然后计算Ys = (g^b) mod p, Ys 是公钥, 需要发送给客户端。
-
客户端发送Y e 数值给服务器端,服务器端计算Z = (Yc^b) mod p 。
-
服务器端发送Ys 数值给发送方, 客户端计算Z=(Ys^a)mod p 。
-
服务器端和客户端生成的Z 就是会话密钥,协商完成。
这里的关键点就是私钥a 和b 不应该泄露,分别由通信双方维护,另外Ys 和Yc 进行互换才能完成协商,这两个值被截获对攻击者来说没有任何价值。换句话说,只要私钥不发生泄露,攻击者即使有了Ys 和Yc 也不会计算出会话密钥。
看到幂运算和求模过程,就知道DH 算法和RSA 算法一样,如果需要破解密钥, 就必须面临离散对数和因式分解问题。和其他公开密钥算法一样,只要确保一定的密钥长度,DH 算法具有很高的安全性。RSA 和DH 密钥对一样能够受到暴力攻击,提高密钥对的长度能够有效避免攻击。
可以看到,整个密钥协商过程中,客户端和服务端公开了 4 个信息:p、g、Yc、Ys,其中 p、g是算法的参数,Yc、Ys 是公钥,而 a、b 是双方各自保管的私钥,黑客无法获取这 2 个私钥,因此黑客只能从公开的 p、g、Yc、Ys 入手,计算出离散对数(私钥)。
前面也多次强调, 根据离散对数的原理,如果p 是一个大数,在现有的计算机的计算能力是很难破解出 私钥 a、b 的,破解不出私钥,也就无法计算出会话密钥,如果量子计算机出来了,那就有可能被破解,当然如果量子计算机真的出来了,那么密钥协商算法就要做大的升级了。因此 DH 密钥交换是安全的。
2.DHE 密钥协商算法
上文说过,根据私钥生成的方式,DH 算法分为两种实现:
- static DH 算法,这个是已经被废弃了;
- DHE 算法,现在常用的;
静态DH 算法, p 和g 两个参数永远是固定的,而且服务器的公钥C Ys )也是固定的。和RSA 密钥协商算法一样,一旦服务器对应的DH 私钥泄露,就不能提供前向安全性。静态DH 算法的好处就是避免在初始化连接时服务器频繁生成参数p 和g ,因为该过程是非常消耗CPU 运算的。
于是,DH 交换密钥时就只有客户端的公钥是变化,而服务端公钥是不变的,那么随着时间延长,黑客就会截获海量的密钥协商过程的数据,因为密钥协商的过程有些数据是公开的,黑客就可以依据这些数据暴力破解出服务器的私钥,然后就可以计算出会话密钥了,于是之前截获的加密数据会被破解,所以 static DH 算法不具备前向安全性。
既然固定一方的私钥有被破解的风险,那么干脆就让双方的私钥在每次密钥交换通信时,都是随机生成的、临时的,这个方式也就是 DHE 算法,E 全称是 ephemeral(临时性的)。下图形象得说明了DHE算法密钥交换过程
所以,即使有个牛逼的黑客破解了某一次通信过程的私钥,其他通信过程的私钥仍然是安全的,因为每个通信过程的私钥都是没有任何关系的,都是独立的,这样就保证了「前向安全」。
3.ECDHE 密钥协商算法
DHE 算法由于计算性能不佳,因为需要做大量的乘法,为了提升 DHE 算法的性能,所以就出现了现在广泛用于密钥交换算法 —— ECDHE 算法。
ECDHE 是使用椭圆曲线(ECC)的 DH(Diffie-Hellman)算法,ECDHE 算法是在 DHE 算法的基础上利用了 ECC 椭圆曲线特性,可以用更少的计算量计算出公钥,以及最终的会话密钥。
客服端和服务器使用 ECDHE 密钥交换算法的过程:
- 双方事先确定好使用哪种椭圆曲线,和曲线上的基点 G,这两个参数都是公开的;
- 双方各自随机生成一个随机数作为私钥d,并与基点 G相乘得到公钥Q(Q = dG),此时客服端的公私钥为 Q1 和 d1,服务器的公私钥为 Q2 和 d2;
- 双方交换各自的公钥,最后客户端计算点(x1,y1) = d1Q2,服务器计算点(x2,y2) = d2Q1,由于椭圆曲线上是可以满足乘法交换和结合律,所以 d1Q2 = d1d2G <==> d2Q1= d2d1G ,因此双方的 x 坐标是一样的,x 还不是最终的会话密钥,它是一个预主密钥。
这个过程中,双方的私钥都是随机、临时生成的,都是不公开的,即使根据公开的信息(椭圆曲线、公钥、基点 G)也是很难计算出椭圆曲线上的离散对数(私钥)。
4.RSA算法、DH算法与ECDH算法的安全性对比
为了保证DH的密钥对不被破解,提升安全性的主要手段就是增加密钥对的长度,但是长度越长,性能越低。公开密钥算法是一个O(n)操作,n就是密钥对的长度,n越小,操作越快。为了解决性能问题,需要了解下椭圆曲线密码学( Elliptic Curve Cryptography ) ,简称为ECC 。
ECC 是新一代的公开密钥算法,主要的优点就是安全性,极短的密钥能够提供很大的安全性。比如224 比特的ECC 密钥和2048 比特的RSA 密钥可以达到同样的安全水平,由于ECC 密钥具有很短的长度,运算速度非常快。ECC 基于非常复杂的算法,到目前位置,对于ECC 进行逆操作还是很难的,数学上被证明是不可破解的, ECC 算法的优势就是性能和安全性非常高。
在具体应用的时候, ECC 可以结合其他公开密钥算法形成更快、更安全的公开密钥算法,比如结合DH 密钥协商算法组成ECDH 密钥协商算法,结合数字签名DSA 算法组成ECDSA 数字签名算法。下表是三个主流算法的密钥长度对比:
通过下表可以看出,一个80 比特的对称加密算法密钥长度安全性相当于10 24 比特的公开密钥算法密钥、相当于160 比特的ECC 椭圆曲线密钥。
必须明白密码学算法不同长度密钥的安全性,在使用密码学算法的时候,也要选择安全长度的密钥,我们可以采用ECRYPT II 的标准,下表是主流算法的推荐密钥安全长度:
需要注意的是,需要长期关注密钥的长度,随着时间的推移,目前安全的密钥长度未来可能就不安全了,如果特定长度的密钥不再安全,必须立刻更新密钥。
二、ECDHE 密钥协商握手过程
知道了 ECDHE 算法基本原理后,我们就结合实际的情况来看看。
我用 Wireshark 工具抓了用 ECDHE 密钥协商算法的 TSL 握手过程,可以看到是四次握手:
细心的小伙伴应该发现了,使用了 ECDHE,在 TLS 第四次握手前,客户端就已经发送了加密的 HTTP 数据,而对于 RSA 握手过程,必须要完成 TLS 四次握手,才能传输应用数据。
所以,ECDHE 相比 RSA 握手过程省去了一个消息往返的时间,这个有点「抢跑」的意思,它被称为是「TLS False Start」,跟「TCP Fast Open」有点像,都是在还没连接完全建立前,就发送了应用数据,这样便提高了传输的效率。
接下来,分析每一个 ECDHE 握手过程。
1.TLS 第一次握手
客户端首先会发一个「Client Hello」消息,消息里面有客户端使用的 TLS 版本号、支持的密码套件列表,支持的压缩算法,以及生成的随机数(Client Random)。
2.TLS 第二次握手
服务端收到客户端的「打招呼」,同样也要回礼,会返回「Server Hello」消息,消息面有服务器确认的 TLS 版本号,也给出了一个随机数(Server Random),然后从客户端的密码套件列表选择了一个合适的密码套件和压缩算法(这里压缩算法为null,表示不压缩),目前不建议进行TLS压缩,如果压缩,可能会被攻击,比如CRIME攻击,禁止TLS压缩并不会影响HTTPS网站的性能,所以服务器应该禁止TLS压缩。
Nginx服务器高版本已经关闭TLS压缩:
- Nginx1.1.6、1.0.9(基于OpenSSL1.0.0以后的版本)以后的版本禁止使用TLS压缩;
- Nginx1.3.2+、1.2.2(基于OpenSSL1.0.0以前的版本)以后的版本禁止使用TLS压缩。
不过,这次选择的密码套件就和 RSA 不一样了,我们来分析一下这次的密码套件的意思。
「 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384」
- 密钥协商算法使用 ECDHE;
- 签名算法使用 RSA;
- 握手后的通信使用 AES 对称算法,密钥长度 256 位,分组模式是 GCM;
- 摘要算法使用 SHA384;
接着,服务端为了证明自己的身份,发送「Certificate」消息,会把证书也发给客户端。
这一步就和 RSA 握手过程有很大到区别了,因为服务端选择了 ECDHE 密钥协商算法,所以会在发送完证书后,发送「Server Key Exchange」消息。
这个过程服务器做了三件事:
- 选择了名为 named_curve 的椭圆曲线,选好了椭圆曲线相当于椭圆曲线基点 G 也定好了,这些都会公开给客户端;
- 生成随机数作为服务端椭圆曲线的私钥,保留到本地;
- 根据基点 G 和私钥计算出服务端的椭圆曲线公钥,这个会公开给客户端。
为了保证这个椭圆曲线的公钥不被第三方篡改,服务端会用 RSA 签名算法给服务端的椭圆曲线公钥做个签名。
随后,就是「Server Hello Done」消息,服务端跟客户端表明:“这些就是我提供的信息,打招呼完毕”。
至此,TLS 两次握手就已经完成了,目前客户端和服务端通过明文共享了这几个信息:Client Random、Server Random 、使用的椭圆曲线、椭圆曲线基点 G、服务端椭圆曲线的公钥,这几个信息很重要,是后续生成会话密钥的材料。
3.TLS 第三次握手
客户端收到了服务端的证书后,自然要校验证书是否合法,如果证书合法,那么服务端到身份就是没问题的。校验证书到过程,会走证书链逐级验证,确认证书的真实性,再用证书的公钥验证签名,这样就能确认服务端的身份了,确认无误后,就可以继续往下走。
客户端会生成一个随机数作为客户端椭圆曲线的私钥,然后再根据服务端前面给的信息,生成客户端的椭圆曲线公钥,然后用「Client Key Exchange」消息发给服务端。
至此,双方都有对方的椭圆曲线公钥、自己的椭圆曲线私钥、椭圆曲线基点 G。于是,双方都就计算出点(x,y),其中 x 坐标值双方都是一样的,x 是一个预主密钥。
还记得 TLS 握手阶段,客户端和服务端都会生成了一个随机数传递给对方吗?
最终的会话密钥,就是用「客户端随机数 + 服务端随机数 + x(ECDHE 算法算出的共享密钥) 」三个材料生成的。
之所以这么麻烦,是因为 TLS 设计者不信任客户端或服务器「伪随机数」的可靠性,为了保证真正的完全随机,把三个不可靠的随机数混合起来,那么「随机」的程度就非常高了,足够让黑客计算出最终的会话密钥,安全性更高。
算好会话密钥后,客户端会发一个「Change Cipher Spec」消息,告诉服务端后续改用对称算法加密通信。
接着,客户端会发「Encrypted Handshake Message」消息,把之前发送的数据做一个摘要,再用对称密钥加密一下,让服务端做个验证,验证下本次生成的对称密钥是否可以正常使用。
4.TLS 第四次握手
最后,服务端也会有一个同样的操作,发「Change Cipher Spec」和「Encrypted Handshake Message」消息,如果双方都验证加密和解密没问题,那么握手正式完成。于是,就可以正常收发加密的 HTTP 请求和响应了。
三、总结
RSA 和 ECDHE 握手过程的区别:
- RSA 密钥协商算法「不支持」前向保密(ECDH和DH也不支持),ECDHE 密钥协商算法「支持」前向保密(DHE支持);
- 使用了 RSA 密钥协商算法,TLS 完成四次握手后,才能进行应用数据传输,而对于 ECDHE 算法,客户端可以不用等服务端的最后一次 TLS 握手,就可以提前发出加密的 HTTP 数据,节省了一个消息的往返时间;
- 使用 ECDHE, 在 TLS 第 2 次握手中,会出现服务器端发出的「Server Key Exchange」消息,而 RSA 握手过程没有该消息;
ECDH 和 ECDHE 握手过程的区别:
字面少了一个E,E代表了“临时”,即在握手流程中,作为服务器端,ECDH少了一步计算Pb(服务端的DH公钥)的过程,Pb用证书中的公钥代替,而证书对应的私钥就是Xb。由此可见,使用ECDH密钥交换算法,服务器必须采用ECC证书;服务器不发送server key exchange报文,因为发送certificate报文时,证书本身就包含了Pb信息。
四、HTTP/1.2、HTTP/2
HTTP/2 中只能使用 TLSv1.2+,还禁用了几百种 CipherSuite(详见:TLS 1.2 Cipher Suite Black List)。实际上,HTTP/2 允许使用的 CipherSuite 必须采用具有前向安全性的密钥交换算法,不允许使用 RSA 密钥交换。这也是为什么 RSA Private Key 无法解密 HTTP/2 加密流量。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义