前言

这里来对TLS1.2做一个基本的分析,研究,主要从设计目标,基本架构,核心要素来进行分析,然后协议主要内容是关于Handshake层和Record层。

正文

设计目标

  • 安全性:机密性(公钥协商和对称加密),身份认证(例如证书),完整性(例如mac)
  • 可拓展性:可以替换密码组件;拥有拓展功能
  • 性能:冷热启动,会话恢复

基本架构

分为五种协议,

  • handshake:生成对称加密所需的参数,身份认证,会话恢复等功能
  • record:利用handshake生成的参数进行分段,压缩,加密,mac,再发送
  • alert:通知返回码
  • changecipher spec:通知对端协议已经切换,冗余1.3删除
  • application data:将上层http等协议数据传入record层做处理

最主要的是handshake和record,然后record层封装其他四个协议

密码学相关

密码组件

主要涉及到三大类的密码组件

  • 1.对称加密传输组件,例如aes-128-gcm
  • 2.认证密钥协商组件,例如rsa-ecdhe;
  • 3.密钥扩展组件,例如TLS-PRF-sha256

这三类组件又可以划分为五种密码相关

  • authentication (认证算法)
  • encryption (加密算法 )
  • message authentication code (消息认证码算法 简称MAC,在aead模式下不要mac)
  • key exchange (密钥交换算法)
  • key derivation function (密钥衍生算法)

eg: ECDHE-RSA-AES-128-GCM-SHA256

使用ecdhe做handshake时密钥交换,rsa做认证,aes-128-gcm做为record层的加密算法,然后因为gcm是aead模式不需要单独的mac所以没有mac算法,然后sha256作为密钥衍生算法的选择,从handshake协商出来的参数衍生出record所需要的参数

前向安全性

秘钥泄露后对之前的历史信息不会有影响,RSA和静态DH不满足前向安全,DHE和ECDHE支持前向安全

接下来主要说下record层和handshake层

Record层

流程大致是:分段,压缩(可选,安全问题一般不开启),加密和mac(三种模式,stream ,block,aead),添加消息头。

tls机密性,完整性和防重放就是在这一层实现的。

在handshake层会协商后会得到以下参数:

      struct {
          ConnectionEnd          entity;
          PRFAlgorithm           prf_algorithm;
          BulkCipherAlgorithm    bulk_cipher_algorithm;
          CipherType             cipher_type;
          uint8                  enc_key_length;
          uint8                  block_length;
          uint8                  fixed_iv_length;
          uint8                  record_iv_length;
          MACAlgorithm           mac_algorithm;  /*mac 算法*/
          uint8                  mac_length;     /*mac 值的长度*/
          uint8                  mac_key_length; /*mac 算法密钥的长度*/
          CompressionMethod      compression_algorithm;
          opaque                 master_secret[48];
          opaque                 client_random[32];
          opaque                 server_random[32];
      } SecurityParameters;

client和server利用上述参数和PRF函数生成下列的加密key,mac key和iv,根据具体算法不同是可选的,注意的是client和server两端的key是不同的,会存在安全问题

client write MAC key*
server write MAC key*
client write encryption key
server write encryption key
client write IV
server write IV
*表示可选,AEAD模式无需MAC

分层和压缩暂且不看,我们重点关注加密和Mac这一步骤

压缩后得到的数据结构是这样的

 struct {
          ContentType type;
          ProtocolVersion version;
          uint16 length;
          select (SecurityParameters.cipher_type) {
              case stream: GenericStreamCipher;
              case block:  GenericBlockCipher;
              case aead:   GenericAEADCipher;
          } fragment;
      } TLSCiphertext;

