## 第 4 章
### 传输层安全( TLS)
SSL( Secure Sockets Layer,安全套接字层)协议最初是网景公司为了保障网上交 易安全而开发的,该协议通过加密来保护客户个人资料,通过认证和完整性检查来 确保交易安全。为达到这个目标, SSL 协议在直接位于 TCP 上一层的应用层被实现 (图 4-1)。 SSL 不会影响上层协议(如 HTTP、电子邮件、即时通讯),但能够保证 上层协议的网络通信安全。
![](https://box.kancloud.cn/fdcfae64fe6e5057842ef72c7b82c50b_1373x667.png)
图 4-1:传输层安全( TLS)
在正确使用 SSL 的情况下,第三方监听者只能推断出连接的端点、加密类型,以及 发送数据的频率和大致数量,不能实际读取或修改任何数据。
![](https://box.kancloud.cn/1ac09340038a7ad2490142411870bb9e_185x159.png)
>*IETF 后来在标准化 SSL 协议时,将其改名为 Transport Layer Security ( TLS,传输层安全)。很多人会混用 TLS 和 SSL,但严格来讲它们并不相 同,因为它们指代的协议版本不同。
SSL 2.0 是该协议第一个公开发布的版本,但由于存在很多安全缺陷很快就被 SSL 3.0 取代。鉴于 SSL 协议是网景公司专有的, IETF 成立了一个小组负责标准化该协 议,后来就有了 RFC 2246,即 TLS 1.0,也就是 SSL 3.0 的升级版。
>本协议与 SSL 3.0 的区别并不特别明显,但这些区别会严重妨碍 TLS 1.0 与 SSL 3.0 之间的互操作性。
——TLS 协议( RFC 2246)
TLS 1.0 自 1999 年 1 月发布后,为解决发现的安全缺陷同时扩展协议的功能, IETF 工作组先后又发布过两个新版本: 2006 年 4 月发布了 TLS 1.1, 2008 年 8 月发布了 TLS 1.2。从内部来看, SSL 3.0 实现与后续所有 TLS 版本很相似,很多客户端到今 天还在支持 SSL 3.0 和 TLS 1.0。当然,要保护用户免受攻击,最好还是升级到最新 版本。
>*TLS 设计的初衷是在可靠的传输协议(如 TCP)之上运行。可是,有实现 把它放到了数据报协议(如 UDP)之上。 RFC 6347,即 DTLS( Datagram Transport Layer Security,数据报传输层安全)就旨在以 TLS 协议为基础, 同时兼顾数据报交付模式并提供类似的安全保障。*
## 4.1 加密、 身份验证与完整性
TLS 协议的目标是为在它之上运行的应用提供三个基本服务:加密、身份验证和数 据完整性。从技术角度讲,并不是所有情况下都要同时使用这三个服务。比如,可 以接受证书但不验证其真实性,而前提是你非常清楚这样做有什么安全风险且有防 范措施。实践中,安全的 Web 应用都会利用这三个服务。
- 加密
混淆数据的机制
- 身份验证
验证身份标识有效性的机制
- 完整性
检测消息是否被篡改或伪造的机制
为了建立加密的安全数据通道,连接双方必须就加密数据的密钥套件和密钥协商一 致 。 TLS 协议规定了一套严密的握手程序用于交换这些信息,相关内容将在 4.2 节 “ TLS 握手”中介绍。握手机制中设计最巧妙的地方,就是其使用的公钥密码系统 (也称“非对称密钥加密”),这套系统可以让通信双方不必事先“认识”即可商定共 享的安全密钥,而且协商过程还是通过非加密通道完成的。
握手过程中, TLS 协议还允许通信两端互相验明正身。在浏览器中,验证机制允许 客户端验证服务器就是它想联系的那个(比如,银行),而不是通过名字或 IP 地址伪 装的目标。这个验证首先需要建立“认证机构信任链”( Chain of Trust and Certificate Authorities)。此外,服务器也可以选择验证客户端的身份。比如,公司的代理服务器 可以验证所有雇员,每位雇员都应该有公司签发的独一无二的认证证书。
除了密钥协商和身份验证, TLS 协议还提供了自己的消息分帧机制,使用 MAC ( Message Authentication Code,消息认证码)签署每一条消息。 MAC 算法是一个单 向加密的散列函数(本质上是一个校验和),密钥由连接双方协商确定。只要发送 TLS 记录,就会生成一个 MAC 值并附加到该消息中。接收端通过计算和验证这个 MAC 值来判断消息的完整性和可靠性。
上述三种机制为 Web 通信构建了一个安全的环境。所有现代 Web 浏览器都支持多 种加密套件,能够验证客户端和服务器,并能对每条记录进行消息完整性检查。
>**Web 代理、 中间设备、 TLS 与新协议**
>HTTP 良好的扩展能力和获得的巨大成功,使得 Web 上出现了大量代理和中间设 备:缓存服务器、安全网关、 Web 加速器、内容过滤器,等等。有时候,我们知 道这些设备的存在(显式代理),而有时候,这些设备对终端用户则完全不可见。 然而,这些服务器的存在及成功也给那些试图脱离 HTTP 协议的人带了一些不便。 比如,有的代理服务器只会简单地转发自己无法解释的 HTTP 扩展或其他在线格 式( wire format),而有的则不管是否必要都会对所有数据执行自己设定的逻辑, 还有一些安全设备可能会把本来正常的数据误判成恶意通信。
换句话说,现实当中如果想脱离 HTTP 和 80 端口的语义行事,经常会遭遇各种部 署上的麻烦。比如,某些客户端表现正常,另一些可能就会异常,甚至在某个网 段表现正常的客户端到了另一个网段又会变得异常。
为解决这些问题,出现了一些新协议和对 HTTP 的扩展,比如 WebSocket、 SPDY 等。 这些新协议一般要依赖于建立 HTTPS 信道,以绕过中间代理,从而实现可靠 的部署,因为加密的传输信道会对所有中间设备都混淆数据。这样虽然解决了中 间设备的问题,但却导致通信两端不能再利用这些中间设备,从而与这些设备提 供的身份验证、缓存、安全扫描等功能失之交臂。
你或许一直想不通为什么大多数 WebSocket 手册都告诉你要使用 HTTPS 向移动客 户端发送数据,现在你应该明白了。随着时间推移,网络上的中间设备通过升级 也开始能识别新协议,基于 HTTPS 部署的要求也将逐渐弱化。到时候,除非你的 会话真的需要 TLS 提供的加密、身份验证和完整性检查功能,否则完全可以不用 HTTPS !
## 4.2 TLS握手
客户端与服务器在通过 TLS 交换数据之前,必须协商建立加密信道。协商内容包括 TLS 版本、加密套件,必要时还会验证证书。然而,协商过程的每一步都需要一个 分组在客户端和服务器之间往返一次(图 4-2),因而所有 TLS 连接启动时都要经历 一定的延迟。
![](https://box.kancloud.cn/7600310d5c19fe79178d724fc41e7550_1366x1028.png)
图 4-2: TLS 握手协议
>*图 4-2 假设“光通过光纤”的单程时间都是 28 ms,也就是前面 TCP 连接 中从纽约到伦敦之间的时间,另见表 1-1。
>- 0 ms: TLS 在可靠的传输层( TCP)之上运行,这意味着首先必须完成 TCP 的“三 次握手”,即一次完整的往返。
>- 56 ms: TCP 连接建立之后,客户端再以纯文本形式发送一些规格说明,比如它所运 行的 TLS 协议的版本、它所支持的加密套件列表,以及它支持或希望使用的另外一 些 TLS 选项。
>- 84 ms:然后,服务器取得 TLS 协议版本以备将来通信使用,从客户端提供的加密 套件列表中选择一个,再附上自己的证书,将响应发送回客户端。作为可选项,服 务器也可以发送一个请求,要求客户端提供证书以及其他 TLS 扩展参数。
>-112 ms:假设两端经过协商确定了共同的版本和加密套件,客户端也高高兴兴地 把自己的证书提供给了服务器。然后,客户端会生成一个新的对称密钥,用服务 器的公钥来加密,加密后发送给服务器,告诉服务器可以开始加密通信了。到 目前为止,除了用服务器公钥加密的新对称密钥之外,所有数据都以明文形式 发送。
>-140 ms:最后,服务器解密出客户端发来的对称密钥,通过验证消息的 MAC 检 测消息完整性,再返回给客户端一个加密的“ Finished”消息。
>-168 ms:客户端用它之前生成的对称密钥解密这条消息,验证 MAC,如果一切 顺利,则建立信道并开始发送应用数据。*
>*新 TLS 连接要完成一次“完整的握手”需要两次网络往返。另外,还可以使 用“简短握手”,只需一次往返,详细信息请参见 4.3 节“ TLS 会话恢复”。 *
协商建立 TLS 安全信道是一个复杂的过程,很容易出错。好在服务器和浏览器会替 我们做好这些工作,我们要做的就是提供和配置证书!
总之,尽管我们的 Web 应用不一定参与上述过程,但最重要的是知道每一个 TLS 连接在 TCP 握手基础上最多还需要两次额外的往返。这些都会增加实际交换数据之 前的等待时间!如果考虑不周,通过 TLS 交付数据很可能会引入几百甚至几千 ms 的网络延迟。
>**公钥与对称密钥加密的性能**
>公钥加密系统( http://en.wikipedia.org/wiki/Public-key_cryptography)只在建立 TLS 信道的会话中使用。在此期间,服务器向客户端提供它的公钥,客户端生成对称 密钥并使用服务器的公钥对其加密,然后再将加密的对称密钥返回服务器。服务 器继而用自己的私钥解密出客户端发来的对称密钥。
接下来,客户端与服务器间的通信就全都使用客户端生成的共享密钥加密,这就 是对称密钥加密。之所以这样设计,很大程度上是出于性能考虑,因为公钥加密 需要很大的计算量。为了演示两者的差别,假如你的电脑上安装了 OpenSSL,可 以试试以下两条命令:
• $> openssl speed rsa
• $> openssl speed aes
需要注意,这两条命令涉及的单位不能直接相比: RSA 测试会提供一个摘要表格, 列出不同密钥大小每秒的操作数,而 AES 性能通过每秒的字节数来度量。无论如 何,都不难看出 RSA 操作(完整的 TLS 握手)的数量,在推荐的 1024 或 2048 位的密钥长度下都很可能成为瓶颈。
实际的数值可能会因硬件、核心数量、 TLS 版本、服务器配置、典型的服务负载, 以及其他因素不同而有很大差异。请大家不要轻信一些广告宣传或过时的测试报 告,最好还是在自己的硬件上运行上述测试。
### 4.2.1 应用层协议协商( ALPN)
理论上,网络上的任意两端都可以使用自定义的协议进行通信。为此,需要提前确 定使用什么协议、指定端口号( HTTP 是 80, TLS 是 443),并配置所有客户端和服 务器使用它们。然而在实践中,这种方法效率很低且很难做到。因为每个端口都必 须得到认可,而防火墙及其他中间设备通常只允许在 80 和 443 端口上通信。
于是,为了简化自定义协议的部署,我们往往必须重用 80 或 443 端口,再通过额 外的机制协商确定协议。 80 端口是为 HTTP 保留的,而 HTTP 规范还专门为协商协 议规定了一个 Upgrade 首部。可是,使用 Upgrade 需要一次额外的往返时间,且由 于很多中间设备的存在,协商结果也不可靠。要了解更多信息,请参考 4.1 节中的 “ Web 代理、中间设备、 TLS 与新协议”。
>*12.3.9 节“有效的 HTTP 2.0 升级与发现”中有一个使用 HTTP Upgrade 的 实际例子。*
解决办法不难想象,那就是使用 443 端口,这是给(运行于 TLS 之上的)安全 HTTPS 会话保留的。由于端到端的加密信道对中间设备模糊了数据,因此这种方式 就成为了一种部署任意新应用协议的可靠而快捷的方式。不过,虽然使用 TLS 保障 了可靠性,但我们还需要一种机制来协商协议。
作为 HTTPS 会话,当然可以利用 HTTP 的 Upgrade 机制来协商,但这会导致一次额 外的往返,造成延迟。那在 TLS 握手的同时协商确定协议可行吗?
顾名思义,应用层协议协商( ALPN, Application Layer Protocol Negotiation)作为 TLS 扩展,让我们能在 TLS 握手的同时协商应用协议(图 4-2),从而省掉了 HTTP 的 Upgrade 机制所需的额外往返时间。具体来说,整个过程分如下几步:
- 客户端在 ClientHello 消息中追加一个新的 ProtocolNameList 字段,包含自己支 持的应用协议;
- 服务器检查 ProtocolNameList 字段,并在 ServerHello 消息中以 ProtocolName 字 段返回选中的协议。
服务器可以从中选择一个协议名,否则如果不支持其中的任何协议,则断开连接。 只要 TLS 握手完成、建立了加密信道并就应用协议达成一致,客户端与服务器就可 以立即通信。
>*ALPN 抛开了 HTTP 的 Upgrade 机制,省却了一次往返的延迟。不过, TLS 握手还是必需的。事实上, ALPN 协商不会比基于非加密信道的 HTTP Upgrade 更快,它只能保证通过 TLS 进行协议协商不会更慢。*
>**NPN 与 ALPN 的渊源**
>NPN( Next Protocol Negotiation,下一代协议协商)是谷歌在 SPDY 协议中开发的 一个 TLS 扩展,目的是通过在 TLS 握手期间协商应用协议来提高效率。听着耳熟 吗?最终结果与 ALPN 功能等价。
ALPN 是 IETF 在 NPN 基础上修订并批准的版本。在 NPN 中,服务器广播自己 支持的协议,客户端选择和确认协议。而在 ALPN 中,交换次序颠倒过来了,客 户端先声明自己支持的协议,服务器选择并确认协议。这样修改的目的是为了让 ALPN 与其他协议协商标准保持一致。
换句话说, ALPN 是 NPN 的继任者,而 NPN 已经废弃。原来配置为使用 NPN 的 客户端和服务器,都需要重新配置升级到 ALPN。
### 4.2.2 服务器名称指示( SNI)
网络上可以建立 TCP 连接的任意两端都可以建立加密 TLS 信道,客户端只需知道 另一端的 IP 地址即可建立连接并进行 TLS 握手。然而,如果服务器想在一个 IP 地 址为多个站点提供服务,而每个站点都拥有自己的 TLS 证书,那怎么办?这问题看 似有点怪,但其实一点都不怪。
为了解决这个问题, SNI( Server Name Indication,服务器名称指示)扩展被引入 TLS 协议,该扩展允许客户端在握手之初就指明要连接的主机名。 Web 服务器可以检查 SNI 主机名,选择适当的证书,继续完成握手。
>**TLS、 HTTP 及专用 IP**
>TLS+SNI 机制与 HTTP 中发送 Host 首部是相同的,只不过后者是客户端要在请求 中包含站点的主机名。总之,都是相同的 IP 地址服务于不同的域名,而区分不同 域名的手段就是 SNI 或 Host。
遗憾的是,很多旧版本的客户端( Windows XP 上的大多数 IE、 Android 2.2 等平 台上的浏览器)都不支持 SNI。如果你想与这些旧版本的客户端进行 TLS 通信, 就得为每个主机准备一个专用 IP 地址。
## 4.3 TLS会话恢复
完整 TLS 握手会带来额外的延迟和计算量,从而给所有依赖安全通信的应用造成严 重的性能损失。为了挽回某些损失, TLS 提供了恢复功能,即在多个连接间共享协 商后的安全密钥。
### 4.3.1 会话标识符
最早的“会话标识符”( Session Identifier, RFC 5246)机制是在 SSL 2.0 中引入的, 支持服务器创建 32 字节的会话标识符,并在上一节我们讨论的完整的 TLS 协商期 间作为其“ ServerHello”消息的一部分发送。
在内部,服务器会为每个客户端保存一个会话 ID 和协商后的会话参数。相应地,客 户端也可以保存会话 ID 信息,并将该 ID 包含在后续会话的“ ClientHello”消息中, 从而告诉服务器自己还记着上次握手协商后的加密套件和密钥呢,这些都可以重用。 假设客户端和服务器都可以在自己的缓存中找到共享的会话 ID 参数,那么就可以进 行简短握手(图 4-3)。否则,就要重新启动一次全新的会话协商,生成新的会话 ID。
借助会话标识符可以节省一次往返,还可以省掉用于协商共享加密密钥的公钥加密 计算。由于重用了之前协商过的会话数据,就可以迅速建立一个加密连接,而且同 样安全。
>*实际应用中,大多数 Web 应用会尝试与同一个主机建立多个连接,以便并 行取得资源。在这种情况下,会话恢复就成为减少延迟及两端计算量的必 备优化手段。
大多数现代浏览器在打开到相同服务器的新连接之前,都会有意等待第一 个 TLS 连接完成。这样,后续的 TLS 连接就可以重用第一个 SSL 会话, 从而避免重新握手造成的损失。*
![](https://box.kancloud.cn/0072a3788adce4088b41b6bf24f82826_1364x874.png)
图 4-3:简短 TLS 握手协议
由于服务器必须为每个客户端都创建和维护一段会话缓存,“会话标识符”机制在实 践中也会遇到问题,特别是对那些每天都要“接待”几万甚至几百万独立连接的服 务器来说,问题就更多了。由于每个打开的 TLS 连接都要占用内存,因此需要一套 会话 ID 缓存和清除策略,对于拥有很多服务器而且为获得最佳性能必须使用共享 TLS 会话缓存的热门站点而言,部署这些策略绝非易事。
当然,这些问题并非无解,今天的很多高流量站点都在成功地使用会话标识符。只 不过对于多服务器部署来说,为确保会话缓存的良性循环,必须对会话标识符进行 周密的规划和系统的设计。
### 4.3.2 会话记录单
为了解决上述服务器端部署 TLS 会话缓存的问题,“会话记录单”( Session Ticket, RFC 5077)机制出台了,该机制不用服务器保存每个客户端的会话状态。相反,如 果客户端表明其支持会话记录单,则服务器可以在完整 TLS 握手的最后一次交换中 添加一条“新会话记录单”( New Session Ticket)记录,包含只有服务器知道的安 全密钥加密过的所有会话数据。
然后,客户端将这个会话记录单保存起来,在后续会话的 ClientHello 消息中,可以将其包含在 SessionTicket 扩展中。这样,所有会话数据只保存在客户端,而由于 数据被加密过,且密钥只有服务器知道,因此仍然是安全的。
我们这里所说的会话标识符和会话记录单机制,也经常被人称为“会话缓存”或“无 状态恢复”机制。无状态恢复机制的优点主要是消除了服务器端的缓存负担,通过要 求客户端在与服务器建立新连接时提供会话记录单简化了部署(除非记录单过期)。
>*实践中,在一组负载均衡服务器上部署会话记录单仍然要求周密规划和系 统设计。比如,所有服务器一开始都拥有相同的会话密钥,然后再通过另 外的机制定期在所有服务器端轮换共享的密钥。*
## 4.4 信任链与证书颁发机构
身份验证是建立每个 TLS 连接必不可少的部分。毕竟,加密信道两端可以是任何机 器,包括攻击者的机器。为此,必须确保我们与之交谈的计算机是可信任的,否则 之前的工作都是徒劳。为理解如何验证通信两端的身份,下面我们以张三和李四之 间的验证为例简单说明一下:
- 张三和李四分别生成自己的公钥和私钥;
- 张三和李四分别隐藏自己的私钥;
- 张三向李四公开自己的公钥,李四也向张三公开自己的公钥;
- 张三向李四发送一条新消息,并用自己的私钥签名;
- 李四使用张三的公钥验证收到的消息签名。
信任是上述交流的关键。公钥加密可以让我们使用发送端的公钥验证消息是否使用 了正确的私钥签名,但认可发送端仍然是基于信任。在上述交流中,张三和李四可 以当面交换自己的公钥,因为他们互相认识,能够保证不被别人冒名顶替。可以说, 他们已经通过之前安全(物理)的握手确认了对方。
接下来,张三收到王五发来的一条消息。张三从未见过王五,但王五自称是李四的 朋友。事实上,为了证明自己是李四的朋友,王五还请李四用李四的私钥签署了自 己的公钥,并在消息中附上了签名(图 4-4)。此时,张三首先检查王五公钥中李四的签名。他知道李四的公钥,因而可以验证李四确实签署了王五的公钥。由于他信 任李四对王五的签名,所以就接受了王五的消息,并对消息进行完整性检查,以确 保消息确实来自王五。
刚才这个过程建立了一个信任链:张三信任李四,李四信任王五, 通过信任的传递,张 三信任王五。只要这条链上的人不会被冒名顶替,我们就可以继续扩展这个信任网络。
![](https://box.kancloud.cn/07bd1809c3a276c8c3393bde81b476ac_1360x280.png)
图 4-4:张三、李四和王五的信任链
Web 以及浏览器中的身份验证与上述过程相同,这就意味着此时此刻你应该问自 己:我的浏览器信任谁?我在使用浏览器的时候信任谁?这个问题至少有三个答案。
- 手工指定证书
所有浏览器和操作系统都提供了一种手工导入信任证书的机制。至于如何获得证 书和验证完整性则完全由你自己来定。
- 证书颁发机构
CA( Certificate Authority,证书颁发机构)是被证书接受者(拥有者)和依赖证 书的一方共同信任的第三方。
- 浏览器和操作系统
每个操作系统和大多数浏览器都会内置一个知名证书颁发机构的名单。因此,你 也会信任操作系统及浏览器提供商提供和维护的可信任机构。
实践中,保存并手工验证每个网站的密钥是不可行的(当然,如果你愿意,也可 以)。现实中最常见的方案就是让证书颁发机构替我们做这件事(图 4-5):浏览器 指定可信任的证书颁发机构(根 CA),然后验证他们签署的每个站点的责任就转移 到了他们头上,他们会审计和验证这些站点的证书没有被滥用或冒充。持有 CA 证 书的站点的安全性如果遭到破坏,那撤销该证书也是证书颁发机构的责任。
![](https://box.kancloud.cn/681b2a97e5b504d7c5fb9c121054409a_1367x459.png)
图 4-5:证书颁发机构签署数字证书
所有浏览器都允许用户检视自己安全连接的信任链,常见的访问入口就是地址栏头 儿上的锁图标,点击即可查看(图 4-6)。
- igvita.com 证书由 StartCom Class 1 Primary Intermediate Server 签发。 • StartCom Class 1 Primary Intermediate Server 证书由 StartCom Certification Authority 签发。
- StartCom Certification Authority 被认为是根证书颁发机构。
![](https://box.kancloud.cn/c70108fd4355db85529c15a611482b07_1365x722.png)
图 4-6: igvita.com 的证书信任链(谷歌 Chrome v25)
整个链条的“信任依据”是根证书颁发机构,在这里就是 StartCom Certification Authority。每个浏览器都会内置一个可信任的证书颁发机构(根机构)的名单,在 此浏览器相信而且能够验证 StartCom 根证书。实际上,通过浏览器到浏览器开发商,再到 StartCom 证书颁发机构的信任链传递,可以把信任扩展至目标站点。
>*所有操作系统和浏览器在默认情况下都会提供一个它们信任的证书颁发机 构名单。如果你想进一步了解,可以搜索并研究研究这个名单。
实践中,知名和可信任的证书颁发机构有好几百个,而这也是系统经常遭 到责难的原因。想象一下,这么多证书颁发机构无疑会构成一个较大的攻 击面,从而给坏人潜入你的浏览器信任链提供可乘之机。*
## 4.5 证书撤销
有时候,出于种种原因,证书颁发者需要撤销或作废证书,比如证书的私钥不再安 传输层安全全、证书颁发机构本身被冒名顶替,或者其他各种正常的原因,像以旧换新或所属 关系更替等。为此,证书本身会包含如何检测其是否过期的指令(图 4-7)。为确保 信任链不被破坏,通信的任何一端都可以根据嵌入的指令和签名检查链条中每个证 书的状态。
![](https://box.kancloud.cn/4c54037fa3d3d42c8a6f15c818e17164_1368x807.png)
图 4-7: igvita.com 证书中的 CRL 和 OCSP 指令(谷歌 Chrome v25)
### 4.5.1 证书撤销名单( CRL)
CRL( Certificate Revocation List,证书撤销名单)是 RFC 5280 规定的一种检查所 有证书状态的简单机制:每个证书颁发机构维护并定期发布已撤销证书的序列号名 单。这样,任何想验证证书的人都可以下载撤销名单,检查相应证书是否榜上有名。 如果有,说明证书已经被撤销了。
CRL 文件本身可以定期发布、每次更新时发布,或通过 HTTP 或其他文件传输协议 来提供访问。这个名单同样由证书颁发机构签名,通常允许被缓存一定时间。实践 中,这种机制效果很好,但也存在一些问题:
- CRL 名单会随着要撤销的证书增多而变长,每个客户端都必须取得包含所有序列 号的完整名单;
- 没有办法立即更新刚刚被撤销的证书序列号,比如客户端先缓存了 CRL,之后某 证书被撤销,那到缓存过期之前,该证书将一直被视为有效。
### 4.5.2 在线证书状态协议( OCSP)
为解决 CRL 机制的上述问题, RFC 2560 定义了 OCSP( Online Certificate Status Protocol,在线证书状态协议),提供了一种实时检查证书状态的机制。与 CRL 包含 被撤销证书的序列号不同, OCSP 支持验证端直接查询证书数据库中的序列号,从 而验证证书链是否有效。总之, OCSP 占用带宽更少,支持实时验证。
然而,没有什么机制是完美无缺的!实时 OCSP 查询也带了一些问题:
-证书颁发机构必须处理实时查询;
- 证书颁发机构必须确保随时随地可以访问;
- 客户端在进一步协商之前阻塞 OCSP 请求;
- 由于证书颁发机构知道客户端要访问哪个站点,因此实时 OCSP 请求可能会泄露 客户端的隐私。
>*实践中, CRL 和 OCSP 机制是互补存在的,大多数证书既提供指令也支持 查询。 更重要的倒是客户端的支持和行为。有的浏览器会分发自己的 CRL 名单, 有的浏览器从证书颁发机构取得并缓存 CRL 文件。类似地,有的浏览器会 进行实时 OCSP 检查,但在 OCSP 请求失败的情况下行为又会有所不同。要 了解具体的情况,可以检查浏览器和操作系统的证书撤销网络设置。 *
## 4.6 TLS记录协议
与位于其下的 IP 或 TCP 层没有什么不同, TLS 会话中交换的所有数据同样使用规 格明确的协议进行分帧(图 4-8)。 TLS 记录协议负责识别不同的消息类型(握手、 警告或数据,通过“内容类型”字段),以及每条消息的安全和完整性验证。
![](https://box.kancloud.cn/55fdec3bbddf5d2f96e0db3ac5d10bb3_1369x373.png)
图 4-8: TLS 记录结构
交付应用数据的典型流程如下。
- 记录协议接收应用数据。
- 接收到的数据被切分为块:最大为每条记录 214 字节,即 16 KB。
- 压缩应用数据(可选)。
- 添加 MAC( Message Authentication Code)或 HMAC。
- 使用商定的加密套件加密数据。
以上几步完成后,加密数据就会被交给 TCP 层传输。接收端的流程相同,顺序相 反:使用商定的加密套件解密数据、验证 MAC、提取并把数据转交给上层的应用。
同样,值得庆幸的是以上过程都由 TLS 层帮我们处理,而且对大多数应用都是完全 透明的。不过,记录协议也带来了一些重要的限制,务必要注意:
- TLS 记录最大为 16 KB;
- 每条记录包含 5 字节的首部、MAC(在 SSL 3.0、TLS 1.0、TLS 1.1 中最多 20 字节, 在 TLS 1.2 中最多 32 字节),如果使用块加密则还有填充;
- 必须接收到整条记录才能开始解密和验证。
有可能的话,应该自主选择记录大小,这也是一项重要的优化。小记录会因记录分 帧而招致较大开销,大记录在被 TLS 层处理并交付应用之前,必须通过 TCP 传输 和重新组装。
## 4.7 针对TLS的优化建议
鉴于网络协议的分层架构,在 TLS 之上运行应用与直接通过 TCP 通信没有什么不 同。正因为如此,只需要对应用进行很少改动甚至不用改动就可以让它基于 TLS 通 信。当然,前提是你根据 2.5 节“针对 TCP 的优化建议”中的最佳实践去做了。
不过,你还应该关心 TLS 部署运维的一些方法,比如服务器的部署方式和地理位 置、 TLS 记录及内存缓冲区大小、是否支持简短握手,等等。关注这些细节不仅能 大大改善用户体验,还能帮你节省运维成本。
### 4.7.1 计算成本
建立和维护加密信道给两端带来了额外的计算复杂度。特别地,首先有一个非对称 (公钥)加密,我们在 4.2 节“ TLS 握手”中介绍过。然后,在握手期间确定共享密 钥,作为对后续 TLS 记录加密的对称密钥。
如前所述,公钥加密与对称加密相比,需要更大的计算工作量。因此,在 Web 发展 早期,通常都需要专门的硬件来进行“ SSL 卸载”。好在现在不这样了。现代硬件突飞猛进的发展为减小这种损失提供了强力支持,原先需要专门硬件来做的工作,今 天直接通过 CPU 就能完成。Facebook、谷歌等大公司都通过 TLS 向数百万用户提 供服务,相关的计算工作通过软件和普通的计算机就能完成。
>今年( 2010 年) 1 月份, Gmail 切换为默认使用 HTTPS。在此之前,它只是 一个选项,而现在所有用户都在使用 HTTPS 在浏览器与谷歌之间随时随地 安全地收发邮件。为做到这一点,我们并没有部署额外的机器,也没有专用 硬件。在我们的前端机器上, SSL/TLS 计算只占 CPU 负载的不到 1%,每个 连接只占不到 10 KB 的内存,以及不到 2% 的网络资源。很多人认为 SSL/ TLS 占用了很多 CPU 时间, 我们希望上述几个数字(首次公开)能消除人 们的顾虑。 如果你现在不想往下再看了, 那只需要记住一件事: SSL/TLS 计算已经不是 问题了。
——Adam Langley(谷歌)
我们已经大规模部署了 TLS, 既使用了硬件也使用软件负载均衡器。我们发 现当前基于软件的 TLS 实现在普通 CPU 上已经运行得足够快,无需借助专 门的加密硬件就能够处理大量的 HTTPS 请求。 我们使用运行于普通硬件上 的软件提供所有 HTTPS 服务。
——Doug Beaver( Facebook)
尽管如此,像 4.3 节“ TLS 会话恢复”中介绍的技巧对优化性能依旧很重要,它能 帮你减少计算损失和 TLS 握手期间的公钥加密延迟。 CPU 也不应该处理本不该处理 的计算。
>*说到优化 CPU 周期,一定别忘了把你的 SSL 库升级到最新版本,在此基 础上再构建 Web 服务器和代理服务器!最新版的 OpenSSL 在性能方面有 了明显的提升,而你系统中默认的 OpenSSL 库很有可能已经过时了。
### 4.7.2 尽早完成( 握手)
建立连接的延迟体现在每个 TLS 连接上,包括新连接和恢复的连接,因此是优化的 重点。我们知道 TCP 连接首先要经过 2.1 节描述的“三次握手”,两端要通过一次 完整的往返交换 SYN/SYN-ACK 分组。其次, 4.2 节介绍的“ TLS 握手”在完整的 情况下,需要两次额外的往返,或在 4.3 节描述的“ TLS 会话恢复”的情况下,需 要一次额外的往返。
最差的情况,在实际交换应用数据之前,建立 TCP 和 TLS 连接的过程要经过三次往 返!以前面纽约的客户端连接伦敦的服务器为例,每次往返耗时 56 ms(见表 1-1), 那么建立完整的 TCP 和 TLS 连接需要三次往返即 168 ms,而恢复 TLS 会话需要 112 ms。当然, 56 ms 是最乐观的数字,正常情况下延迟越长,性能损失越严重。
因为所有 TLS 会话都是在 TCP 之上完成的,因此 2.5 节“针对 TCP 的优化建议” 在这里也完全适用。如果说重用 TCP 连接对于非加密通信是一个重要的优化手段, 那么这个手段对运行在 TLS 上的应用同样至关重要。换句话说,只要能省掉握手, 就应该省掉。如果必须握手,那么还有一个可能的技巧:尽早完成。
第 1 章讨论过,我们不能指望延迟在将来能下降多少,因为光电信号的传输速度已 经是光速的一个非常小的常数因子了。不能让分组传播更快,但可以缩短传播距 离!尽早完成就是这么一个技巧,即通过把服务器放到离用户更近的地方(图 4-9), 让客户端与服务器之间往返延迟最少。
![](https://box.kancloud.cn/9fa0c3408127384771ce86c172c44605_1365x615.png)
图 4-9:客户端连接的尽早完成
做到尽早完成的最简单方式,就是在世界各地的服务器上缓存或重复部署数据和服 务,而不要让所有用户都通过跨海或跨大陆光缆连接到一个中心原始服务器。当然, 这正是 CDN( Content Delivery Networks,内容分发网络)服务的内容:通过使用 本地代理服务器分流负载等手段降低延迟。
虽然 CDN 最常用于在全球优化分发静态资源,但其优点并不止于此。距离客户端 更近的服务器还可以缩短 TLS 会话,因为 TCP 和 TLS 握手的对象都是近处的服务 器,所以建立连接的总延迟就会显著减少。相应地,本地代理服务器则可以与原始 服务器建立一批长期的安全连接,全权代理请求与响应。
简言之,把服务器放到接近客户端的地方能够节约 TCP 和 TLS 握手的时间!大多数 CDN 提供商都提供这种服务,当然如果你愿意也可以以最小的成本部署自己的基础设施:在全球几大数据中心租用一些云服务器,然后在每台服务器上都运行代理(服 务器)程序,把请求转发到你的原始服务器,再加上地理 DNS 负载均衡系统即可。
>**不缓存的原始获取 **
>使用 CDN 或代理服务器取得资源的技术,如果要根据用户定制或者涉及隐私 数据,则不能做到全球缓存,这种情况被称为“不缓存的原始获取”( uncached origin fetch)。
虽然只有把数据缓存到全球各地的服务器上 CDN 才能发挥最大的效用,但“不 缓存的原始获取”仍然具有性能优势:客户端连接终止于附近的服务器,从而 显著减少握手延迟。相应地, CDN 或你的代理服务器可以维护一个“热连接池” ( warm connection pool),通过它将数据转发给原始服务器,同时做到对客户端快速响应。
事实上,作为附加的一个优化层, CDN 提供商在连接两端都会使用邻近服务器! 客户端连接终止于邻近 CDN 节点,该节点将请求转发到与对端服务器邻近的 CDN 节点,之后请求才会被路由到原始服务器。 CDN 网络中多出来这一跳,可以让数 据在优化的 CDN 骨干网中寻路,从而进一步减少客户端与服务器之间的延迟。
### 4.7.3 会话缓存与无状态恢复
无论什么情况下,在接近用户的地方终止连接都有助于减少延迟,但有延迟终归快不 过没有延迟。启用 TLS 会话缓存和无状态恢复可以完全消除“回头客”的往返时间。
SSL 2.0 引入的会话标识符机制是 TLS 会话缓存的基础,目前已经得到大多数客户 端和浏览器的广泛支持。然而,如果你在自己的服务器上配置 SSL/TLS,千万不能 主观认为该机制默认是开启的。实际上,在大多数服务器的默认配置下它是禁用的。 为此,应该仔细检查和验证自己的配置:
- 支持多进程或工作进程的服务器应该使用共享的会话缓存;
- 共享的会话缓存的大小应该根据流量调整;
- 应该设置会话超时时间;
- 在多台服务器并存的情况下,把相同的客户端 IP 或相同的 TLS 会话 ID 路由到同 一台服务器可以最好地利用会话缓存;
- 在不适宜使用“单一”负载均衡策略的情况下,应该为多台服务器配置共享缓存, 以便最好地利用会话缓存;
- 检查和监控 SSL/TLS 会话缓存的使用情况,以之作为性能调优的依据。 传输层安全
此外,如果客户端和服务器都支持会话记录单,则所有会话数据都将保存在客户端, 上述步骤就都不需要了(问题一下子就简单了很多)!不过,由于会话记录单还是 相对新的 TLS 扩展,并非所有客户端都支持它。实践中,为了取得最优结果,应该 做好两手准备:在支持的客户端中使用会话记录单,而在不支持的客户端中使用会 话标识符。这两种手段不会相互干扰,而是会很好地协同工作。
### 4.7.4 TLS记录大小
所有通过 TLS 交付的应用数据都会根据记录协议传输(图 4-8)。每条记录的上限 为 16 KB,视选择的加密方式不同,每条记录还可能额外带有 20 到 40 字节的首部、 MAC 及可选的填充信息。如果记录可以封装在一个 TCP 分组内,则还会给它增加 相应的 IP 和 TCP 字段,即 20 字节的 IP 首部和 20 字节的 TCP 首部(无选项)。结 果,每条记录的大小就变成了 60 到 100 字节。由于 MTU 通常为 1500 字节,因此 分组占比最小的情况下只相当于帧大小的 6%。
记录越小,分帧浪费越大。但简单地把记录增大到上限( 60 KB)也不一定好!如 果记录要分成多个 TCP 分组,那 TLS 层必须等到所有 TCP 分组都到达之后才能解 密数据(图 4-10)。只要有 TCP 分组因拥塞控制而丢失、失序或被节流,那就必须 将相应 TLS 记录的分段缓存起来,从而导致额外的延迟。实践中,这种延迟会造成 浏览器性能显著下降,因为浏览器倾向于逐字节地读取数据。
![](https://box.kancloud.cn/82f94ff1c0420def2c5685b573b03f1d_1366x608.png)
图 4-10: WireShark 的截图,其中 11 211 字节的 TLS 记录被分成了 8 个 TCP 段
小记录会造成浪费,大记录会导致延迟。因此,记录到底多大合适没有唯一“正 确”的答案。不过对于在浏览器中运行的 Web 应用来说,倒是有一个值得推荐的做 法:每个 TCP 分组恰好封装一个 TLS 记录,而 TLS 记录大小恰好占满 TCP 分配MSS( Maximum Segment Size,最大段大小)。换句话说,一方面不要让 TLS 记录 分成多个 TCP 分组,另一方面又要尽量在一条记录中多发送数据。以下数据可作为 确定最优 TLS 记录大小的参考:
- IPv4 帧需要 20 字节, IPv6 需要 40 字节;
- TCP 帧需要 20 字节;
- TCP 选项需要 40 字节(时间戳、 SACK 等)。
假设常见的 MTU 为 1500 字节,则 TLS 记录大小在 IPv4 下是 1420 字节,在 IPv6 下是 1400 字节。为确保向前兼容,建议使用 IPv6 下的大小: 1400 字节。当然,如 果 MTU 更小,这个值也要相应调小。
可惜的是,我们不能在应用层控制 TLS 记录大小。 TLS 记录大小通常是一个设置, 甚至是 TLS 服务器上的编译时常量或标志。要了解具体如何设置这个值,请参考服 务器文档。
>*如果服务器要处理大量 TLS 连接,那么关键的优化是把每个连接占用的内 存量控制在最小。默认情况下, OpenSSL 等常用的库会给每个连接分配 50 KB 空间,但正像设置记录大小一样,有必要查一查文档或者源代码,然 后再决定如何调整这个值。谷歌的服务器把 OpenSSL 缓冲区的大小减少到 了大约 5 KB。*
### 4.7.5 TLS压缩
TLS 还有一个内置的小功能,就是支持对记录协议传输的数据进行无损压缩。压缩 算法在 TLS 握手期间商定,压缩操作在对记录加密之前执行。然而,出于如下原 因,实践中往往需要禁用服务器上的 TLS 压缩功能:
- 2012 年公布的“ CRIME”攻击会利用 TLS 压缩恢复加密认证 cookie,让攻击者 实施会话劫持;
- 传输级的 TLS 压缩不关心内容,可能会再次压缩已经压缩过的数据(图像、视频, 等等)。
双重压缩会浪费服务器和客户端的 CPU 时间,而且暴露的安全漏洞也很严重,因此 请禁用 TLS 压缩。实践中,大多数浏览器会禁用 TLS 压缩,但即便如此你也应该 在服务器的配置中明确禁用它,以保护用户的利益。
>*虽然不能使用 TLS 压缩,但应该使用服务器的 Gzip 设置压缩所有文本资 源,同时对图像、视频、音频等媒体采用最合适的压缩格式。 *
### 4.7.6 证书链的长度
验证信任链需要浏览器遍历链条中的每个节点,从站点证书开始递归验证父证书, 直至信任的根证书。因此,优化的首要工作就是检查服务器在握手时没有忘记包含 所有中间证书。如果忘记了包含中间证书,虽然很多浏览器可以正常工作,但它们 会暂停验证并自己获取中间证书,验证之后再继续。此时很可能需要进行新 DNS 查 找、建立 TCP 连接、发送 HTTP GET 请求,导致握手多花几百 ms 时间。
>*浏览器怎么知道到哪里去找证书呢?子证书中通常包含父证书的 URL。*
另一方面,还要确保信任链中不包含不必要的证书!或更一般化地讲,应该确保证 书链的长度最小。我们在 4.2 节“ TLS 握手”中介绍过,服务器证书是在握手期间 发送的,而发送证书使用的很可能是一个处于 2.2.2 节“慢启动”算法初始阶段的 新 TCP 连接。如果证书链长度超过了 TCP 的初始拥塞窗口(图 4-11),那我们无意 间就会让握手多了一次往返:证书长度超过拥塞窗口,从而导致服务器停下来等待 客户端的 ACK 消息。
![](https://box.kancloud.cn/604c6610f884e54c746abde1743d3dc8_1367x378.png)
图 4-11: WireShark 的截图显示的 5323 字节的 TLS 证书链
图 4-11 所示的证书链超过了 5 KB,也超过了大多数旧版本浏览器的初始拥塞窗口 大小,因此会在握手期间增加一次额外的往返。对此,可以通过增大拥塞窗口来解 决,参见 2.2.2 节最后的“增大 TCP 的初始拥塞窗口”部分。此外,就是要看一看 能否减少要发送的证书大小了。
- 尽量减少中间证书颁发机构的数量。理想情况下,发送的证书链应该只包含两个 证书:站点证书和中间证书颁发机构的证书。把这一条作为选择证书颁发机构的 标准。第三个证书,也就是根证书颁发机构的证书,已经包含在浏览器内置的信 任名单中了,不用发送。
- 很多站点会在证书链中包含根证书颁发机构的证书, 这是完全没有必要的。如果浏 览器的信任名单中没有该根证书,那说明它是不被信任的,即便发送它也无济于事。
- 理想的证书链应该在 2 KB 或 3 KB 左右,同时还能给浏览器提供所有必要的信息, 避免不必要的往返或者对证书本身额外的请求。优化 TLS 握手可以消除关键的性 能瓶颈,因为每个新 TLS 连接都要经历同样的延迟。
### 4.7.7 OCSP封套
每个新 TLS 连接都要求浏览器验证发送过来的证书链的签名。然而,不要忘了还 有一步:浏览器也需要验证证书没有被撤销。为此,浏览器可能会定期下载并缓存 4.5.1 节提到的证书颁发机构发布的“证书撤销名单( CRL)”,而且还可能需要分派 发送一个 4.5.2 节提到的“在线证书状态协议( OCSP)”请求,以便实时验证。遗 憾的是,浏览器在这时候的行为差别很大。
- 某些浏览器会使用自己的更新机制推送更新的 CRL 名单,而不会按需发送请求。
- 某些浏览器可能只会针对扩展验证证书( EV 证书)进行实时 OCSP 和 CRL 检查。
- 某些浏览器可能会在上述任何一种方式下阻塞 TLS 握手,有些则不会,具体情况 取决于开发商、平台和浏览器版本。
这里的情况很复杂,也没有最好的解决方案。不过,在某些浏览器中还是可以采用 一个叫做 OCSP 封套( OCSP stapling)的优化措施:服务器可以在证书链中包含 (封套)证书颁发机构的 OCSP 响应,让浏览器跳过在线查询。把查询 OCSP 操作 转移到服务器可以让服务器缓存签名的 OCSP 响应,从而节省很多客户端的请求。 与此同时,还要注意一些情况。
- OCSP 响应从 400 字节到 4000 字节不等。把这么大的响应封套在证书链里照样 会造成 TCP 拥塞窗口溢出,因此要关注整体大小。
- 只能包含一个 OCSP 响应,即在没有缓存的情况下,浏览器对其他中间证书可能 仍然需要发送 OCSP 请求。
最后,要启用 OCSP 封套,还需要服务器支持才行。好在, NginX、 Apache 和 IIS 等服务器都可以通过配置支持 OCSP 封套。对于其他服务器,请参考文档中的说明。
### 4.7.8 HTTP严格传输安全( HSTS)
HTTP 严格传输安全( HSTS, Strict Transport Security)是一种安全策略机制,它能让 服务器通过简单的 HTTP 首部( 如 Strict-Transport-Security: max-age=31536000) 对适用的浏览器声明访问规则。具体来讲,它可以让用户代理遵从如下规则:
- 所有对原始服务器的请求都通过 HTTPS 发送;
- 所有不安全的链接和客户端请求在发送之前都应该在客户端自动转换为 HTTPS;
- 万一证书有错误,则显示错误消息,用户不能回避警告;
- max-age 以秒为单位指定 HSTS 规则集的生存时间(例如, max-age=31536000 等于 缓存 365 天);
- 用户代理可以根据指令在指定的证书链中记住(“印下”)某主机的指纹,以便将 来访问时使用,从而有效限制证书颁发机构在特定时间(由 max-age 指定)内可 颁发证书的范围。(这一项是可选的。)
事实上, HSTS 会把原始服务器转换为只处理 HTTPS 的目标服务器,从而确保应用 不会因各种主动或被动攻击给用户造成损失。从性能角度说, HSTS 通过把责任转 移到客户端,让客户端自动把所有链接重写为 HTTPS,消除了从 HTTP 到 HTTPS 的重定向损失。
>*到 2013 年初,支持 HSTS 的浏览器有 Firefox 4+、 Chrome 4+、 Opera 12+ 和 Android 平台的 Chrome 和 Firefox。要了解最新的支持情况,请访问: http://caniuse.com/stricttransportsecurity。*
## 4.8 性能检查清单
作为应用开发人员,事实上你接触不到 TLS 的这些复杂性。只要别把页面中的 HTTP 和 HTTPS 内容混为一谈,那你的应用就可以在这两种情况下都顺畅运行。然 而,应用的整体性能却会受到服务器底层配置的影响。
好在,只要想到优化,任何时候都不算晚。而且一旦优化到位,所有与服务器的新 连接都将受益无穷!下面是一个简单的检查清单:
- 要最大限制提升 TCP 性能,请参考 2.5 节“针对 TCP 的优化建议”;
- 把 TLS 库升级到最新版本,在此基础上构建(或重新构建)服务器;
- 启用并配置会话缓存和无状态恢复;
- 监控会话缓存的使用情况并作出相应调整;
- 在接近用户的地方完成 TLS 会话,尽量减少往返延迟;
- 配置 TLS 记录大小,使其恰好能封装在一个 TCP 段内;
- 确保证书链不会超过拥塞窗口的大小;
- 从信任链中去掉不必要的证书,减少链条层次;
- 禁用服务器的 TLS 压缩功能;
- 启用服务器对 SNI 的支持;
- 启用服务器的 OCSP 封套功能;
- 追加 HTTP 严格传输安全首部。
## 4.9 测试与验证
最后,要验证和测试你的配置,可以使用 Qualys SSL Server Test( https://www. ssllabs.com/ssltest/)等在线服务来扫描你的服务器,以发现常见的配置和安全漏洞。 此外,最好熟练掌握 openssl 命令行工具,通过它来检查整个握手和本地服务器配 置情况:
```
$> openssl s_client -state -CAfile startssl.ca.crt -connect igvita.com:443
CONNECTED(00000003)
SSL_connect:before/connect initialization
SSL_connect:SSLv2/v3 write client hello A
SSL_connect:SSLv3 read server hello A
depth=2 /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing /CN=StartCom Certification Authority
verify return:1
depth=1 /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing /CN=StartCom Class 1 Primary Intermediate Server CA verify return:1
depth=0 /description=ABjQuqt3nPv7ebEG/C=US /CN=www.igvita.com/emailAddress=ilya@igvita.com
verify return:1
SSL_connect:SSLv3 read server certificate A
SSL_connect:SSLv3 read server done A ❶
SSL_connect:SSLv3 write client key exchange A
SSL_connect:SSLv3 write change cipher spec A
SSL_connect:SSLv3 write finished A
SSL_connect:SSLv3 flush data
SSL_connect:SSLv3 read finished A
---
Certificate chain ➋
0 s:/description=ABjQuqt3nPv7ebEG/C=US /CN=www.igvita.com/emailAddress=ilya@igvita.com
i:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing /CN=StartCom Class 1 Primary Intermediate Server CA
1 s:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing /CN=StartCom Class 1 Primary Intermediate Server CA
i:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing /CN=StartCom Certification Authority
---
Server certificate -----BEGIN CERTIFICATE-----
... snip ...
---
No client certificate CA names sent
---
SSL handshake has read 3571 bytes and written 444 bytes ➌
---
New, TLSv1/SSLv3, Cipher is RC4-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : RC4-SHA
Session-ID: 269349C84A4702EFA7 ... ➍
Session-ID-ctx:
Master-Key: 1F5F5F33D50BE6228A ...
Key-Arg : None
Start Time: 1354037095
Timeout : 300 (sec)
Verify return code: 0 (ok)
```
❶ 客户端完成对接收到的证书链的验证
➋ 接收到的证书链( 2 个证书)
➌ 接收到证书链的大小
➍ 对有状态 TLS 恢复发送的会话标识符
在上面的例子中,我们连接到 igvita.com 默认的 TLS 端口( 443),并进行了 TLS 握 手。因为 s_client 假设没有根证书,所以我们手工把路径指定为 StartSSL Certificate Authority 的根证书,这一点很重要。你的浏览器内置了 StartSSL 的根证书,因此可 以验证这个信任链,但 s_client 没有依赖于此。如果在这里忽略根证书,应该会在 日志中看到验证错误。
通过检查证书链,我们发现服务器发送了两个证书,累计起来是 3571 字节,差不 多相当于 3~4 个 TCP 初始拥塞窗口的大小。这里要注意不能超过拥塞窗口大小,否 则就要考虑增大服务器上 cwnd 的值。最后,我们可以看到协商后的 SSL 会话变量: 选择的协议、加密套件、密钥等。还可以看到服务器为当前会话发送的会话标识符, 这个标识符在将来恢复时会用到。