本文翻译来自 Keyless SSL: The Nitty Gritty Technical Details
文章目录
![基本网络](https://i-blog.csdnimg.cn/blog_migrate/25dbaa9fd520d996590164aa40f00839.png)
无钥SSL产生了很好的反映,许多人想了解更多细节。这篇博客将详细描述无钥SSL如何设计,如何工作,以及为什么是安全的。在此之前,我们讲介绍互联网上如何加密,如果你已经熟悉,请跳过。
TLS
TLS(Transport Layer Security)是网络安全的基础,能够建立网站与浏览器之间的互信,在全程被监视的情况下建立加密连接。虽然TLS已经发布了好多年,但是并不为人熟知。了解TLS基础是学习无钥SSL的关键。
两个目标
TLS主要有两个目标,保密和认证。两者在互联网上都极为重要。
通信的两方认为没有第三者能明白通信内容被认为是保密通信。这可以通过对称加密来实现,用一个只有通信双方知道的密钥来加密通信内容。对于TLS,使用强密码块加密算法比如AES,老的浏览器可能只支持DES-3,RC4等目前看起来并不安全的加密算法。
另外一个目标是认证。认证的目的是确保对方是他所宣称的名字。这采用公钥实现。网站采用公钥密码来向浏览器认证。浏览器需要做两件事:验证证书所有者和确保证书可信。
一个网站有一个公钥,如果他能证明他控制着私钥,则认为网站是公钥所有者。如果一个证书由受信证书机构颁发并且包含网站域名,则认为证书可信。更多网络证书请参考另一篇博客CFSSL
在网络通信中,加密和认证通过建立一个共享密钥和证明是证书拥有者来实现。TLS是通过几次握手报文来实现
握手协议
TLS是在上世纪90年代中期,由Netscape公司的SSL协议发展而来。在1999年,IETF(Internet Engineering Task Force)对SSL协议扩展标准化,起名TLS。实际上,TLS和SSL很像,TLS1.0使用SSL3.1,是对SSL3.0做了很小的更新。之后的TLS版本也沿用这种模式,有时候TLS和SSL两个术语不做区分。
TLS有两种握手协议,一种基于RSA,一种基于DH(Diffie-Hellman). RSA和DH算法是现代密码学的奠基石,两种算法区别在于密钥的产生和认证实现
握手协议 | 密钥建立 | 认证 |
---|---|---|
RSA | RSA | RSA |
DH | DH | RSA/DSA |
两种握手协议各有优缺点。RSA只使用一个公钥算法,DH握手可以使用RSA或者DSA认证。因为RSA握手采用RSA来做认证,握手计算量更小,所以更快。公钥算法消耗大量计算(CPU)资源,是TLS握手中最慢的环节。对于一个每秒可以运算千万次AES对称加密的计算机来说,每秒只能运算几百次RSA加密。
DH需要两种算法,但它能不依赖网站私钥建立公共钥匙。这带来了转发安全性,甚至网站的私钥泄密了也能保证信息不泄密。某些DH版本可以使用ECDSA等非RSA认证来提高性能。椭圆曲线算法在更少计算量的基础上也能保证同样的安全性。使用椭圆曲线DSA认证和椭圆曲线DH密钥交换比RSA更快。
TLS术语
- 会话钥匙(session key) 这是握手最终产物,是对称加密的关键,客户端和服务器用来加密数据发送给对方。
- 客户端随机数(client random) 这是由客户端生成的32字节码。每个连接独一无二,其中包含四字节的时间戳和28字节随机数。最近谷歌chrome浏览器为了阻值客户端签名,采用32字节随机数。这个随机数往往只使用一次。
- 服务器随机数(Server random) 服务器随机数是由服务器生成,其他与客户端客户端随机数类似。
- 前导码(Pre-master secret) 48字节二进制数,和客户端/服务器随机数一起作为会话钥匙
- 加密套件(Cipher suite) 这是建立TLS连接的算法标识,用于下列功能
- 钥匙建立(DH算法变体或者RSA)
- 认证
- 加密
- 完整性(哈希)
比如套件AES128-SHA定义了一个会话采用下列算法:
- 钥匙生成-RSA
- 认证-RSA
- 加密-128比特AES, CBC(Cipher Block Chaining)模式
- 完整性-160bit SHA
一个更复杂,但是有效的套件ECDHE-ECDSA-AES256-GCM-SHA384定义了会话的下列算法 - 钥匙生成-ECDHE(Elliptic Curve Diffie-Hellman Ephemeral)
- 认证-ECDSA(Elliptic Curve Digital Signature Algorithms)
- 加密-256比特AES, GCM(Galois/Counter mode)模式
- 完整性-384bit SHA
有了上述定义,我们来描述RSA握手过程
RSA握手
下列信息交互是未加密的,完全是明文传输
消息1: “Client Hello”
client hello(后面简称CH消息)消息包含客户端想用的协议版本号和其他用于启动握手过程的信息,包括客户端随机数,支持的加密套件。现在的浏览器也发送主机名SNI(Server Name Indication), SNI可以让服务器用相同的IP去处理多个域名请求。
消息2: “Server Hello”
收到CH消息后,服务器选择接下来握手使用的参数。 加密套件的选择决定了使用哪种握手方式。 Server hello(后面简称SH消息)包含服务器随机数,服务器选择的加密套件,服务器的证书。服务器证书包含服务器的公钥和域名。
消息3: “Client Key Exchange”
客户端验证访问域名证书的有效性后,客户端生成一个random pre-master secret。这个随机数由公钥加密后发给服务器。
收到客户端发的消息后,服务器用私钥解密pre-master secret,这时双方都有pre-master secret,客户端随机数,服务器随机数,导出相同的会话钥匙。双方互发一个完成消息(Finish)指示接下来消息将被加密,握手正式完成。完成消息是“client finished” or “server finished” 由会话钥匙加密后的密文。之后的所有消息交互都是用会话钥匙加密过的。
在这个握手过程中,密钥交换和认证一步完成。背后逻辑是如果服务器可以得到会话钥匙,则认为拥有私钥,因此是证书的拥有者。
握手的下行安全性由私钥的安全性保证。假设有个中间人记录了握手过程和接下来的所有通信,如果他意外获取了私钥,他将能解密premaster secret和推导出会话钥匙,由此可以解密整个通信过程。证书过期甚至注销都无所谓。所以我们采用另外一种握手方式,私钥泄漏了都是安全保密的。
瞬时DH握手
瞬时DH握手是TLS握手的另外一种方式。它采用两种不同机制,一种用于建立共享的pre-master secret,一种用于验证服务器。这主要依赖于DH密钥协商算法。
在DH协商中,双方握有不同密钥,通过报文交互而获得共享密钥。此握手过程依赖指数计算的交换率,即
x
a
b
=
x
a
b
=
x
b
a
x^{a^b}=x^{ab}=x^{b^a}
xab=xab=xba。
握手过程如下:
- A有密钥a, 发送 g a g^a ga给B
- B有密钥b, 发送 g b g^b gb给A
- A计算 g b a g^{b^a} gba
- B计算 g a b g^{a^b} gab
- 双方使用 g a b g^{ab} gab做为共享密钥
这对于常规数字并不适用,因为
g
a
b
g^{ab}
gab可能变得非常大,并且有方法可以获取数字的第n个根。 但是,我们可以改变问题空间并使其工作。 这是通过将计算结果总是用大质数除以剩余来限制计算到固定大小的数字。 这被称为模运算。 在模运算中取一个n根称为离散对数问题,被认为是一个难题。
DH密钥协议的另一个变体是使用椭圆曲线算法ECDHE。有关椭圆曲线的更多信息,请查看我们去年发布的这个入门书。 使用这些固定大小的DH密钥协议算法可以导出共享密钥。
现在让我们介绍DH握手过程:
消息1: “Client Hello”
和RSA握手类似,CH消息包含协议版本,客户端随机数,支持的加密套件列表,以及可选的SNI扩展。 如果客户端想使用ECDHE,之后会附带支持的曲线列表。 如果省略或者不匹配,则调试起来会非常棘手。
消息2: “Server Hello”
收到CH消息后,服务器选择接下来握手使用的参数,包括ECDHE的曲线。SH消息包含服务器随机数,服务器选择的加密套件,服务器的证书。
RSA和DH握手在此之后开始有所不同。
消息3: “Server Key Exchange”
为了启动DH密钥交换,服务器需要选择一些启动参数并将它们发送给客户端,比如上面提到的 g a g^a ga。服务器还需要一种方法来证明它具有对私钥的控制,所以服务器对所发消息进行数字签名。DH参数和签名都在此消息中发送。
消息4: “Client Key Exchange”
验证证书是可信的,并且属于他们试图访问的站点后,客户端验证从服务器发来的数字签名。 他们还向客户端发送一半的Diffie-Hellman握手(对应上面的
g
b
g^b
gb)。
此时,双方都可以根据DH参数算出pre-master secret(对应于上面的
g
a
b
g^{ab}
gab),这时双方都有pre-master secret,客户端随机数,服务器随机数,导出相同的会话钥匙。双方互发一个完成消息(Finish)指示接下来消息将被加密,握手正式完成。之后的所有消息交互都是用会话钥匙加密的。
Making it keyless
昨天我们发布了无密钥SSL,采用Cloudflare的解决方案,网站可以不用保管客户的私钥。
从上面的握手图中可以看出,私钥只在每次握手中使用一次。这使得我们可以分解TLS握手过程,除了密钥相关操作放在keyserver上,其他握手过程放在CloudFlare的服务器上。这个keyserver可以运行在客户自己指定的服务器上,密钥使用得到严格监管。
一旦安全隧道建立,RSA握手如下所示:
DH握手如下所示:
以这种方式扩展TLS握手需要修改NGINX服务器和OpenSSL,使私钥操作可选择远程无阻塞模式,NGINX可以在等待密钥服务器的返回同时继续处理其他请求。iSVC Partners和Matasano Security审计了NGINX/ OpenSSL的变更和客户端服务器之间的通信协议之后发现,Keyless SSL的安全性与内部SSL相当。学术研究人员也从安全和性能角度证实了无匙SSL的可用性。
密钥服务器可以运行在Linux(Red Hat/CentOS,Debian和Ubuntu等), UNIX操作系统(包括FreeBSD)和Microsoft Windows Server上。客户还可以参考C语言实现的参考代码,以便搭建自己的密钥服务器。
安全硬件提供商(比如venafi)很快提供了密钥管理解决方案供客户使用。
无钥匙SSL支持相同证书的多个密钥服务器。密钥服务器是无状态的,允许客户使用现成的硬件,并随着流量的增长扩容服务器。通过运行多个密钥服务器并通过DNS进行负载均衡,客户的站点可以保持高可用性。
Protecting the oracle
为了确保Keyless SSL安全,CloudFlare的边缘与密钥服务器之间的连接也需确保安全。密钥服务器可以通过对任何可以联系的人执行私钥操作来充当加密预言者。确保密钥服务器只相应特定来源请求,对保证keyless SSL的安全是至关重要的。
我们通过相互验证的TLS保护从CloudFlare到密钥服务器的连接。之前,我们描述了单向认证的TLS握手:只有客户端验证服务器。在相互认证的TLS中,客户端和服务器都有相互认证的证书。密钥服务器认证CloudFlare,CloudFlare认证密钥服务器。
在无钥匙SSL中,密钥服务器仅允许携带由CloudFlare内部证书颁发机构签署的证书的客户端连接。我们使用由我们自己的证书颁发机构颁发的证书来进行连接的双方。我们严格控制如何授予这些证书,并使用“X.509扩展密钥用法”选项来确保仅按照预期使用证书。这样可以防止没有CloudFlare授权证书的服务器与密钥服务器进行通信。客户还可以选择添加防火墙规则,对keyserver的入向连接限速。
此外,我们将此连接的密码套件限制为以下之一:
- ECDHE-ECDSA-AES256-GCM-SHA384
- ECDHE-RSA-AES256-GCM-SHA384
这些是OpenSSL中最强大的两个密码,并保证CloudFlare和密钥服务器之间的连接具有完美的前向保密性。
其他安全考虑
密钥服务器可以使用硬件安全模块(HSM, hardware security module),以避免像hearbleed之类的漏洞攻击。
密钥服务器不像Bleichenbacher那样受到密码字典攻击的影响,因为它使用的是恒定大小的响应。只要密钥服务器上使用的底层加密库不受影响,定时攻击等旁路攻击(SCA, Side-channel attacks )就无效。我们在我们的参考实现中使用了OpenSSL。
性能增强
CloudFlare旨在使网站更快:连接CloudFlare上运行的网站所需的时间应少于竞争对手。无密钥SSL也是如此。使用无钥匙SSL连接到站点应比连接到禁用CloudFlare的同一站点更快。这看起来很奇怪,因为无钥匙SSL需要额外连接到密钥服务器,这是如何实现的呢,答案在于地理。
CloudFlare的数据中心遍布全球20个国家,95%的情况下网络时延不超过20ms。这使访问者可以与网络上离他们最近的CloudFlare服务器进行通信。访问者和CloudFlare之间发送的消息不需要太多路由,所以连接延迟较小。这种邻近效应是CloudFlare加速方式之一。
在上面的Keyless SSL图表中,除了一个之外的所有消息都通过CloudFlare和访问者之间的短链接传播。唯一的长途往返是密钥服务器。
如上图,旧金山的访问者想通过TLS访问托管在伦敦的网站,没有CloudFlare,TLS握手需要从旧金山到伦敦的两次往返。 随着无钥匙SSL和在伦敦托管的密钥服务器,访问者将最终在CloudFlare附近的圣何塞数据中心。 在这种情况下,只有其中一条消息必须前往伦敦并返回。 消息必须行进较短的距离,握手更快。
CloudFlare与server保持着只需要一次我们只需要一个往返密钥服务器的原因是持久连接。 一旦CloudFlare连接到密钥服务器,它就可以保持连接,以便任何新的访问者访问该网站。 第一次连接到无钥匙SSL的网站是快速的,接下来的连接会更快。
快速握手
TLS提供了称为“会话恢复”的出色性能功能。如果客户端之前已经建立了与服务器的会话,并且正尝试再次连接,则可以使用快速握手。有两种机制可以这样做:会话号(session ID)和会话票(session ticket)。
会话号要求服务器保持会话状态(即会话密钥),以备前一个会话需要恢复。在会话票据的情况下,服务器在初始握手期间发送会话票据(包括用票据密钥加密的会话密钥)到客户端。当恢复会话时,客户端将加密的密钥发送回解密它的服务器并恢复会话。没有必要使用私钥来恢复会话。
Firefox和Chrome是支持会话票据的主要浏览器。所有其他现代浏览器都支持通过会话ID进行恢复。在大规模使用这些技术时面临的一个挑战是负载平衡。为了使服务器恢复连接,它需要具有先前建立的会话密钥。如果访问者尝试恢复与新服务器的连接,则该服务器需要以某种方式获取原始会话密钥。
会话恢复的主要问题是不能扩展到负载平衡的服务器。如果客户端在一台服务器上启动会话,则无法在另一台服务器上恢复该会话。这不是协议的缺点,只是开源Web服务器中的一个缺失功能(译者注:现在很多LB可以支持session同步)。
通过无钥匙SSL,我们引入了高级会话恢复功能来解决这个问题。在全球范围内,可以通过session ticket来做会话恢复, 在同一个数据中心里,可以用session id做会话恢复。会话恢复允许访问者多次快速建立连接,因为不需要服务器到密钥服务器来建立新的连接。
会话票恢复
通过会话票,我们能从网络上任意一台机器恢复会话,不过这需要开放的社区支持
最近,Twitte宣布他们正在使用每12小时轮换一次的会话票密钥。通过每小时轮换会话票来增加安全。Twitter建立了一个集中的会议票密钥生成器,每隔一小时发布一个新密钥,以便在全球网络上分发。每个密钥持续一段可配置的时间(默认为96小时),然后永久删除。为了分发密钥,我们向密钥存储Kyoto Tycoon添加了一个TLS层,以便分发过程通过TLS的相互验证和CloudFlare的证书认证。通过Kyoto Tycoon,会话票密钥可以在数秒内分发到全球的每个服务器节点。为了与我们的开源哲学保持一致,我们计划向Kyoto Tycoon公开我们的变更。凭借每台服务器上的票证密钥,我们可以在整个网络中的任何一台机器上恢复任何连接。
票据钥匙的旋转有助于我们为客户保持完美的前向保密性,同时减少客户端(Firefox,Chrome……)的访问延迟。
会话号恢复
开源代码
CloudFlare在设计无钥SSL时开发了大量代码,并且把主要部分回馈社区:
- Strict SSL: this code allows upstream connections from NGINX to validate TLS connections, needed for validating the identity of the key server. This change was merged into NGINX.
- Session tickets: 我们在Nginx上增加了对会话票的支持,we added support for session ticket in NGINX. This change was merged into NGINX.
- CFSSL: we recently open sourced the tool we use for our internal certificate authority. It is available on GitHub.
- Kyoto Tycoon: 一个我们广泛使用的,在同步时可以双向认证的高性能key-value数据库。近期我们将开源我们的修改。
- Key Server: 参考代码已经在Github开源
意义何在
无钥SSL允许站长可以不用上传私钥,而使用CloudFlare的云服务来加速网站访问,这是一个重大进步。 如我们之的公告,Sebastien可以用一晚上搭建起无钥SSL原型。我们花了两年时间使得此技术安全,快速,可扩展。现在,使用持久连接和会话恢复技术,无钥SSL不仅安全,而且超快。