SSL的话题
撰写于:2016年3月31日
作者:silex Wi-Fi专家
上一次,我讲解了作为互联网代名词的 HTTP 协议,并且我曾写道,在 HTTP 的加密中被广泛使用的 SSL(以及它的演变版本 TLS)与 HTTP “实际上是不可分割的”。这次我要讲解的就是关于 SSL/TLS 的内容。
正如上一次也提到过的那样,SSL 3.0 和 TLS 1.0 的基本工作原理几乎是相同的。在这篇文章中,将以 SSL3.0 为基础,对于 TLS 则会根据需要添加注释。在使用的术语方面,对于 SSL 和 TLS 共有的内容,以 “SSL” 为基准,只有在描述 TLS 及其后续版本特有的内容时才会写成 “TLS” 。
SSL 基本操作
SSL 的运行由 “初始信息交换(Hello)”、“加密密钥交换(Key Exchange)”和“加密通信开始(Change Cipher Spec)” 这三个阶段构成。简单来说
客户端 服务器
→ Client Hello →
← Server Hello ←
→ Client Key Exchange →
→ Change Cipher Spec →
← Change Cipher Spec ←
(随后的通信将被加密)
如上述情况。只看这些的话,感觉很简单呢。
SSL 的通信数据结构被整合在一个叫做「记录」的框架内,根据需要,在记录中会包含「消息」。用类似 C 语言的方式来写的话,如下所述。
struct Message {
unsigned char msg_type;
unsigned char length[3];
unsigned char body[*];
};
struct Record {
unsigned char type;
unsigned char version[2];
unsigned char length[2];
union {
struct Message message[*];
unsinged char plain_data[*];
} record_data;
};
就会是像这样的结构。在会话建立后的加密通信中,记录结构也会得以维持、加密后的数据会被放置在 application_data 记录(type = 23)的 plain_data 部分,并以这种形式进行发送和接收。
消息长度是 24 比特,而外侧的记录长度却是 16 比特,这感觉有点奇怪。不过在 SSL 中,对于包含在具有相同类型的记录中的数据,会将其当作连续的数据来处理,而且记录是允许进行分段(Fragmentation)后再发送的。SSL 消息大多在几百字节以内,但在使用将用于验证的根证书(CA 证书)汇总在一起一并发送的证书链(Certificate Chain)时,数据量有时会达到 100KB 以上,在这种情况下,消息就会被分割成多个记录。

分割 SSL 信息
即便以数据的分割与重新组合为前提,像偏移量或序列这类实现该操作所必需的信息,在 SSL 的数据结构中却是不存在的,这类功能被委托给了下层。因为它是在像 TCP 这样能够保证数据顺序的协议之上运行的,所以这并没有什么问题。然而,如果要在不具备这种特性的协议上运行,那就会很麻烦,这也是导致在 WiFi 企业认证中所使用的 EAP-TLS 系列的实现变得复杂的原因之一。
SSL 的消息长度通常刚好能放入记录中,但在 TLS 中,有时记录的长度会比消息的总长度还要大。这是由在 RFC3546(2003 年)中定义的 TLS 消息扩展机制所导致的,该机制是在消息主体之后紧接着 16 比特的扩展信息长度(※注),然后是任意数量的扩展信息以 TLV 格式(标签 16 比特 + 长度 16 比特 + 任意长度的数据)依次排列。

