SSL/TLS协议简介
安全套接字层(SSL)和传输层安全性(TLS)是加密协议,通过TCP/IP网络进行的通信提供安全的身份验证和数据完整性。
通过使用如对称密钥密码,加密安全哈希函数以及非对称加密/解密和身份验证的公钥基础结构之类的加密原语,这些协议使主机能够在不安全的网络上,建立安全信道。
TLS 1.1和TLS 1.2 是IETF标准,并且是对TLS 1.0的改进,而TLS 1.0本身是Netscape开发的SSL协议(版本3.0)的标准化后继版本。
TLS是一个分层协议。 两个端点之间交换的所有数据都包含在TLS记录内,TLS记录是协议堆栈的最底层。 TLS结构如下:
1 | Offset Length Content |
TLS是按big-endian字节顺序排列。
不同版本的SSL的Record Version对应如下:
1 | \x03\x00 //SSL 3.0 |
注意,在大多数应用程序中,默认情况下SSL 2.0没有Record层,已被弃用并禁用。
TLS支持几种记录类型(Record Type):
包括握手协议(\x16),更改密码规范协议(\x14),警报协议(\x15)和应用程序数据协议(\x17)。
客户端和服务器使用握手协议来协商通用密钥,交换密钥数据并验证握手过程的完整性。 更改密码规范协议(\x14)消息用于初始化两端的密码。 建立TLS会话后,所有TLS消息的记录数据(Record Data)部分都会使用协商密钥加密。
握手协议的记录数据(Record Data)字段具有以下格式:
1 | Offset Length Content |
存在几种类型的握手消息类型(Handshake Type),包括Client Hello(\x01),Server Hello(\x02),Hello Verify Request(\ x03),Certificate(\ x0b),Server Hello Done(\ x0e),Client Key Exchange(\ x10) ,Finished(\ x14)等,
每个都有自己的结构。
注:Change Cipher Spec 消息具有记录类型(\x14),记录数据长度(\x01)和记录数据(\x01),这是固定消息。
TLS 握手:
SSL/TLS对于传输层的加密是通过动态密钥对数据进行加密实现的,而动态密钥则通过握手流程协商制定;为了保证动态密钥的安全性,其中使用公钥加密算法(非对称)、数字证书签名等手段。
一个SSL/TLS 握手过程需要协商的信息包括:
- 协议的版本号;
- 加密算法,包括非对称加密算法、动态密钥算法;
- 数字证书,传输双方通过交换证书及签名校验对彼此进行鉴权;
- 动态密钥,传输数据过程使用该密钥进行对称加解密,该密钥通过非对称密钥进行加密传输。
客户端和服务器之间的以下消息流描述了正常的TLS握手(标记为带星号的是可选的):
TLS消息流程图
简单说明流程图:
注意:握手协议作为SSL的一个子协议,在实现过程中是一个状态机。但握手协议中Change Cipher Spec(更改密钥规格)并不是一个握手消息,而是一个独立的TLS子协议类型。对此做法的官方解释是为了避免流水线推迟。OpenSSL中间人漏洞的发现者Masashi Kikuchi认为这种设计是漏洞成因之一。
客户端发出一个 Client Hello 消息,携带的信息包括:
所支持的SSL/TLS 版本列表;支持的与加密算法;所支持的数据压缩方法;随机数A;
服务端响应一个 Server Hello 消息,携带的信息包括:
协商采用的SSL/TLS 版本号;会话ID;随机数B;服务端数字证书 ServerCA;
由于双向认证需求,服务端需要对客户端进行认证,会同时发送一个 Client Certificate Request,表示请求客户端的证书;
客户端校验服务端的数字证书;校验通过之后发送随机数C,该随机数称为预主密钥(premaster key),使用服务器的数字证书中的公钥加密后发出;
由于服务端发起了 Client Certificate Request,客户端使用私钥加密一个随机数 ClientRandom随客户端的证书 ClientCA一并发出;
服务端校验客户端的证书,并成功将客户端加密的随机数ClientRandom 解密;
根据随机数A/随机数B/随机数C(premaster key)产生动态密钥(master-key),加密一个Finish 消息发至客户端;
客户端根据 同样的随机数和算法生成动态密钥(master-key),加密一个Finish 消息发送至服务端;
服务端和客户端分别解密成功,至此握手完成,之后的数据包均采用生成动态密钥(master-key)进行加密传输。
Client Key Exchange 消息说明
Client Key Exchange 消息包含用于生成密钥的最后一条加密信息:使用服务器的公钥加密的预主密钥(premaster key)。客户端生成的预主密钥(premaster key)只能由服务器解密,并防止中间人窃听。两端都使用预主密钥(premaster key)来生成主密钥(master key)。然后,将主密钥(master key)和公开可用的Client Hello和Server Hello的随机值A/随机值B,一起用作密钥扩展伪随机函数的输入,以生成密钥块。然后,将来自密钥块的字节分配给各种加密,解密,IV和MAC密钥,所有这些仅客户机和服务器共享。
Change Cipher Spec消息说明
注意Change Cipher Spec消息是如何使用的:客户端在收到服务器Hello Done消息后,生成预主密钥(premaster key),主密钥(master key)和最后的密钥块,发送Client Key Exchange消息,然后才发送Change Cipher Spec消息。服务器发送其Change Cipher Spec收到并验证来自客户端的Finished消息。在每一端,均使用Change Cipher Spec消息来初始化密码,并另一端所有其他TLS数据将受到刚刚协商的密码的保护。
双向认证和单向认证说明
双向认证更好的解决了身份冒充问题,服务端提供证书的同时要求对客户端身份进行认证;但是在一些常见的应用场景下往往只有单向认证,如采用https网站只需要求客户端(浏览器)对服务端的证书进行认证。
在单向认证场景下,握手阶段2服务端不会发出 Client Certificate Request,之后服务端也不需要校验客户端证书;
在双向认证场景下,客户端如果无法提供证书,会发出 no digital certificate alert 的警告信息,此时可能导致握手失败;
随机数的使用说明
由于数字证书是静态的,因此要求使用随机因素来保证协商密钥的随机性;对于RSA 算法来说,预主密钥(premaster key)本身就是一个随机数,再加上Hello消息中的随机数,三个随机数通过一个密钥导出器最终导出一个对称密钥。
之所以采用 预主密钥(premaster key)机制是因为SSL协议不信任每个主机都能产生完全随机的随机数,如果预主密钥(premaster key)不随机,那么被猜出来的风险就很大,于是仅仅使用 预主密钥(premaster key)作为密钥不合适,需要引入新的随机因素,也就是同时结合hello消息中的双向随机数。
会话密钥重用说明
SSL/TLS握手过程比较繁琐,同时非对称加解密性能比对称密钥要差得多;如果每次重建连接时都需要进行一次握手会产生较大开销,因此有必要实现会话的重用以提高性能。
常用的方式包括:
SessionID(RFC 5246),客户端和服务端同时维护一个会话ID和会话数据状态;重建连接时双方根据sessionID找到之前的会话密钥实现重用;
SessionTicket(RFC 5077),由服务端根据会话状态生成一个加密的ticket,并将key也发给客户端保证两端都可以对其进行解密。该机制相较sessionID的方式更加轻量级,服务端不需要存储会话状态数据,可减轻一定压力。
证书的校验说明
检查数字签名;
CA链授权检查;
证书过期及激活时间检查;
SSL/TLS协议握手过程
SSL的握手过程严格说是SSL协议的子协议,RFC5246(SSL V1.2)分为三个子协议,握手协议,记录协议(加密传输数据)和告警协议(用于告警和关闭连接)。握手过程实际就是握手协议的内容。
下图为一个单向认证的握手过程,13为客户端,55为服务端。
一、客户端向服务端发送
Client Hello
支持的协议版本,比如TLS 1.2
支持的加密算法(Cipher Suties)
客户端生成的随机数A
二、服务端向客户端发送
Server Hello
确认使用的协议版本
服务器生成的随机数B
session id
确认使用的加密算法
Certificate
服务器证书
Server Key Exchange
如果是DH算法,这里发送服务器使用的DH参数。RSA算法不需要这一步。
Server Hello Done
Server Hello结束。因为采用单向认证所以没有Certificate Request
三、客户端向服务端发送
Client Key Exchange
包含预主密钥(premaster key)。客户端生成第三个随机数。如果是采用RSA算法,会生成一个48字节随机数,然后用服务端的公钥加密之后再放入报文中;如果是DH算法,这里发送的就是客户端的DH参数,之后服务器和客户端根据DH算法,各自计算出相同的预主密钥(premaster key)。因为采用单向认证,所以不会发送Certificate Verify消息。
Change Cipher Spec
客户端通知服务器开始使用加密方式发送报文。客户端使用上面的3个随机数client random, server random, premaster key, 计算出48字节的主密钥master secret, 这个就是对称加密算法的密钥。
Finished
客户端发送第一个加密报文。使用HMAC算法计算收到和发送的所有握手消息的摘要,然后通过RFC5246中定义的一个伪函数PRF计算出结果,加密后发送。Finished是上图的Encrypted Handshake Message。
四、服务端向客户端发送
服务器端发送Change Cipher Spec和Finished消息,到这里握手结束。
OpenSSL中间人漏洞
OpenSSL中间人漏洞(CVE-2014-0224)。 该漏洞是由于在处理Change Cipher Spec消息时的OpenSSL方法存在缺陷。 未经身份验证的中间人攻击者可以通过使用特制握手强制使用弱密钥来利用此漏洞。 成功的利用将使攻击者能够解密流量并将纯文本注入TLS连接。
漏洞影响版本:
- OpenSSL Project OpenSSL prior to 0.9.8za
- OpenSSL Project OpenSSL prior to 1.0.0m
- OpenSSL Project OpenSSL prior to 1.0.1h
远程未经身份验证的中间人(MITM)攻击者可以利用此漏洞来解密和注入流量。当OpenSSL客户端和服务器尝试执行TLS握手时,中间人(MITM)会拦截并将Client Hello转发到服务器。攻击者然后从服务器截获Server Hello,服务器Certificate和Server Hello Done消息,再将Server Hello消息转发到客户端,然后在转发服务器 Certificate和Server Hello Done消息之前,注入Change Cipher Spec消息。
注意:也有可能在服务器Certificate之后,但在Server Hello Done之前发送Change Cipher Spec记录消息。
客户端在收到这些Change Cipher Spec消息后会初始化弱密码(主密钥为空),并以Client Key Exchange,Change Cipher Spec和Finished消息进行响应。在将Client Key Exchange和Finished的消息发送到服务器之前,中间人(MITM)再次拦截这些消息,并注入自己的Change Cipher Spec记录消息。当服务器在Client Key Exchange之前收到中间人(MITM)发送的Change Cipher Spec消息时,它会初始化相同的弱密码(主密钥为空),然后处理Client Key Exchange消息以更新其主密钥,并最终计算正确的verify_data,处理Finished消息。然后,服务器使用自己的Change Cipher Spec和Finished消息作为响应,中间人(MITM)将其拦截,重新加密并转发给客户端。客户端接受这些消息,从而使用可公开计算的密码与服务器建立TLS会话,攻击者可利用该密码解密数据或注入有效的加密数据流,进行会话劫持。
注意:在上述攻击情形中,中间人(MITM)攻击者需要使用弱密码对通常为纯文本消息(例如Server Hello Done,Client Key Exchange等)进行加密,然后再将它们从一端转移到另一端。
漏洞触发条件
- 目标客户端和服务器必须已安装并正在运行该产品的易受攻击的版本。
- 攻击者必须能够拦截数据包并将其注入目标主机之间的传输层连接中。
中间攻击者的人将来自客户端的Client Hello数据包转发到服务器。 然后,攻击者从服务器截获Server Hello,Certificate和Server Hello Done消息,并在转发Server Hello之后,在Server Certificate和Server Hello Done消息之前,发送恶意的Change Cipher Spec记录。 然后,攻击者拦截客户端的Client Key Exchange,Change Cipher Spec和Finished消息,然后向服务器发送恶意的Change Cipher Spec记录,然后发送Client Key Exchange和Finished消息。 然后,攻击者拦截并加密服务器的Change Cipher Spec和Finished消息,发送到客户端,从而成功建立了具有弱密码的TLS会话。
漏洞触发流程
客户端向服务器发送Client Hello。 中间人(MITM)将其转发到服务器:
1 | [Client] ------------> [MITM] -----------> [Server] |
服务器向中间人(MITM)发送Server Hello,Certificate 和 Server Hello Done:
1 | [Client] [MITM] <----------- [Server] |
中间人(MITM)转发Server Hello,注入Change Cipher Spec,并将服务器Certificate和Server Hello Done发送给客户端:
1 | [Client] <------------ [MITM] [Server] |
客户端向中间人(MITM)发送 Client Key Exchange,Change Cipher Spec和Finished消息:
1 | [Client] ------------> [MITM] [Server] |
中间人(MITM)将Change Cipher Spec,Client Key Exchange和Finished消息发送到服务器
1 | [Client] [MITM] ------------> [Server] |
服务器发送其Change Cipher Spec和Finished消息,而中间人(MITM)会将其加密并发送给客户端:
1 | [Client] <------------ [MITM] <------------ [Server] |
数据包分析
230为客户端,206为中间人,164为服务器,整个中间人劫持流程配合数据包分析如下:
客户端(230)发送Client Hello。 中间人(206)截获了它;
中间人(206)将Client Hello转发到服务器(164);
服务器(164)发送Serve Hello, Certificate和Server Hello Done响应, 中间人(206)截获了它;
中间人(206)将Server Hello,恶意Change Cipher Spec和(加密的) Certificate以及(加密的)Server Hello Done发送给客户端(230):
客户端(230)发送Client Key Exchange,Change Cipher Spec和(加密的)Finished消息,中间人(206)截获了它。
中间人(206)发送恶意的Change Cipher Spec,然后发送(加密的)Client Key Exchange和(加密的)Finished消息:
服务器(164)以Change Cipher Spec和(已加密)Finished消息作为响应,中间人(206)截获了它:
中间人(206)向客户端(230)发送(加密的)Change Cipher Spec和(加密的)Finished消息。
整个劫持流程结束。
分析客户端(230)与中间人(206)的通信。
客户端(230)发送Client Hello给中间人(206);
中间人(206)将Server Hello,恶意Change Cipher Spec和(加密的) Certificate以及(加密的)Server Hello Done发送给客户端(230);
客户端(230)发送Client Key Exchange,Change Cipher Spec和(加密的)Finished消息给中间人(206);
中间人(206)向客户端(230)发送(加密的)Change Cipher Spec和(加密的)Finished消息;
之后中间人(206)开始发送Application Data给客户端(230)。
下图是客户端(230)与中间人(206)通信与正常的SSL协议握手过程的对比图。
明显可以发现,客户端(230)与中间人(206)的通信中缺少服务器发送的Certificate和Server Key Exchange和Server Hello Done的消息。因为中间人(206)截获消息向服务器发送了恶意Change Cipher Spec在Server Hello之后,在服务器Certificate和Server Hello Done消息之前。所以无需Server Key Exchange,让服务器强制使用空密钥生成之后的对称密钥。而且,中间人(206)已经使用Client Hello和Server Hello的随机值与空预主密钥生成之后的对称密钥,来加密 Certificate和Server Hello Done并发送给客户端(230)。所以,在数据包中体现为Encrypted Handshake Message类型消息。
分析中间人(206)与服务器(164)的通信。
中间人(206)将Client Hello转发到服务器(164);
服务器(164)发送Serve Hello, Certificate和Server Hello Done响应给中间人(206);
中间人(206)发送恶意的Change Cipher Spec,然后发送(加密的)Client Key Exchange和(加密的)Finished消息;
服务器(164)以Change Cipher Spec和(已加密)Finished消息作为响应给中间人(206);
之后服务器(164)开始发送Application Data给中间人(206)。
下图是中间人(206)与服务器(164)的通信与正常的SSL协议握手过程的对比图。
明显可以发现,中间人(206)与服务器(164)的通信缺少客户端发送的Client Key Exchange的消息。因为中间人(206)截获消息向服务器发(164)送了恶意Change Cipher Spec,中间人(206)只需Client Hello和Server Hello的随机值与空预主密钥生成之后的对称密钥。利用生成的对称密钥加密发送Client Key Exchange和客户端(203)的Finished消息。在数据包中体现为Encrypted Handshake Message类型消息。
漏洞分析
OpenSSL中存在一个安全绕过漏洞。该漏洞存在于处理OpenSSL客户端和服务端上的Change Cipher Spec消息。
握手协议作为SSL的一个子协议,在实现过程中是一个状态机。但握手协议中Change Cipher Spec(更改密钥规格)并不是一个握手消息,而是一个独立的TLS子协议类型。对此做法的官方解释是为了避免流程推迟。该漏洞的发现者Masashi Kikuchi认为这种设计是漏洞成因之一。
在整个握手协议中,也没有强制对Change Cipher Spec的发送顺序做任何校验,那么中间人攻击者有机会在握手过程中的任何时机插入Change Cipher Spec消息。一种可能的攻击方法就是,在计算出master、生成对称密钥之前,就插入Change Cipher Spec消息。这将导致消息接收方以全空的主密钥生成对称密钥。那么中间人有机会完全窃听通信过程中的加密数据。
ssl3_read_bytes()用于处理传入的数据。收到Change Cipher Spec类型的数据时,ssl3_read_bytes()首先检查s-> s3-> tmp.new_cipher(用于临时存储密码参数的结构)是否为NULL,如果未设置,则设置s-> s3-> change_cipher_spec并调用ssl3_do_change_cipher_spec( )。每当接收到ChangeCipherSpec消息时,便会执行此操作。
ssl3_do_change_cipher_spec()首先检查临时密码的密钥块s->s3->tmp.key_block是否为NULL,如果是,则继续使用ssl3_generate_key_block()或tls1_generate_key_block()的setup_key_block()从主密钥,Client Hello,Server Hello的随机值计算密钥块。
这两个函数都使用s-> session-> master_key(已初始化为0)作为其主密钥,因此最终仅基于Client Hello和Server Hello随机值生成密钥块。
在正常的情况下,如此类握手,在客户端和服务器双方进行验证后,TLS握手立即终止时,Finished消息(包含已交换的所有握手消息的verify_data中包含散列)都将不一致。但是,当客户端和服务器都使用易受攻击的OpenSSL库时,它们最终将根据正确的主密钥计算并验证客户端和服务器的Finished消息的verify_data哈希。
在客户端上,这是因为在处理多个Change Cipher Spec消息时,ssl3_do_change_cipher_spec()每次都会调用final_finish_mac()来计算其Finished消息。 收到所有必要的加密信息(特别是服务器Certificate消息)后,客户端将计算其预主密钥(premaster key)和主密钥(master key)并更新其s-> session-> master_key。 任何后续的Change Cipher Spec消息都会导致使用新的主密钥(master key)为已保存的握手消息计算verify_data。
在服务器端,当从ssl3_do_change_cipher_spec()接收到Change Cipher Spec时,以及从函数ssl3_get_cert_verify()接收到客户端的Finished消息时,都调用了final_finish_mac(),该函数调用ssl3_get_message(),该函数会重新计算带有验证信息的主密钥(master key)。
由于Change Cipher Spec不是握手消息,而是具有自己的记录,因此它不参与verify_data的计算,因此可以注入到流中。如上所述,即使使用空的主密钥(master key),Finished消息中的verify_data也将排队等待验证,并且TLS会话也将使用弱主密钥(master key)来建立。
[1] http://tools.ietf.org/html/rfc4346
[2] http://tools.ietf.org/html/rfc5246