TLS/SSL 握手协议学习

最近必须要了解一下https通信过程中,tls握手到底是怎么样的,这里记录一下学习过程

TLS/SSL握手

简介

自从进入了https时代,网络通信终于也不用再裸奔了。实现这一切的就是TLS协议,TLS(Transport Layer Security)以及老版本SSL(Secure Sockets Layer),是一个应用层的协议,这个协议会帮助通信的双方进行一次密钥协商,并且对之后的通信内容进行加密。这个协议广泛的应用于各种应用上,包括web浏览器,邮件,各类即时通讯等。TLS保护的是服务器与客户端通信过程中的流量

协议过程

TLS协议的主要目标是在两个通信应用程序之间提供隐私和数据完整性。该协议由两层组成:TLS记录协议 Record ProtocolTLS握手协议 Handshake ProtocolTLS记录协议位于最底层,位于某些可靠的传输协议(例如TCP )之上。 TLS记录协议提供了具有两个基本属性的连接安全性:

  • 连接是私有的(private)。 对称加密用于数据加密(例如AES,RC4等)。 此对称加密的密钥是为每个连接唯一生成的,并且基于由另一个协议(例如TLS握手协议)协商的机密。 记录协议也可以不加密使用。
  • 链接是可依靠的(reliable)。消息传输包括使用密钥MAC的消息完整性检查。例如SHA1之类的哈希函数用于MAC的计算。记录协议可以在没有MAC的情况下运行,但是通常仅在该模式下使用,而另一协议使用记录协议作为协商安全参数的传输方式。

TLS记录协议用于封装各种更高级别的协议。 TLS握手协议就是这样一种这样的封装协议,允许服务器和客户端进行身份验证,并在应用协议发送或接收其第一字节数据之前协商加密算法和加密密钥。 TLS握手协议提供具有三个基本属性的连接安全性:

  • 可以使用非对称身份验证对等方的身份,或者使用公钥算法来验证。验证过程实际上是可u俺的,但是一般情况下至少有一方会被要求验证(一般是验证服务器)
  • 共享密钥的协商是安全的:协商者通常无法获得协商的密钥(某些情况下除外),并且在加密的各条链接上都是无法获取的。甚至于说攻击者如果将自己放入协商后的连接中,依然也不可获取密钥
  • 协商是可依靠的:没有一个攻击者可以不被感知的修改协商的内容

TLS的一个优点是它与应用程序协议无关。高层协议可以透明地在TLS协议之上分层。 但是,TLS标准未指定协议如何通过TLS添加安全性; 有关如何启动TLS握手以及如何解释交换的认证证书的决定,由运行在TLS之上的协议的设计者和实现者来决定。

TLS目标

TLS协议的目标按优先级顺序如下:

  • 1.加密安全性:应该使用TLS在两方之间建立安全连接。
  • 2.互操作性:独立的程序员应该能够使用TLS开发应用程序,这些应用程序可以成功交换密码参数而无需了解彼此的代码。
  • 3.可扩展性:TLS寻求提供一个框架,可以在必要时将新的公共密钥和批量加密方法并入其中。这还将实现两个子目标:避免创建新协议的需要(并冒引入可能的新弱点的风险),以及避免实现整个新的安全性库的需要。
  • 4.相对效率:加密操作通常会占用大量CPU资源,尤其是公钥操作。因此,TLS协议已合并了可选的会话缓存方案,以减少需要从头开始建立的连接数。此外,已采取措施减少网络活动。

一些细节

  • 在流数据中,大部分的数据都是大端的
  • 可选的数据用[[]]双中括号括起来
  • 如果有未解释的单字节对象,定义为不透明类型(opaque)

在这篇文档中,我们将一些具有类似含义的数据流定义为向量(也就是通常意义上的数组)。这些向量的大小通常以如下的形式声明:

1
T T'[n]

这里我们定义了一个T类型的向量T’,T’总共占了n个字节

在之后的文章中我们协定,当定义数据的时候,那个数组的大小定义的实际上是bytes的数量,而非元素的个数,这点和通常的C语言定义不同,例如:

1
2
3
4

opaque Datum[3]; /* three uninterpreted bytes */
Datum Data[9]; /* 3 consecutive 3 byte vectors */

如上,Datum占用了三个字节,并且这里协议并未解释其作用,这边的Data总共占用了9个字节,实际上是三个Datum数据。

可变长度向量是通过指定有效长度的子范围(包括端值)来定义的,包括符号<floor..ceiling>。 对它们进行编码时,实际长度在字节流中位于矢量内容之前。 该长度将采用数字形式,该数字消耗保持矢量指定的最大(上限)长度所需的字节数。 实际长度字段为零的可变长度向量称为空向量。

1
T T'<floor..ceiling>;

在下面的示例中,mandatory是一个向量,必须包含300到400个不透明类型的字节。 它永远不能为空。实际长度字段消耗两个字节uint16,这足以表示值400(请参见第4.4节)。 另一方面,更长的时间可以表示最多800个字节的数据或400个uint16元素,并且它可能为空。 它的编码将包括在向量之前的两字节实际长度字段。 编码向量的长度必须是单个元素长度的偶数倍(例如,uint16的17字节向量是非法的)。

1
2
3
4
5

opaque mandatory<300..400>;
/* length field is 2 bytes, cannot be empty */
uint16 longer<0..800>;
/* zero to 400 16-bit unsigned integers */