TLS 消息扩展形式
在 TLS 中,利用这一机制对 SSL 所没有的功能(比如能够加快重新连接速度的Session Ticket,或者 ECDH 椭圆曲线密钥交换流程的相关信息等)进行了事后追加扩展。然而,如果实现方式或版本不同,对扩展信息的处理方式也会有所差异,这有时也会成为导致兼容性问题的根源。
※注:不过,只要老老实实地阅读 RFC3546/4366 就会发现,从表面上看,Extended Hello 消息中,Extension的 TLV数组是紧跟在 compression method 之后排列的,而且根本没有在任何地方写着 Extension Length 之类的内容呢…… 它到底是在哪里定义的呢?
SSL 的通信流程
Client Hello 流程
SSL 的通信在 TCP 连接完成后(SYN - SYN - ACK的交互),由客户端(通常是发送了 SYN 的节点)发送 Client Hello 消息(msg_type = 1)来启动。在 Client Hello 中,重要的是版本号以及客户端所支持的加密算法列表(Cipher Suite)。在 SSL3.0/TLS 中,虽然版本号会附在所有记录的开头,但在 Client Hello 消息中会再次包含版本号。这恐怕是从没有在记录中附带版本号的 SSL2.0 继承下来的类似阑尾一样的东西吧。
Cipher Suite 是一个 16 位的整数值,例如 0x0005 = SSL_RSA_WITH_RC4_128_SHA 这样,它是预先定义好并与公钥加密算法 + 对称密钥加密算法 + 哈希算法的组合相关联的。在 SSL3.0 中定义的从 0x0000 到 0x001B 的 Cipher Suite 值在 TLS1.0 中被原封不动地继承下来了。不过,在 SSL3.0 中为 Fortezza 公司的 IC 卡认证所定义的 0x001C 到 0x001E 这些值中,0x001E 在 TLS1.0 的 Kerberos 扩展认证(RFC2712)中存在重复定义,从这一点上也能隐约看出 IETF“对特定企业专利产品的反感”。
SSL 不仅考虑到可用于加密,还考虑到可作为压缩协议来使用,因此在 Client Hello 消息中也有一个名为 Compression Method 的字段。长期以来,这个字段一直被当作 “为将来预留” 的,通常只填入一个值 0(null)。不过,在 RFC3749(2004 年发布)中,定义了值为 1(DEFLATE)的压缩算法。然而,TLS 的数据压缩选项或许是在安全性方面的考量有所欠缺,后来引发了被称为 “CRIME” 和 “BREACH” 的安全漏洞风波。
题外话:Monkey Cipher
在 SSL 的 Cipher Suite 中,定义了诸如 0x0003(SSL3_RSA_EXPORT_RC4_40_MD5)和 0x0008(SSL3_RSA_EXPORT_DES_40_CBC_SHA)这样的 「用于出口的密码套件」。这些套件将 RC4 或 DES 算法以比原本更短的 40 位密钥来运行,并且用于密钥交换的 RSA 或 DH 的公钥长度也限制为 512 位,从而成为了 「更容易被解密」的东西。特意把加密设置得更容易被解密,这实在是 “让人无法理解”,但这其实是过去加密技术曾作为军事技术受到管制那个时代的遗留产物(※注)。因为作为民用产品出货的东西不能落入敌对国家手中并被当作军事技术挪用,进而成为对美国的威胁并损害国家利益…… 所以在 20 世纪 90 年代,美国国家安全局(NSA)认定在现实可行的时间内能够解密的 512 位公钥和 40 位私钥的加密产品是可以出口的,而具有更强加密强度的加密产品则 “仅限于在美国国内使用” 。
当然,这不仅遭到了用户的差评,还招致了业界 “反而会削弱美国国际产品的竞争力,损害国家利益” 这样的反对声浪,在美国国会引发了激烈的争论。到 2000 年为止,出口管制逐步放宽,那些极其薄弱的 512 位和 40 位的“monkey cipher” 也成为了历史。
※注:不过,现如今也并非意味着有关加密的进出口管制已完全废除,至今仍有许多国家要求对产品中所使用的加密技术进行清单申报,而且最新、最强的加密技术有时也会被视为军事机密。此外,并非 “敌对国家” 的犯罪分子对加密网络的滥用已经成为一个长期存在的社会问题。在美国发生的枪击案中作为证据的 iPhone 手机,关于司法部门是否有权获取数据解密权限等问题也引发了诸多讨论。在加密技术领域,一直存在着个人利益与社会利益之间的平衡问题。
Server Hello 流程
Server Hello 是对 Client Hello 的回复。在大多数情况下,Server Hello 流程由 Server Hello、Certificate、Server Hello Done 这三条消息组成。Server Hello 流程中的消息组是被整合到一条记录中一起发送,还是分别作为单独的记录进行发送,这会因具体的实现方式不同而有所差异。
Server Hello 消息(msg_type = 2)中所包含的信息,大体上与 Client Hello 的相同。不过,一个很大的不同点在于, Cipher Suite 字段里并非是 “算法列表”,而只包含了服务器所选择的一个算法。
Certificate 消息(msg_type = 11)是用于发送 X.509 数字证书的,而证书是 SSL 中保障安全的关键部分。关于这一点,之后会详细阐述。
Server Hello Done 消息(msg_type = 14)表示 Server Hello 流程的结束。
在相互认证的情况下
之前写到 “在大多数情况下” 由三条消息组成,这是因为当客户端也发送证书来进行相互认证(Mutual Authentication)时,Server Hello 流程中会额外包含 Server Key Exchange 消息(msg_type=13)和 Certificate Request メッセージ在这种情况下,客户端会在 Client Key Exchange 流程之前返回 Certificate 消息(msg_type = 13)和 Certificate Verify 消息(msg_type=15)。
相互认证由于存在需要在客户端(网页浏览器)也安装证书的繁琐性,因而不受欢迎,在 WWW 上几乎不被使用。通常的做法是仅使用服务器的证书进行认证,在加密通信开始后,通过 HTTP POST 方式在加密线路上发送用户名和密码,这种实现方式较为常见。不过,不知是幸运还是不幸,在 WiFi 企业认证的 EAP-TLS 中采用了相互认证。在 EAP-PEAP 和 EAP-TTLS 中,从原理上来说,和网页认证一样,属于 “单向认证、建立加密连接、发送密码” 这样的实现方式。但不幸的是,就像我之前在这个博客中写过的那样,最终出现了多个彼此不兼容、看似相似实则不同的标准并存的情况。
Anonymous DH 的情况下
根据 SSL/TLS 的规范,不使用证书进行公钥交换的 Anonymous Diffie-Hellman(简称 ADH 或 DH_anon)流程也有相应的定义。然而,这仅仅是 “有定义” 而已,实际上几乎没人使用(※注)。即便在仅以加密为目的且不需要认证的情况下,也并非 “不使用证书”,很多时候会使用无法进行篡改验证的自签名证书(Self-Signed Certificate)。具体详情请参考后文 “X.509 数字证书” 这一项。
※注:Cisco 公司的 EAP-FAST 企业认证在 Phase0 的自动配置模式中使用了 EAP-TLS/ADH,我认为这是 ADH 密钥交换为数不多的应用案例之一。
Client Key Exchange 流程
接收到 Server Hello 消息的客户端,在使用证书确认了服务器的合法性之后,会根据所显示的 Cipher Suite,通过随机数生成对应的对称密钥,然后对其进行加密,并发送 Client Key Exchange 消息(msgtype=16) 。
在使用 RSA 证书的情况下,证书中所显示的公钥会直接作为加密密钥。因为 RSA 算法具有对称性,所以使用公钥加密的数据,只有使用私钥才能解密。
在使用 Diffie-Hellman(DH)算法的情况下,流程会稍有不同。会根据交换得到的 DH 参数以及服务器和客户端各自在内部持有的私密信息,计算出一个共同的数据序列。无论哪种情况,服务器和客户端之间都会以一种让窃听通信线路的第三方无法知晓的方式交换共享的私密信息,而这些私密信息将成为后续用于数据加密的加密密钥。
Change Cipher Spec 流程
在发送 Client Key Exchange 消息之后,客户端会立即发送一个 Change Cipher Spec レコード(type=20) ,以此通知对方此后的通信将进行加密处理。请注意,Change Cipher Spec 并非是 Handshake 记录(type = 22)中的消息,而是一种具有不同类型的记录(※注)。在发送 Change Cipher Spec 记录之后,紧接着会再次以 Handshake 记录的形式发送 Finished 消息(msgtype=20) ,不过由于其内容已经被加密,所以一般情况下是无法读取的。
接收到 Change Cipher Spec 的服务器也会返回 Change Cipher Spec 和 Finished 消息,至此,SSL 的加密通信线路便建立起来了。
※注:将 Change Cipher Spec 定义为记录类型而非消息类型,这样就自动禁止了在同一条记录中同时存在明文和密文的情况。这可真是个巧妙的设计呢。乍一看,要是简单地将其统一为消息类型,然后写上诸如 “更改密码规范消息必须在独立的记录中发送” 之类的规定,用 MUST, MUST NOT 等词汇来作为实现上的限制,这或许只是规范制定者的自我满足罢了,但对于实际进行开发实现的人员来说,这却增添了额外的麻烦。毕竟,并不是所有参与开发实现的人员都会从始至终逐字逐句地通读规范文档,并且在完全理解所有给出的条件之后才进行开发实现的……
SSL 的实施
正如 Secure Socket Layer 这个名称所表明的那样,SSL/TLS 是一种介入在 TCP/IP 套接字和应用层之间的中间件。在原始的(未加密的)TCP 套接字通信中,
socket_t s;
struct sockaddr_in client_address;
struct sockaddr_in server_address;
char buff[1024];
int len;
(client_address, srever_address の初期化は省略)
s = socket(AF_INET, SOCK_STREAM, 0);
bind(s, &client_address, sizeof(client_address));
connect(s, &server_address, sizeof(server_address));
send(s, "GET / HTTP/1.0\r\n\r\n", 18, 0);
len = recv(s, buff, sizeof(buff), 0);
close(s);
像上述这样进行连接,不过,要是将其变为支持 SSL 的情况的话,
socket_t s;
struct sockaddr_in client_address;
struct sockaddr_in server_address;
char buff[1024];
int len;
SS_CTX* ctx;
SSL* ssl;
(client_address, srever_address の初期化は省略)
s = socket(AF_INET, SOCK_STREAM, 0);
bind(s, &client_address, sizeof(client_address));
connect(s, &server_address, sizeof(server_address));
SSL_ctx = SSL_CTX_new(SSLv3_client_method());
ssl = SSL_new(ctx);
SSL_set_fd(ssl, s);
SSL_connect(ssl);
SSL_write(ssl, "GET / HTTP/1.0\r\n\r\n", 18);
len = SSL_read(ssl, buff, sizeof(buff));
close(s);
SSL_free(ssl);
SSL_CTX_free(ctx);
就会变成上述这样。在 socket_t 的 “外侧” 创建了 SSL 结构体指针,并且以 “覆盖” 在诸如connect, send, recv 等套接字 API 之上的形式,实现了 SSL_connect, SSL_write, SSL_read 等 SSL API。也就是说,当想要对某个软件使用 SSL 进行加密时,原则上需要进行修改源代码并重新编译的工作(※注),这就是 SSL 的局限性之一。与此相对,在 IPsec 中,由于加密是在较低的协议层进行的,所以应用程序无需进行任何修改就能够实现加密处理。
SSL 的 API 并没有标准规格,不过由于开源库 OpenSSL 被广泛使用,所以即便是进行自主实现的时候,很多情况下也会采用类似于 OpenSSL 的 API。
※注:之所以写上 “原则上”,是因为也存在一些无需修改源代码就能实现 SSL 加密的方法,比如通过外部程序实现的 “SSL 隧道”,或者借助外部硬件的 “SSL VPN 路由器” 等。
X.509 数字证书
在 SSL/TLS 中所使用的 X.509 数字证书是 OSI 协议标准的产物。OSI 有着一个宏大的构想,即通过 X.500 目录管理协议(DAP) 对全世界的数据进行统一的集中管理,而 X.509 最初就是为了在 X.500 上进行认证而开发的。
所谓 “数字证书”,是指针对某一数据块计算出其哈希值(校验和),并附上用私钥对该哈希值进行加密后的运算结果的东西。我们将这个附上的内容称为「签名(Signature)」。信息的接收方可以从相同的数据块中重新计算哈希值,然后用数据中所包含的公钥对签名进行解密,若解密结果与重新计算出的哈希值一致,就能够验证该数据的合法性。然而,仅靠这一流程,当哈希值和公钥都被完全替换时,是无法进行验证的。
(图)自签名证书。 Issuer 和 Subject 拥有相同的 DN。虽然可以确认公钥和签名是一致的,但如果公钥和签名两者都被一起替换掉,那就无法进行验证了。
于是在 X.509 中就出现了「根证书(CA 证书)」这一概念。在这种情况下,附在证书上的签名并非由证书中所包含的公钥对生成,而是由为该证书提供担保的认证机构(CA)来创建的。认证机构使用非公开的私钥进行签名,并将与该私钥成对的公钥作为 CA 证书对外公开。
接收方使用 CA 证书的公钥对签名进行解密,并通过确认其与证书的哈希值是否一致来进行验证。即使攻击者试图替换哈希值和公钥,在没有获取到认证机构私钥的情况下(除非花费天文数字般的尝试时间),也无法生成与 CA 证书相符的哈希值。
(图)外部签名证书。Issuer 指的是 “根证书” 的 Subject。由于签名是使用根证书的私钥来进行的,所以要替换证书的内容并且还能使签名一致,这是非常困难的。
CA 证书有时候还会被其他的 CA 证书签名,一路追溯下去,就会找到以 “无条件信任为前提” 的根证书。根证书由Verisign, GlobalSign, GeoTrust 等社会信誉度高的安全企业颁发,在 Windows 等个人计算机操作系统中,预先安装了十几种具有代表性的根证书。因为存在这种证书链系统,所以攻击者想要制作出拥有相同 Subject 的伪造 CA 证书并使签名相符,也变得很困难了。
所有的证书都有有效期,而且(极其罕见地)会出现安全问题,导致 CA 证书在到期前就失效,所以在 Windows 系统中,存在着通过 Windows Update 来更新最新证书的机制。
X.509 subject 和 URL 的关系
那么,像这样,X.509 数字证书是一种为 “某一数据块” 提供合法性验证机制(俗称 “盖章认证”)的体系。然而,为了确保 Web 服务器的合法性,究竟应该给 “什么” 颁发证书才合适呢?我们无法证明服务器本身这一物理实体的合法性,而且给频繁更新的 HTML 数据或者无法保证持久性的 IP 地址颁发证书,从现实角度来看也是不可行的。
在 X.509 中,需要证明的对象是用 X.500 DN 这种格式来表示的。DN 是 Distinguished Name 的缩写,在旨在对全世界的数据进行统一管理的 X.500 体系中,它是 “赋予该数据的独一无二的标识符”。DN 是由多个具有层级结构的字段组成,包括国家(C)、州 / 省(ST)、市 / 镇(L)、组织(O)、专有名称(CN)等,并为每个字段赋予相应的名称后组合在一起,例如 "C=US,ST=CA,L=Santa Ana,O=STA,CN=YS" 这样的形式。
在最初的 OSI 构想中,由于 X.509 中所写的 DN 本身也是 X.500 的 DN 标识符,所以 “该证书验证的对象是什么” 是不言而喻的。然而,在由 TCP/IP 协议而非 OSI 连接的 WWW 网络中,并不使用 DN。用于标识网站的标识符是像 “http://www.silex.jp” 这样的 URL,那么,是不是像 “C=jp, O=silex, CN=www” 这样,将 URL 分解并与 DN 对应起来就可以了呢?(※注)
※注:1998 年发布的 RFC2247 中定义了新的 DC 字段,并规定了将 DNS 域名展开为类似 “DC=www,DC=silex,DC=jp” 这样的 DN 的方法。然而,我认为没人会使用这个规范。至少在万维网领域中是没有被采用的。
在实际的 Web 证书中, 会在 CN 字段里填入 URL 的域名(例如 “www.silex.jp”)。收到证书的浏览器会忽略 C、O 等字段, 仅验证 CN 与域名是否一致而且,不知从何时起,将 CN 中包含的 * 字符视为通配符匹配的这种实现方式也逐渐普及开来。例如,北美的谷歌网站的证书的 CN=*.google.com 这样一来,无论是www.google.com还是mail.google.com,都可以用同一份证书来进行认证。
从原本作为 “赋予数据的独一无二的标识符” 的 X.500 DN 的起源来看,在 DN 中包含能与任何内容都匹配的通配符字符,这简直就像是本末倒置一样。但这也算是为了将原本为 OSI 开发的 X.509 应用于 TCP/IP 和 WWW 而做出的一种 “迁就适配” 吧。(※注)
※注:不过,我不清楚 “网站证书应在subject DN 的 CN 字段中填入域名” 以及 “域名中可以混合使用通配符” 这样的实现是在哪个规范文档中规定的。关于通配符,在 RFC2459(1999 年)中有相关描述,但这是针对 subjectAltName 扩展字段的描述,无法从中解读出可以直接在subject DN 中使用通配符。在最新版的 RFC5280(2008 年)中也是同样的情况,关于 subject DN 的描述(4.1.2.6 章)中也没有写 “当认证对象是 Web 服务器时,应在subject DN 的 CN 字段中填入域名”。或者它可能是在 W3C 的某个规范文档中定义的,但这是我一直有点在意(但又不想深入研究)的地方。
关于证书有效期的一则小故事
如前所述,X.509 证书是有有效期的,它被记录为介于 notBefore 和 notAfter 这两个时间点之间的一个时间段。然而,不少小型嵌入式设备并没有配备带电池备份的时钟(实时时钟),每次开机时时间都从 1970 年 1 月 1 日开始计时。所以,如果忘记调整 SSL 的设置选项(或者打上补丁)来使其 “不验证证书时间” 的话,当尝试通过 EAP-TLS 进行连接时,就常常会出现被判定为 “证书在有效期之外(无效)” 而无法连接的问题。
notBefore / notAfter 使用 UTCTime 或 GeneralizedTime 数据类型。这两者都是以数字罗列的形式来表示日期和时间的字符串,不过,由于 UTCTime 的年份表示是两位数,所以一旦超过 2000 年,就会出现循环。在当前的 X.509 标准中, 将 UTCTime 的表示范围定义为 1950 年至 2049 年,并且规定在表示 2050 年及之后的日期和时间时,应当使用新定义的年份以四位数表示的 GeneralizedTime。
在 “为全世界的所有数据赋予独一无二的标识符并进行统一管理” 这样宏大的 OSI 构想中,其标准的日期和时间数据类型却只考虑了两位数的年份表示,这真的会让人产生 “这是在搞什么啊” 的感觉。不过,像这样的情况在现实中也是屡见不鲜的呢。
SSL/TLS 的应用
非 HTTP 加密
SSL/TLS 作为用于 HTTP 的加密和认证协议,在网络上被广泛使用。不过,由于只需对源代码稍作修改,就能对现有的程序进行加密,所以它也被应用于 HTTP 以外的加密场景。其中具有代表性的就是邮件协议中的 SMTP 和 POP3、以及文件传输协议 FTP 。
HTTP over SSL (https) 传统上使用端口号 443。POP3 over SSL 使用端口号 995,FTP over SSL 使用端口号 990,但它们不像 HTTPS 那么常用,并且根据软件或服务提供商的不同,也可能会使用其他端口。至于SMTP over SSL,甚至在 IANA 都没有注册端口号,像 465 和 587 等多个端口号都在混合使用。
WiFi 企业认证
我已经多次提到过,SSL/TLS 也被用于 EAP 之上的认证协议 EAP-TLS(以及它的衍生协议 TTLS、PEAP、FAST)。由于原本 TLS 是被设计为基于像 TCP 这样的传输协议运行的,现在却要在基于数据包消息交换的 EAP 协议中实现,所以其实现过程相当复杂且不连贯。当出现 “在特定的 RADIUS 服务器上认证不通过” 之类的问题时,需要转储通信日志,提取 EAP 有效载荷,逐段还原已被拆分得七零八落的 TLS 协商过程并进行追踪排查,这种工作真的会让人想哭。
SSL VPN
SSL/TLS 也应用于 VPN(Virtual Private Network) 。VPN 是一种通过对公共线路进行加密,从而连接组织内部专用网络的技术。过去,有 PPTP(GRE)、L2TP(※注)、SOCKS 等多种类型,令人眼花缭乱。但现在,IPsec 和 SSL 形成了两大主要阵营。
※注:L2TP 会在底层使用 IPsec,但是与只允许 IP 协议通过的 IPsec 不同,L2TP 是以处理任意 MAC 帧的 “封装框架” 为前提的,所以它常常被视为与 IPsec VPN 是不同的类型。
SSL VPN 的特点在于它能够 “借助” WWW 的通信基础设施,因此很容易穿越代理服务器和 NAT 路由器。要是想从公共 WiFi 线路连接 VPN,使用 IPsec 的话基本上是不可能实现的。此外,由于可以沿用 HTTP 的加密和认证机制,所以安装在个人电脑上的相关软件体积较小,而且设置起来也很方便。凭借这些特点,SSL VPN 最初是作为移动连接方式得以普及的,而在站点之间的 VPN 连接方面,一直以来使用的都是专用的 IPsec VPN 路由器,二者各司其职。不过,最近用于站点间连接的 SSL VPN 路由器也逐渐增多了。
SSL 和 IPsec
SSL/TLS 的基本操作由 “Hello 交换”“证书交换”“密钥交换” 这简单的三个阶段构成,并没有太多的选项。从规格上来说,虽然也有双向认证和 Anonymous DH 等方式,但尤其是在互联网上的 Web 服务器方面,大多数情况下都采用 “仅通过服务器证书的 subject CN 字段来认证服务器的域名” 以及 “在加密线路建立后,使用 Basic Authentication 或 HTTP POST 方法来进行用户认证” 这样单一的模式来运行。“规格相对简单” 且 “使用方式单一”,这意味着作为 “所能实现的功能也有限” 这一限制的交换条件,它具备 “易于理解”“兼容性高” 的优点,所以,对于用户、管理者以及开发者来说,它都成了一种 “使用方便” 的技术。
与此相对,IPsec/IKE 为了实现 “似乎什么都能做到” 的效果,随意地增加了许多选项,结果变成了一种在设置和运用上都困难重重的协议。当然,因为这是数字技术,所以 “只要所有部分都正确实现并正确设置,就应该能正常运行”,但要做到这一点实在是太难了,尤其是在存在不同机型、不同操作系统混杂的情况下,甚至会让人感到绝望。对于开发者而言,要对数量多得离谱的各种组合都进行运行验证,这需要耗费大量的人力和时间,而且往往会有所疏漏,从而留下像 “当 AH 隧道报头 + ESP + 认证尾端使得有效载荷长度为 8 的整数倍减 1 时,有时哈希值会不匹配” 这样难以发现的潜在漏洞,进而引发一个让兼容性越来越低的恶性循环。
IPsec 以成为 “无所不能的安全协议” 为目标是有其原因和背景的,但从结果来看,不禁让人觉得 “果然还是要遵循‘Keep It Simple and Stupid’啊……”。比起众多聪明人聚在一起集思广益形成的 “民主” 式规范,反而是从特定企业的特定实现衍生出的 “独裁专制” 式规范,最终却被更广泛地使用,这样的情况在现实中也是屡见不鲜的。
总结
以上,我从 SSL/TLS 的工作原理到各种相关的闲谈话题都进行了一番总结。再次梳理之后,切实感受到它承载着诸如 OSI 构想的遗产,还有冷战时代的印记等各种各样的过往。毕竟 SSL2.0 是在 1995 年发布的,比 IEEE802.11 无线LAN(初代标准在 1997 年发布,11b 标准在 1999 年发布)还要古老呢。
开发并发布 SSL 的 Netscape Communications 已经不复存在了,然而 SSL/TLS 作为 TCP/IP 协议之上的安全协议,已然占据了事实上的标准地位,在相当长的一段时间内,似乎也不会出现能够取代它的协议。“Netscape Communications” 这个名字,想必也会作为 SSL/TLS 所承载的过往历史的一部分,一直传承至未来吧。