TLS是一种密码学协议,用于保证两者之间的会话安全。整个SSL/TLS协议内容太多,本文只从握手,加密,套件等方面对TLS 1.2进行简要总结。
协议详细内容,可以参见:
- RFC:http://tools.ietf.org/html/rfc5246
- CLOUDFLARE:https://www.cloudflare.com/learning/ssl/what-is-ssl/
握手是TLS协议中最精密复杂的部分。在这个过程中,通信双方协商连接参数,并且完成身份验证。根据使用的功能的不同,整个过程通常需要交换6~10条消息。根据配置和支持的协议扩展的不同,交换过程可能有许多变种。在使用中经常可以观察到以下三种流程:
- 完整的握手,对服务器进行身份验证;
- 恢复之前的会话采用的简短握手;
- 对客户端和服务器都进行身份验证的握手。
也可以参考cloudflare的流程:https://www.cloudflare.com/learning/ssl/keyless-ssl/
上图的流程大概可总结为:
- 客户端开始新的握手,并将自身支持的功能提交给服务器。
- 服务器选择连接参数。
- 服务器发送其证书链。
- 根据选择的密钥交换方式,服务器发送生成主密钥的额外信息。
- 服务器通知自己完成了协商过程。
- 客户端发送生成主密钥所需的额外信息。
- 客户端切换加密方式并通知服务器。
- 客户端计算发送和接收到的握手消息的MAC并发送。
- 服务器切换加密方式并通知客户端。
- 服务器计算发送和接收到的握手消息的MAC并发送。
TLS/SSL使用非对称加密提供身份认证和密钥协商(RSA、DH、ECC),使用对称加密完成信息加密(AES、DES、RC4),使用散列算法提供完整校验(MD5、SHA)
接下来将用Wireshark来分析一次真实的握手过程
ClientHello为握手的第一条消息,发送的内容有:
- TLS版本
- Handshake Type
- 随机数:总共包含32字节的数据。只有28字节是随机生成的,剩余的4字
节包含额外的信息,受客户端时钟的影响。这里标记为random_c - Session ID:在第一次连接时,会话ID( session D)字段是空的,这表示客户端并不希望恢复某个已存在的会话。在后续的连接中,这个字段可以保存会话的唯一标识。服务器可以借助会话ID在自己的缓存中找到对应的会话状态
- 支持的密码套件cipher suites:客户端支持的所有密码套件组成的列表,该列表是按优先
级顺序排列的 - 压缩方法:客户端可以提交一个或多个支持压缩的方法。默认的压缩方法是null,代表没有压缩
- Extensions:扩展( extension)块由任意数量的扩展组成。这些扩展会携带额外数据。
该部分是服务器返回给客户端的证书链,如果你访问百度,会发现会有两个证书且按照等级高的在前的顺序列出来,由于样例中的证书是自己签发的且不涉及CA,所以只有一个证书。
服务器必须保证它发送的证书与选择的算法套件一致。因为并非所有套件都使用身份验证,也并非所有身份验证方法都需要证书,所以该消息是可选的。
该消息的目的是携带密钥交换的额外数据,消息内容对于不同的协商算法套
件都会存在差异。由于这里采用的是ECDHE算法,类似DH算法,所以需要密钥交换阶段,RSA算法是没有这步的。
本例中的是DH算法的的秘钥
ChangeCipherSpec不属于握手消息,它是另一种协议,只有一条消息,作为它的子协议进行实现
会话标识符,解决会话缓存问题,这些数据采用一个只有服务器知道的密钥进行加密。Session Ticket由客户端进行存储,并可以在随后的一次会话中添加到 ClientHello消息的SessionTicket扩展中
与客户端发送的意义类似,代表服务器端协商的结束,与加密的开始。
完整的握手协议非常复杂,需要很多握手消息和两次网络往返才能开始发送客户端应用数据。会话的恢复机制可以减少网络的交互以及计算秘钥的资源开销。话恢复机制为:在一次完整协商的连接断开时,客户端和服务器都会将会话的安全参数保存一段时间。
- 服务器希望恢复:服务器在ServerHello消息中将会话ID发回客户端。
- 客户端希望恢复:客户端将会话ID放入ClientHello消息,然后提交。
服务器如果愿意恢复会话,就将相同的会话ID放入ServerHello消息返回,接着使用之前协商的主密钥生成一套新的密钥,再切换到加密模式,发送Finished消息。客户端收到会话已恢复的消息以后,也进行相同的操作。这样的结果是握手只需要一次网络往返。
在Wireshark中的流程为:
密钥交换的主要目的是计算预主密钥( premaster secret)。这个值是组成主密钥的来源。
由客户端生成一个随机值作为预主密钥,并以服务器公钥加密,将其包含在ClientKeyExchange消息中,最后发送出去。服务器只需要解密这条消息
就能取出预主密钥。
优点:简单,快速
缺点:
- 不支持向前保密
- 用于加密预主密钥的服务器公钥,一般会保持多
年不变。如果可以得到私钥,就可以恢复预主密钥,并构建相同的主密钥
临时椭圆曲线Diffie-Hellman( ephemeral elliptic curve Diffie-Hellman, ECDHE)密钥交换建立在椭圆曲线加密的基础之上,其有速度快且向前兼容的优点。在TLS中, ECDHE可以与RSA或者ECDSA身份验证
一起使用。
现在就可以计算预主密钥。
身份验证的目的是来验证双方的身份,基础是证书包含的公钥密码(最常见的是RSA,有时也用ECDSA)。不同的秘钥交换算法,验证的方式存在一点差异:
- RSA秘钥交换:在RSA密钥交换的过程中,客户端生成一个随机值作为预主密钥,并以服务器公钥加密后发送出去,只有拥有证书对应私钥的服务器才能解开预主密钥
- ECDHE秘钥交换:服务器使用证书对应的私钥对算法参数进行加密,客户端收到参数使用证书的公钥进行解密。
加密算法主要用来对应用数据进行加密,常见的算法有:3DES、 AES、 ARIA、 CAMELLIA、 RC4。目前最为广泛的是AES
TLS支持的加密类型包括以下三种:
- 序列加密
- 分组加密:
- 以验证加密
已验证的密码将加密和完整性验证合二为一,全名是使用关联数据的已验证加密。已验证加密被认为是当前TLS中可用的加密模式中最好的一种,因为它可以避免MAC-then-
encrypt方式带来的问题。
虽然TLS当前定义了基于GCM和CCM块模式的已验证套件,实际上仅支持GCM套件。
大部分TLS连接都以握手作为起点,经过应用数据的交换,最后关闭会话。但如果请求重新协 商,就会发起一次新的握手,对新的连接安全参数达成一致。该功能在以下场景很有用:
- 客户端证书:客户端证书并不常用,但因为它可以提供双因素身份验证,所以还是有一些网站在使用它
- 改变加密强度: 当网站加密刚刚出现(而且是CPU密集的)的时候,经常可以发现网站将加密配成两个级别。你可以默认使用比较弱的加密,而在特定区域使用强加密。当尝试进入网站中更安全的子区域时,服务器将请求更强的安全性。
警报的目的是以简单的通知机制告知对端通信出现异常状况。它通常会携带close_notify异常,在连接关闭时使用,报告错误。警报非常简单,只有两个字段:
- level:表示警报的严重程度,可取值warning或者fatal
- description:表示警报代码
在TLS中,伪随机函数(pseudorandom function,PRF)用于生成任意数量的伪随机数据,其中主密钥,最终秘钥都依赖伪随机函数
密钥交换过程的输出是预主密钥。不同的秘钥交换算法采取的方式有所不同:
-
RSA:RSA密钥交换的过程十分直接。客户端生成预主密钥(46字节随机数),使用服务器公钥对其加密,将其包含在ClientKeyExchange消息发送出去
-
ECDHE:使用客户端以及服务器端的dh参数生成预主秘钥
对预主密钥进行进一步加工,就是使用PRF生成48字节(384位)主密钥:
由于PRF输出的长度是固定的,所以即使预主密钥长度可能不同,最终结果的长度也是一致的。
同时,因为客户端和服务器的随机字段被用作种子,所以主密钥实际上也是随机的.
最终用来加密数据的秘钥生成方式如下:
key_block = PRF(master_secret, “key expansion”, server_random + client_random)
由于生成key_block的参数,在server以及client上都是一致的,所以两方生成的key_block也是一致的。
key_block块分为六个密钥:
- 两个MAC密钥:已验证加密的套件不使用MAC密钥,例如GCM。
- 两个加密密钥:加密数据
- 两个初始向量:只在必要时生成;序列密码不会使用IV。
密码套件并未完全掌控其安全参数。它们只是定义了最关键的身份验证和密钥交换算法,而 对这些算法的实际参数并没有控制能力(比如密钥和参数强度)。
对于身份验证,其强度主要依靠证书,更确切地说是证书中的密钥长度和签名算法。RSA密 钥交换的强度也依赖证书。可以为DHE和ECDHE密钥交换配置不同的强度,这通常是在服务器 级别的配置中完成的