HTTPS 性能研究笔记(一):概述

由于某些原因,最近一个月一直在进行 HTTPS 性能方面的研究,看了很多关于 HTTPS 的资料之后发现一个问题就是,要在现有的 HTTP 协议框架下提升性能实在是一件很困难的事情:

首先研究 HTTPS 自然需要了解 HTTPS 所用到的密码学知识,密码学所指的安全,即是保证保密性、可靠性、可用性。其中可用性实际上不由 HTTPS 协议作出保证;可靠性方面,HTTPS 要求客户端验证服务端身份,这就要求服务器创建证书,并交由证书颁发机构(也叫 CA)签名以使客户端接受。CA 会签署 HTTPS 证书认证服务器的身份,CA 签发的证书会用在 HTTPS 握手过程中;保密性方面,HTTPS 的报文就是外面包了 TLS 加密的 HTTP,从分层模型上看 HTTPS 在应用层之下传输层之上加入了一个 TLS 安全协议层,原本的 HTTP 内容作为 TLS 的载荷,TLS 把 HTTP 的内容连同头部一起加密之后塞进传输层。

但是凭空多出一层自然会多出一些性能开销(尤其是这个还是加密的),开销主要分为两个部分,一是 TLS 握手所带来的更多的握手次数在网络方面的开销,二是在加密过程中对 CPU 性能的开销;对 CPU 的开销又可以分为在建立连接过程中的非对称加密+连接建立之后传输内容的对称加密,这两个玩意有什么区别呢:简单的说,对称加密算的快,但是要求双方有一个共享的密钥既用于加密也用于解密,在客户端-服务端这个场景下如何使双方共享一个保密密钥是很困难的;非对称加密双方各自有两个密钥,一个用于加密,一个用于解密,在 HTTPS 里双方都使用保密的私钥对报文进行加密,而使用公开的公钥进行解密,配合设计巧妙的密钥交换算法,双方可以在不安全的环境里建立安全的会话,但是缺点是这个算法非常耗费计算资源。那么怎么办呢,HTTPS 把这两个东西结合起来,非对称加密交换密钥(HTTPS 握手),用得到的密钥作对称加密传输 HTTP 数据,密钥交换算法+非对称加密算法+对称加密算法+消息验证信息=一个加密套件(cipher suite),每个加密套件在 RFC 中有独立的序号,代表了 HTTPS 所使用的所有参数。


UPDATE 2016.10.29
严格地说,密钥套件的最后一部分是 HMAC 或 PRF (pseudorandom function) ,PRF 通常是 HMAC 和某种 hash 函数的结合,用于生成 master secret(主密),从 TLS 1.2 开始要求所有套件都使用 HMAC 和 SHA256 作为 PRF,并有引入随机数种子和标签,用于在不同环境下 secret 的重用。


忽略密码学上二者的区别而只关注性能的话,HTTPS 性能调优的主要重点就在建立连接过程(握手阶段)的非对称加密要比传输过程的对称加密要慢很多,因为从压力测试结果粗略的看,握手阶段对性能的开销比传输阶段要高数十倍,加之对称加密有英特尔爸爸的 AES-NI 指令集加速,传输阶段可以优化的余地非常的小,我测试了几个常见的加密套件,目前广泛使用的 AES 可以说已经是在兼顾性能和安全性上做的最好的。

因此我们来看一看 HTTPS 的握手阶段,事实上 TLS 在传统 TCP 三次握手的基础上又多加了四次握手,分别是 client hello、server hello、client key exchange、server finish几个步骤。

tls-handshakeQQ20160809-0@2x

  • Client hello阶段,客户端和服务端交换各自支持的cipher suite进行密钥协商,当客户端服务器没有相同的suit则握手失败,在支持SNI扩展的时候,还可以在client hello中传输server name。
  • Server hello阶段服务端选定加密算法,接受客户端生成的随机数,返回服务器证书和服务器生成的随机数。
  • Client key exchange阶段,客户端验证服务器证书,使用双方的随机数生成的前主密(Premaster key),并使用服务器公钥加密,返回给服务器。
  • Server finish阶段,服务器使用私钥解密premaster key,双方生成session key(块加密算法的密钥),用于加密HTTP数据传输,HTTPS连接建立完成。