三种加密mac方式

  1. stream

    先计算mac: seq_num防重放,type,version,length,fragment避免数据被篡改

           MAC(MAC_write_key, seq_num +
                               TLSCompressed.type +
                               TLSCompressed.version +
                               TLSCompressed.length +
                               TLSCompressed.fragment);

    再加密得到的结构为

    stream-ciphered struct {
             opaque content[TLSCompressed.length];
             opaque MAC[SecurityParameters.mac_length];
         } GenericStreamCipher;
  2. block

    先mac

           MAC(MAC_write_key, seq_num +
                               TLSCompressed.type +
                               TLSCompressed.version +
                               TLSCompressed.length +
                               TLSCompressed.fragment);

    再加密得到的结构为

      struct {
             opaque IV[SecurityParameters.record_iv_length];
             block-ciphered struct {
                 opaque content[TLSCompressed.length];
                 opaque MAC[SecurityParameters.mac_length];
                 uint8 padding[GenericBlockCipher.padding_length];
                 uint8 padding_length;
             };
         } GenericBlockCipher;

    注意IV向量需要时随机的,否则会有安全问题

  3. AEAD

    不用mac,

    AEAD 模式加密密码套件
    GCMAES-128-GCMTLS_DHE_RSA_WITH_AES_128_GCM_SHA256
    CCMAES-128-CCMTLS_RSA_WITH_AES_128_CCM
    ChaCha20-Poly1305ChaCha20-Poly1305ECDHE-ECDSA-CHACHA20-POLY1305

    输入为秘钥,nonce,明文fragment,additional data

    nonce由密码族指定方式生成

    additional data为

    additional_data = seq_num + TLSCompressed.type +
                           TLSCompressed.version + TLSCompressed.length;

    计算过程为

    AEADEncrypted = AEAD-Encrypt(write_key, nonce, plaintext,
                                      additional_data)

    最后的输出为

      struct {
            opaque nonce_explicit[SecurityParameters.record_iv_length];
            aead-ciphered struct {
                opaque content[TLSCompressed.length];
            };
         } GenericAEADCipher;

以上三种加密和mac后最后添加消息头

ContentType type;
ProtocolVersion version;
uint16 length;

Handshake层

这一层的主要目的是:

  1. 生成提供给Record层的SecurityParameters
  2. 身份认证(一般是客户端认证服务器的身份)

然后又分为冷启动,热启动(session_id或者session_ticket)

我们先看下冷启动的流程图:

      Client                                               Server

      ClientHello                  -------->
                                                      ServerHello
                                                     Certificate*
                                               ServerKeyExchange*
                                              CertificateRequest*
                                   <--------      ServerHelloDone
      Certificate*
      ClientKeyExchange
      CertificateVerify*
      [ChangeCipherSpec]
      Finished                     -------->
                                               [ChangeCipherSpec]
                                   <--------             Finished
      Application Data             <------->     Application Data
      *表示可选发送
      []表示独立消息

ClientHello:

         struct {
             uint32 gmt_unix_time;
             opaque random_bytes[28];
         } Random;

      struct {
          ProtocolVersion client_version;
          Random random;
          SessionID session_id;
          CipherSuite cipher_suites<2..2^16-2>;
          CompressionMethod compression_methods<1..2^8-1>;
          select (extensions_present) {
              case false:
                  struct {};
              case true:
                  Extension extensions<0..2^16-1>;
          };
      } ClientHello;       

携带版本号,随机数(PRF生成master secret,session key,校验,用于防重放),session_id(会话恢复),cipher_suites(密码组件),压缩方法,以及拓展模块

ServerHello:

     struct {
          ProtocolVersion server_version;
          Random random;
          SessionID session_id;
          CipherSuite cipher_suite;
          CompressionMethod compression_method;
          select (extensions_present) {
              case false:
                  struct {};
              case true:
                  Extension extensions<0..2^16-1>;
          };
      } ServerHello;

确认版本信息,选择密码组件,回复服务端随机值,session_id会话恢复选择,压缩选择,拓展

ServerCertificate

服务端发送证书,证书中会包含公钥,然后公钥又会被上级CA签名认证,用来做身份认证,以及利用服务器公钥传递加密参数

ServerKeyExchange

当证书中的数据不足生成premaster secret时会发送这个信息,例如DHE,ECDHE类的,因为使用的是临时参数

Certificate Request

服务端也可以请求认证客户端

Server Hello Down

表示已经发送完服务端的消息

Client Certificate

假设client收到server certificate request请求后发送一个证书,用于传输证书链给服务端,或者使用静态DH时包含静态DH参数来计算premaster secret

Client Exchange

