目录
早期我们在访问web时使用HTTP协议,该协议在传输数据时使用明文传输,会带来了以下风险:
-
信息窃听风险,第三方可以获取通信内容;
-
信息篡改风险,第三方可以篡改通信内容;
-
身份冒充风险,第三方可以冒充他人身份参与通信;
为了解决明文传输所带来的风险,网景公司在1994年设计了SSL(安全套接层,Secure Socket Layer)用于Web的安全传输协议。IETF将SSL进行标准化,1999年公布了第一版TLS(传输层安全,Transport Layer Security)标准。随后又公布了 RFC 5246(2008年8月)与 RFC 6176 (2011年3月),该协议在web中被广泛应用。为互联网通信,提供安全及数据完整性保障。
-
隐秘性,所有通信都通过加密后进行传播;
-
身份认证,通过证书进行认证;
-
可靠性,通过校验数据完整性维护一个可靠的安全连接;
SSL简介
首先我们来看一下SSLv3/TLS协议在TCP/IP协议栈中的位置:通常我们认为SSLv3/TLS处于传输层和应用层之间。
SSLv3/TLS通常又分为握手层和记录层:
-
Record Layer:为每条信息提供一个header和在尾部生成一个从Message Authentication Code (MAC) 得到的hash值,其中header由5 bytes组成,分别是:
-
协议说明(1bytes)
-
协议版本(2bytes)
-
长度(2bytes)
-
跟在header后面的协议信息长度不得超过16384bytes。
-
-
Handshake Protocol:开始一个安全连接握手。
经过TCP三次握手建立连接后,才开始TLS的握手:
-
ClientHello:Client端将自己的TLS协议版本,随机数,加密套件,压缩方法,随机数,SessionID(未填充)发送给Server端;
//RFC 中Random的定义
struct
{
uint32 gmt_unix_time; //格林威治时间
opaque random_bytes[28];
} Random;
//RFC 中Client Hello的定义
struct {
ProtocolVersion client_version;
Random random;
SessionID session_id;
CipherSuite cipher_suites<2..2^16-1>;
CompressionMethod compression_methods<1..2^8-1>;
} ClientHello;
-
ServerHello:Server端将选择后的SSL协议版本,压缩算法,密码套件,填充SessionID,生成的随机数等信息发送给Client端
-
ServerCertificates:Server端将自己的数字证书(包含公钥),发送给Client端。(证书需要从数字证书认证机构(CA)申请,证书是对于服务端的一种认证),若要进行更为安全的数据通信,Server端还可以向Client端发送Cerficate Request来要去客户端发送对方的证书进行合法性的认证。
-
ServerHelloDone:当完成ServerHello后,Server端会发送Server Hello Done的消息给客户端,表示ServerHello 结束了。
-
ClientKeyExchage:当Client端收到Server端的证书等信息后,会先对服务端的证书进行检查,检查证书的完整性以及证书跟服务端域名是否吻合,然后使用加密算法生成一个PreMaster Secret,并通过Server端的公钥进行加密,然后发送给Server端。
-
ClientFinishd:Client端会发送一个ChangeCipherSpec(一种协议,数据只有一字节),用于告知Server端已经切换到之前协商好的加密套件的状态,准备使用之前协商好的加密套件加密数据并进行传输了。然后使用Master Secret(通过两个随机数、PreMaster Secret和加密算法计算得出)加密一段Finish的数据传送给服务端,此数据是为了在正式传输应用数据之前对刚刚握手建立起来的加解密通道进行验证。
-
Server Finishd:Sever端在接收到Client端传过来的加密数据后,使用私钥对这段加密数据进行解密,并对数据进行验证,然后会给客户端发送一个ChangeCipherSpec,告知客户端已经切换到协商过的加密套件状态,准备使用加密套件加密数据并传输了。之后,服务端也会使用Master Secret加密一段Finish消息发送给客户端,以验证之前通过握手建立起来的加解密通道是否成功。
根据之前的握手信息,如果客户端和服务端都能对Finish信息进行正常加解密且消息正确的被验证,则说明握手通道已经建立成功。
接下来,双方所有的通信数据都通过Master Secret进行加密后传输。
DTLS-基于UDP的TLS
SSL/TLS协议并不能用于UDP协议,而UDP也有安全传输的需求,于是产生了DTLS协议(Datagram TLS)。
DTLS协议在UDP提供的socket之上实现了客户机与服务器双方的握手连接,并且在握手过程中通过使用PSK或ECC实现了加密,并且利用cookie验证机制和证书实现了通信双方的身份认证,并且用在报文段头部加上序号,缓存乱序到达的报文段和重传机制实现了可靠传送。
记录层
同TLS1.1非常相似。唯一的改变是在记录中包含了显式序列号,显式序列号允许接收方正确验证TLS MAC。DTLS记录格式如下所示:
struct {
ContentType type;
ProtocolVersion version;
uint16 epoch; // New field
uint48 sequence_number; // New field
uint16 length;
opaque fragment[DTLSPlaintext.length];
}DTLSPlaintext;
-
version:DTLS1.0中,version值为{254,255}。该值是DTLS1.0的版本号的补数。这样,就最大化了TLS和DTLS之间的版本号,能很好的区分协议的不同版本。
-
epoch:一个计数值。epoch初始值为0,随着每次发出ChangeCipherSpec消息而递增。为了保证任意给定的 sequence/epoch 对都是唯一的
-
sequence_number:该记录的序列号。 使用显式而不是隐式序列号。同TLS相同,每次发送ChangeCipherSpec消息之后,序列号置0。
-
length:同TLS1.1中的相同。length值不能超过2^14。
传输层映射
每个DTLS记录必须在一个单独的数据报文内。为了避免IP分片,DTLS实现必须检测MTU,发送比MTU小的记录。DTLS实现必须提供一个方法俩检测PMTU(或者最大的应用程序数据包值,即是DTLS载荷的最大值)的值。如果应用程序试图发送一个长度超过MTU的的记录,DTLS实现应该能够产生一个错误,避免该数据包被分片。
多个DTLS记录可能被放置在一个数据报之内。他们简单的被连续编码,DTLS记录桢能够确定他们之间的边界。注意,数据报载荷的首个字节必须在记录的开始。记录不能越过数据报。
使用epoch+sequence_number(64bit)作为序列号计算MAC值;若MAC出错,只是简单丢弃出错包并继续维持连接(TLS中会关闭连接)。
使用序列号来提供重放攻击保护,序列号的验证需要采用滑动窗口(最小的滑动窗口大小为32,但是64是首选,初始化的时候设置成64)机制来实现。
DTLS修改了TLS1.1的协商报头:
struct {
HandshakeType msg_type;
uint24 length;
uint16 message_seq; // New field
uint24 fragment_offset; // New field
uint24 fragment_length; // New field
select (HandshakeType) {
case hello_request: HelloRequest;
case client_hello: ClientHello;
case hello_verify_request: HelloVerifyRequest; // New type
case server_hello: ServerHello;
case certificate:Certificate;
case server_key_exchange: ServerKeyExchange;
case certificate_request: CertificateRequest;
case server_hello_done:ServerHelloDone;
case certificate_verify: CertificateVerify;
case client_key_exchange: ClientKeyExchange;
case finished:Finished;
} body;
} Handshake;
协商流程划分成不同的阶段,每个阶段都有超时和重传机制。超时重传机制采样状态机,其有三个基础状态:
-
准备状态:在准备状态,为下一步的计算阶段做必要的准备,如初始化缓冲区。
-
发送状态:在发送状态,传输并且缓存消息,一旦握手过程中的最后一个阶段的消息发送成功,就进入结束状态。如果希望接收更过的消息,就设置重传时间并且进入等待状态。
-
等待状态:有三种退出等待状态的方式。
-
重传定时器到期。进入发送状态;重新传输发送本阶段的消息,重新设置重传定时器,并且返回等待状态。
-
接收到了对方发送的重传阶段消息。进入发送状态,重新传输发送本阶段的消息,重新设置重传定时器,并且返回等待状态。这里因为对方的定时器超时了,建议重新发送。
-
接收到了希望接收的下一个阶段的消息。如果这是最后一个阶段的消息,则进入到结束状态。如果需要发送下一个阶段的消息,则进入到准备状态。
-
定时器的值初始时设为1秒,每次重传就加倍,直到不超过最大值60秒。应当保存当前的定时器的值直到丢包,因为这个时候会重新设置定时器的值。重新协商时,当大量数据传输完之后,将定时器的值设置成初始值。