这个握手过程伴随着随机数生成、非对称加密计算等过程,其中第二和第三个阶段可能还伴随着 Change Cipher Spec Finished 和验证过程,因而这个过程远比 TCP 握手费时费力,所以我们亲爱的 Web 工程师想出了各种办法消减 HTTPS 握手过程中的开销:

6AD359FF-53B0-4639-8607-DA183DFC2679

TLS 也是建立在 TCP 上的协议,因此加速 TCP 也就加速了 TLS,因此对于 TCP 的优化策略读 HTTPS 都是存在理论有效性的,比如在第一个 TCP 握手包里携带应用层数据,这就是 TCP Fast open 的思想,然而这个实现伴随着诸多问题,三次握手的设计并不是拍脑袋想出来的,要在这上面做手脚代价很大。

类似的,客户端在 Change Cipher Spec Finished 完成加密协商的同时在数据包里携带应用层请求,服务端在完成握手的同时发送应用层响应,这是谷歌的 TCP False start 思想,然而这个实现要求加密算法具有前向安全性,并在握手第一阶段携带 NPN/ALPN 等扩展表示自己支持的协议,实现起来实际上也很困难。

从底层协议入手、在握手包中抢跑携带数据行不通,那么我们来关注一下 TLS 本身,非对称加密算法真的都那么费时费力吗,没有更牛逼一点的算法吗?有,除了最为广泛使用的 RSA 加密算法,实际上还有椭圆曲线密码学,应用椭圆曲线算法到 TLS 加密领域,就是 ECC 证书:之前提到,服务端向 CA 申请 HTTPS 证书的时候需要附带自己的认证信息,实际上是附带自己的非对称加密的公钥,这个私钥可以是 RSA 的也可以是 ECC 的,提交申请时的私钥区别即是证书类型的区别。部署了 ECC 证书的 HTTPS 服务器在短连接压力测试中,相对使用 RSA 证书的同配置服务器有50%的性能提升,在不降低加密强度的前提下可以说是效果拔群。但是,客户端需要使用对应的算法对服务器的公钥进行验证,这里就会带来兼容性问题,一部分客户端不支持 EC 算法,就会无法验证服务端证书信息导致握手失败。虽然 ECC 证书存在一些兼容性问题,另外目前对 ECC 有支持的 CA 并不多,而且 ECC 证书价格相对更贵(好消息是,Let’s encrypt 项目已经支持签发 ECC 证书,而且最常用的 Web 服务器 Nginx 在1.11大版本更新中支持了对单个 vhost 添加多个证书并允许服务端指定 fallback 顺序,基本扫清了客户端兼容性方面的障碍,关于 ECC 和 Nginx 双证书部署的讨论我们留在独立的篇章里展开)

还有一个思路就是,既然连接建立这么困难,能不能复用它呢,可以,这就是 TLS Resumption,这个方法的思路是服务端判断 TLS 协议的 session 字段,只要 session 还在有效期内,就跳过加密步骤直接完成握手,但是这需要服务端程序对 TLS 的字段进行识别,对于大规模部署是一个很困难的事情。

那么很遗憾,在现有 HTTPS 协议的架构下,诸多关于性能优化的理论和方式都效果不佳,那么怎么办呢,制定互联网标准的那帮人也意识到了这个问题,于是 HTTP/2 在2015年应运而生,作为 HTTP 协议1999年的1.1版本之后将近二十年的首次版本更新,HTTP/2 引入了很多革命性的改进,对 HTTPS 性能提升有很大帮助。我们将 HTTP/2 留作独立的篇章来讨论。


后记:密码学是一个非常复杂的学科,应用到 HTTP 领域之后即使忽略其中数学的实现细节也是相当复杂,这篇文章也是极其粗略的概括,尽可能的简单地陈述 HTTPS 基本原理,主要目的是为研究 HTTPS 性能作出铺垫因此重点也放在性能的讨论上,有机会还是想写一点更加详细的现代密码学在网络中的应用。


本文链接:https://www.starduster.me/2016/08/09/https-performance-note-1/
本站基于 Creactive Commons BY-NC-SA 4.0 License 允许并欢迎您在注明来源和非商业使用前提下自由地对本文进行复制、分享或基于本文进行创作。
请注意:受限于笔者水平,本站内容可能存在主观臆断或事实错误,文中信息也可能因时间推移而不再准确,在此提醒读者结合自身判断谨慎地采纳。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据