发送必须的参数来生成premaster secret

  • RSA:利用服务器公钥加密一段48(46随机+2client_version)位的值作为premaster secret发送给server
  • DHE,ECDHE:发送client生成的动态公钥
  • 静态DH:发送个空的

Certificate verify

对client进行显示验证,client用自己的私钥签名一段数据给服务器(消息内容为客户端发送certificate verify前,所有收到和发送的握手信息)

Finished

利用协商出来的参数生成master secret然后结合之前的握手信息,标志做PRF运算得到verify_data来做校验,防止之前的信息被篡改

struct {
    opaque verify_data[verify_data_length];
} Finished;

verify_data = 
    PRF(master_secret, finished_label, Hash(handshake_messages))
    [0..verify_data_length-1];
  • finished_label:
    对于由 Client 发送的结束消息,字符串是 "client finished"。 对于由 Server 发送的结束消息,字符串是"server finished"。

  • handshake_messages 的值包括了从 ClientHello 开始一直到(但不包括)本次Finished 消息的所有握手消息

会话恢复

session_id

      Client                                                Server

      ClientHello                   -------->
                                                       ServerHello
                                                [ChangeCipherSpec]
                                    <--------             Finished
      [ChangeCipherSpec]
      Finished                      -------->
      Application Data              <------->     Application Data

CH中包含一个session_id,然后server检查是否包含并使用,如果使用就在返回的SH中包含相同的session_id,之后就不需要certificate这些步骤了。

  • 会话恢复中保存的是master secret,并且会生成新的client random和server random,这样生成的session key就会不同,增强了安全性。
  • session_id是明文的,所以不能存储敏感信息

优点:

  • 2RTT -> 1RTT
  • 减少加密计算

缺点:

  • 分布式中的的session存储问题

session_ticket

为了解决分布式session问题,所有的状态都保存在客户端。服务器取出它的所有会话数据(状态)并进行加密 (密钥只有服务器知道),再以票证的方式发回客户端,之后客户端将加密信息返回给服务端,服务端检验完整性,并解密,恢复会话。

生产票据

      Client                                               Server

      ClientHello
      (empty SessionTicket extension)-------->
                                                      ServerHello
                                   (empty SessionTicket extension)
                                                     Certificate*
                                               ServerKeyExchange*
                                              CertificateRequest*
                                   <--------      ServerHelloDone
      Certificate*
      ClientKeyExchange
      CertificateVerify*
      [ChangeCipherSpec]
      Finished                     -------->
                                             NewSessionTicket
                                               [ChangeCipherSpec]
                                   <--------             Finished
      Application Data             <------->     Application Data

使用:

      Client                                                Server

      ClientHello
      (SessionTicket extension)     -------->
                                                       ServerHello
                                    (empty SessionTicket extension)
                                                  NewSessionTicket
                                                [ChangeCipherSpec]
                                    <--------             Finished
      [ChangeCipherSpec]
      Finished                      -------->
      Application Data              <------->     Application Data

需要支持sessionticket拓展

密钥使用

三个秘钥premaster secret,master secret,session key

利用PRF函数premaster secret生成master secret,master secret生成session key。

premaster secret生成master secret(48位)

       master_secret = PRF(pre_master_secret, "master secret",
                            ClientHello.random + ServerHello.random)
                            [0..47];

master secret生成session key的函数:

key_block = PRF(SecurityParameters.master_secret,
                "key expansion",
                SecurityParameters.server_random +
                SecurityParameters.client_random);

然后再切分key_block

然后看下PRF,利用了指定的HMAC

   P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
                             HMAC_hash(secret, A(2) + seed) +
                             HMAC_hash(secret, A(3) + seed) + ...
  A(0) = seed
  A(i) = HMAC_hash(secret, A(i-1))

所以PRF具体为:

  PRF(secret, label, seed) = P_<hash>(secret, label + seed)

总结

整个TLS1.2的大致流程就是这样的,我们可以看出其设计的整体思路围绕着预定目标,很好的展现了密码学与软件工程相结合的设计,是一个值得学习的架构设计。

参考:

冰霜之地

TLS协议分析 与 现代加密通信协议设计