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 性能作出铺垫因此重点也放在性能的讨论上,有机会还是想写一点更加详细的现代密码学在网络中的应用。

发表评论

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