tls1.2

注:本文省略了部分开发协议才涉及到的内容,如字段类型的定义以及字段长度的运算,主要聚焦理解tls协议的运作方式

tls协议包含2层协议:TLS Record 协议和TLS Handshake协议,底层采用可靠传输协议(如TCP),TLS Record协议通过如下方式实现数据的安全传输:

链路是私有的,使用对称加密方式对应用数据进行加密。对每条链路来说,对称加密使用的key是各自独立的(通过TLS Handshake协议协商得到)。Record协议也可以用于非加密场景;

链路是可靠的。使用MAC来对消息完整性进行校验,MAC使用哈希函数(如SHA-1)进行运算。Record 协议可以不使用MAC,但通常仅限于使用Record作为底层协议来传输协商使用的安全参数。

TLS Record 协议用于封装多种上层协议,如Handshake协议,用于在传输/接收应用数据前进行相互认证,并协商出加密算法以及使用的密钥。Handshake使用如下三种方式提供数据的安全传输:

对端身份可以通过非对称算法或公钥,加密(如RSA,DSA)等进行认证。该认证是可选的,但通常要求至少通过一种认证方式对对端进行认证;

协商的共享密钥的过程是安全的:窃听者无法获取协商的密钥;

协商是可靠的:攻击者无法在不被链路探测到的情况下修改协商报文。

使用TLS的好处是它与应用层协议是相互独立的,上层协议允许在TLS协议之上。TLS协议没有指出如何添加协议来对链路进行安全加固,如何初始化TLS握手以及如何使用认证的证书,这些交由TLS之上的协议设计者来实现。

5. HMAC and the Pseudorandom Function

TLS使用MAC来保证消息的合法性。本协议使用的cipher suites使用了HMAC的方式,它的实现基于hash函数,其他cipher suites可能使用其他形式的MAC。

除此之外,还需要一种方式在生成或校验key时将密钥扩展为数据块。PRF(pseudorandom function)可以通过输入密钥,种子和认证的标签之后给出任意长度的输出。PRF基于HMAC。本文档所有cipher suites使用的的PRF均采用SHA-256的哈希函数。新的cipher suites必须明确指定PRF,并使用SHA-256或更健壮的hash函数。

使用PRF首先需要定义一个扩展密钥的函数,P_hash(secret, data),该函数用于扩展密钥长度

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

A()定义如下:

A(0) = seed
A(i) = HMAC_hash(secret, A(i-1))

P_hash可以迭代多次来生成所需要的数据。如使用p_SHA256来生成80字节的数据,则需要迭代3次(32bytes*3=96bytes)来生成96字节的数据,保留前面80字节,后16字节的数据会被丢弃。

使用P_hash生成PRF的方式如下:

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

label是ASCII字符串,如字符串"slithy toves"使用时应该为ASCII:

73 6C 69 74 68 79 20 74 6F 76 65 73

6. The TLS Record Protocol

TLS Record 协议是一个层级协议,每一层消息可能包括length,description和content字段。Record协议负责将需要传输的数据分布到不同的块中,可能会对数据进行压缩,在使用MAC和加密后将结果传输。接收端接收到消息之后需要进行解密,校验和解压缩操作,然后传递到上层业务。当接收到未知的Record 类型后,必须发送unexpected_message alert。

本文档定义了4种使用record协议的协议,handshake协议,alert 协议,change cipher spec协议以及application data协议。

6.1. Connection States

TLS Record协议使用TLS connection state作为运行环境,connection state指定了压缩算法,加密算法以及MAC算法,且拥有MAC key和bulk encryption keys相关的参数。Record协议始终保持4个connection state:current read,current write以及pending read和pending write。所有的records都在current read和current write state下处理。pending state可以被TLS Handshake协议修改,而ChangeCipherSpec消息可以选择性地将pending state变为current state,此时pending state初始化为empty state。为未初始化的state配置参数并使之变为current state的方式是非法的。初始的current state没有使用加密,压缩以及MAC。

connection states用于跟踪加密状态。write key用来发送数据,read key用来接收数据。pending state用来保存新的加密key(可以由Handshake协议生成)以及初始向量,而current state则表示当前使用的key,当接受到ChangeCipherSpec消息时,会将pending state的key覆盖current state的key,这样就会使用更新的key进行数据的收发。当更多关于connection states的介绍可以参考:What are read state and write state in SSL connections

TLS Connection read 和write state的安全参数如下:

connection end:接入的实体是”client“还是”server“

PRF算法:该算法通过master secret生成keys

bulk encryption 算法:用于块加密

MAC 算法:用于消息认证

compression算法:用于数据压缩

master secret:链路两端共享48字节的密钥

client random:client提供的32字节的随机数

server random:server提供的32字节的随机数

Record层使用安全参数来生成如下6项内容:

server write MAC key
client write encryption key
server write encryption key
client write IV
server write IV

server在接收和处理record的时候使用client write参数,反之亦然。

一旦设定安全参数并生成key之后,即可以通过将状态变为current state来实例化connection state。这些current state必须同时更新到每个被处理的record。每个connection state包含如下元素:

compression state:current state的压缩算法

cipher state:current state的加密算法,包含了该connection需要调度的key

MAC key:connection的MAC key

sequence number:每个connection state都包含一个序列号,且分别由read和write state维护。当一个connection state变为active state时必须置为0。序列号会在每个record中递增。

6.2. Record Layer

6.2.1. Fragmentation

Record层将消息块分割为TLSPlaintext类型的record(长度小于2^14),相同ContentType的client消息可能合并到一个TLSPlaintext record中,一个client消息也可能分布到多个record中。
enum {
  change_cipher_spec(20), alert(21), handshake(22),
  application_data(23), (255)
} ContentType;

struct {
  ContentType type;
  ProtocolVersion version;
  uint16 length;
  opaque fragment[TLSPlaintext.length];
} TLSPlaintext;

type:上层协议的类型,用于处理封装fragment

version:协议采用的版本,本文档值为{ 3, 3 }

length:TLSPlaintext.fragment的长度,不能大于2^14

fragment:应用数据。由上层协议处理的特定类型的数据块

不能发送0长度的Handshake fragment,alert或ChangeCipherSpec数据。但可以发送0长度的应用数据(可能用于链路探测)。在TLS Record协议中,应用数据处理的优先级很低,一般在上层协议(Handshake)处理完成后发送。

6.2.2. Record Compression and Decompression

所有的record都使用current state中定义的压缩算法进行压缩。初始的压缩算法为CompressionMethod.null(任何时候都有一个激活的压缩算法)。压缩算法将TLSPlaintext结构转换为TLSCompressed结构。当connection state变为激活态时,压缩算法会使用默认的state信息进行初始化。压缩算法不能造成数据丢失,且内容长度不能超过1024字节,当解压缩函数解压缩TLSCompressed.fragment时,如果数据大于2^14字节,必须返回解压缩失败的错误。

struct {
  ContentType type;       /* same as TLSPlaintext.type */
  ProtocolVersion version;/* same as TLSPlaintext.version */
  uint16 length;
  opaque fragment[TLSCompressed.length];
} TLSCompressed;

length:TLSCompressed.fragment的长度,不能大于2^14+1024字节

fragment:压缩TLSPlaintext.fragment之后的数据类型,如下handshake protocol作为Record协议的数据部分

解压缩函数必须保证不能造成buffer溢出

 6.2.3. Record Payload Protection

加密和MAC函数会将TLSCompressed结构转化为TLSCiphertext结构,解密函数则反转上述过程。MAC的record包含序列号,因此能够探测到重复,多余,缺失的record的场景。

TLSCiphertext中定义了流加密块加密,以及AEAD加密方式,流加密和块加密的简单理解可以参考对称加密&加密模式

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

type:和TLSCompressed.type相同

version:和TLSCompressed.version相同

length:TLSCiphertext.fragment的长度,不能超过2^14+1024

fragment:加密TLSCompressed.fragment后的类型(含MAC)

6.2.3.1. Null or Standard Stream Cipher

Stream ciphers将TLSCompressed.fragment结构转换为TLSCiphertext.fragment结构

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

MAC的生成方式如下:

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

seq_num:record的序列号

MAC:SecurityParameters.mac_algorithm知道的MAC算法

MAC必须在加密前计算出来,stream cipher用于加密整个数据块(含MAC)。如果cipher suite为TLS_NULL_WITH_NULL_NULL,加密方式则由操作类型组成(如数据没有被加密,MAC为0--即NULL的定义)。对于null和stream ciphers来说,TLSCiphertext.length =  TLSCompressed.length + SecurityParameters.mac_length

6.2.3.2. CBC Block Cipher

block cipher(如3DES或AES)使用加密和MAC函数将TLSCompressed.fragment结构转换为TLSCiphertext.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;

MAC生成方式与stream cipher相同

IV:IV(Initialization Vector)为随机数且必须为不可预测。

padding:将plantext扩展到完整的(多个)cipher block的长度,长度最大可以为255字节。padding机制可以防止基于报文长度分析进行的攻击。接收端必须检验该padding,并在校验失败时返回bad_alert_mac_alert错误。

padding_lenght:padding length必须使GenericBlockCipher 结构的长度和多个cipher block的长度相同。该长度不包括padding_length本身。

CBC(Cipher Block Chaining)模式下必须知道所有record的明文内容,否则可能遭受CBCTT攻击

6.2.3.3. AEAD Ciphers

AEAD cipher(如CCM或GCM)函数将TLSCompressed.fragment结构转换为TLSCiphertext.fragment结构

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

AEAD使用key,nonce(随机数),plaintest和"additional data"作为输入进行身份认证。key可以是client_write_key或server_write_key,但不能是MAC key。paintext为TLSCompressed.fragment。额外的认证数据也被称为additional_data,定义如下:

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

aead_output输出包括AEAD加密操作的密文,其长度通常大于TLSCompressed.length,但会随着AEAD cipher变化。由于ciphers可能会包含padding,数量可能会随着TLSCompressed.length的不同而不同。每个AEAD cipher的输出不能大于1024字节。

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

为了加密和验证,AEAD cipher会采用key,nonce和"additional_data"以及AEADEncrypted值。输出为明文或错误信息,没有完整性校验。如果解密失败,则发出bad_record_mac alert

TLSCompressed.fragment = AEAD-Decrypt(write_key, nonce,
                                    AEADEncrypted,
                                    additional_data)

6.3. Key Calculation

Record协议使用算法结合handshake协议提供的安全参数来生成current state需要的keys。master secret被分割为client write MAC key,server write MAC key,client write encryption key和server write encryption key,这4个key按照字节顺序分割,未使用的值未空。一些AEAD ciphers会使用到client write IV和server write IV

当keys和MAC keys生成后,master secret作为熵。此处使用PRF生成key

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

key_block划分如下:

client_write_MAC_key[SecurityParameters.mac_key_length]
server_write_MAC_key[SecurityParameters.mac_key_length]
client_write_key[SecurityParameters.enc_key_length]
server_write_key[SecurityParameters.enc_key_length]
client_write_IV[SecurityParameters.fixed_iv_length]
server_write_IV[SecurityParameters.fixed_iv_length]

当前client_write_IV 和server_write_IV仅用于AEAD(3.2.1章节)

master secret算法如下:

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

Record协议使用connection state来维护加密状态,它使用handshake协议来获取安全参数并生成相应的key,最后对数据进行加密传输。

7. The TLS Handshaking Protocols

TLS有3个子协议用于为record层提供安全参数,进行相互认证,初始化协商以及报告错误。

handshake协议用来协商session,包含以下项目:

session identifier:server端选择的随机序列号,用于标记激活的或是重用的session

peer certificates:对端的X509v3证书。

compression method:加密前使用的压缩算法

cipher spec:指定用于生成key的PRF,块加密算法(null,AES等)和MAC算法(如HMAC-SHA1)。

master secret:client和server的48字节的共享密钥

is resumable:用于标记该session是否能被重用

这些参数用来生成安全参数。很多链接都可以通过Handshake协议的重用特性,使用相同的session来初始化链接。

7.1. Change Cipher Spec Protocol

change cipher spec协议主要用来发送改变加密策略的信号。该协议仅包含一个消息(使用current connection state压缩并加密)。消息内容包含一个自己的内容(1)

struct {
  enum { change_cipher_spec(1), (255) } type;
} ChangeCipherSpec;

client或server通过发送ChangeCipherSpec消息来通知对端后续records将使用新协商的CipherSpec和keys加密保护。接收端接收到该消息后,会通知Record层将read pending state中的内容拷贝到read current state中。在发送完该消息之后,发送端必须将write pending state中的内容拷贝到write active state中。

协商好的安全参数会保存在write pending state中,当一端需要使用新的加密算法时,会发送ChangeCipherSpec消息,并立即将write pending state中的内容覆盖到write current state中,接收端则修改read state的内容。

ChangeCipherSpec 在握手过程中且在两端都协商好安全参数后(Finished消息前)发送。

如果在数据传输过程中出现了重握手,双方仍然可以使用就的CipherSpec。但是当收到ChangeCipherSpec消息时,必须使用新的CipherSpec。发送ChangeCipherSpec的一段并不知道对端是否已经计算出了新的keys,因此在接收端可能需要一小段时间来缓存接收到的数据。

7.2. Alert Protocol

alert表达了该消息的严重性(warning或fatal)并描述了该alert。fatal级别的alert会导致直接断开链路,在这种情况下,该session的其他链路可能会继续连接,但该session id会被标记为不可用,用来防止重建链接。跟其他消息一样,alert也会被压缩并加密。

struct {
  AlertLevel level;
  AlertDescription description;
} Alert;

7.2.1. Closure Alerts

client和server必须共享链路的终止状态,用来防止截断攻击。任何一方都可以发出初始化发送closure消息,接收到closure消息之后的所有数据都会被忽略。

close_notify:

该消息用于提示接收者,发送者后续不会在该链接上发送任何消息。除非发生fatal错误,链路的两端都应该在停止关闭链路前发送close_notify alert消息,并丢弃pending writes。

更多参见rfc2818

7.2.2. Error Alerts

TLS Handshake协议处理错误的方式很简单,当一方发现错误时,会将该消息发往对端。当发送或接收该消息时,两端必须立即断开链接,并删除所有与链接相关的session信息(keys secrets等)。任何被fatal alert关闭的链接都不能重用。

当遇到无法判定的alert级别的错误,发送端可能会选择将其作为fatal级别的错误处理。如果实现中发送alert的目的是为了关闭链接,则必须发送fatal级别的alert。

当接收到warning级别的alert,通常可以继续保持链接。作为发送端,通常无法判断接收端接收到warning级别的alert的动作,因此通常warning的alert作用不大。已经定义的alert参见Error Alert

7.3. Handshake Protocol Overview

TLS Handshake协商产生session state所需要的加密参数。当clent和server开始通信时,会协商产生版本号,加密算法(可能会进行相互认证),以及使用公钥生成共享密钥。TLS Handshake包含如下步骤:

通过交换hello消息确定加密算法,交换随机数并检查session 是否重用

交换加密参数,来确定client和server使用的premaster secret

交换证书和加密信息来对client和server进行认证

从premaster secret生成master secret并交换随机数

给Record层提供安全参数

client和server校验对端通过handshake计算出的安全参数

需要注意的是上层协议不能假设TLS可以始终提供健壮的安全链接。实际上有很多中间人攻击可以导致两端切换到它们所支持的最低级别的安全方法。TLS协议用来最小化攻击,但实际中存在很多可能的攻击方式,攻击者可能block安全服务所运行的端口,或者使得两端协商到非认证的链接。使用TLS的基本原则是上层协议必须知道所需要的安全需求,并且不能在低于安全需求的链路上传播信息。因此在使用满足需求的cipher suite前提下,可以认为是安全的。

这些通过Handshake协议来达成,基本过程如下:client发送ClientHello,server接收到后必须返回ServerHello(否则返回fatal的alert错误并断开链接)。ClietHello和ServerHello用来组建链路安全能力。ClientHello和ServerHello用来建立如下属性:Protocol Version,Session ID,Cipher Suite,以及Compression Method。初除此之外,还需要生成并交换2个随机数:ClientHello.Random和ServerHello.Random。

实际交换key需要用到4个消息:server  Certificate, ServerKeyExchange, client Certificate和ClientKeyExchange。可以通过定义这些消息的格式以及消息的用途来实现交换key的方法,最终实现client和server协商出共享密钥。该密钥必须足够长,当前的密钥交换方式交换的密钥长度在46字节以上。

hello消息之后,sever会在Certificate消息中发送其证书(当需要被认证时),除此之外,可能会发送ServerKeyExchange消息。当server端被认证后,server可能会要求对client进行认证,接下来server会发送ServerHelloDone消息来表情hello阶段的握手结束,然后sever端会等待client端的响应。如果server发送了CertificateRequest消息,client必须发送其certificate消息。此时会发送ClientKeyExchange消息,该消息的内容取决于ClientHello和ServerHello协商出的公钥算法。如果client发出了带签名的certificate,会使用数字签名证书来验证拥有certificate私钥的一方。

在client和server进行相互认证之后,client会发送ChangeCipherSpec消息,此时client会将pending state中的Cipher Spec拷贝到Current state中,然后立即使用最新的algorithms,keys和secrets发送加密的Finished消息,同时,server端会响应它的ChangeCIpherSpec消息,并将pending转变为current,使用新的CipherSpec发送Finished消息。到此为止,握手结束,后续client和server可以交换应用数据。应用数据不能先于首次握手(非TLS_NULL_WITH_NULL_NULL的cipher suite)发送。

      Client                                               Server

      ClientHello                  -------->
                                                      ServerHello
                                                     Certificate*
                                               ServerKeyExchange*
                                              CertificateRequest*
                                   <--------      ServerHelloDone
      Certificate*
      ClientKeyExchange
      CertificateVerify*
      [ChangeCipherSpec]
      Finished                     -------->
                                               [ChangeCipherSpec]
                                   <--------             Finished
      Application Data             <------->     Application Data

* 表示不一定发送的消息

ChangeCipherSpec是独立的TLS消息,不属于Handshake消息的一部分

当client和server决定重用先前的session会现有的session时,消息流程如下:

client发送ClientHello(带需要重用的session ID)。server会校验需要匹配该ID的session。如果匹配成功且server同意使用指定的session重建链接,会发送带该Session ID的ServerHello消息。此时client和server必须发送Change CIpher Spec消息并直接处理Finished消息。一旦重建成功,client和server就可以开始交换应用层数据;如果匹配失败,Sever会生成一个新的Session ID,并执行完整的握手过程。

      Client                                                Server

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

7.4. Handshake Protocol

TLS Handshake协议属于TLS Record协议的上层协议。该协议用于协商session的属性。Handshake消息在TLS record层下运行,并将消息封装成TLSPlaintext结构。

handshake协议消息的发送顺序必须遵从如下规则:乱序的handshake消息会导致fatal错误;不需要的handshake消息可以被忽略;唯一一个不需要受发送顺序限制的消息是HelloRequest,但client端进行handshake过程中受到后应该忽略该消息。

7.4.1. Hello Messages

hello阶段用来建立安全加密的能力。当新建一个session的时候,record层connection state的加密,哈希和压缩算法都初始化为null。

7.4.1.1. Hello Request

server端可以在任何时候发送HelloReques消息。

该消息用来通知client端进行协商,client会发送ClientHello消息进行响应。该消息不能用于判定哪一端开始建立链接(仅用于初始化协商)。如果client正在握手协商中或者client不同意建立链接,client会发送no_renegotiation alert。如果server发送HelloRequest并没有受到任何响应,Server可能会关闭链接并发送fatal alert。

该消息不能使用Handshake中(Finished消息和certificate消息)使用的hash。

 7.4.1.2. Client Hello

client发送ClientHello消息来初始化握手协商,ClientHello也用于响应server发送的HelloRequest消息。

ClientHello包含一个随机数的结构体。如下

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

gmt_unix_time:是标准unix 32位格式的发送端的内部时钟时间。在TLS协议中没有规定该时钟的精确度。

random_bytes:生成的28字节的安全随机数。注:随机数并不是session ID

Client Hello消息包含一个可变长度的session id。如果该字段非空,该值表示了client需要重用的session,此时session id可以来自于先前的链接(1),当前的链接(2)或其他当前的链接(3)(其他session的)。第2种用于当client仅需要更新随机数的场景;第3中用于建立独立的安全链接(而无需进行完整的握手过程)。当握手协商通过交换Finished后保存的sessionID超时,或遇到fatal错误时,sessionID会变为无效状态。Session ID的实际内容由server端定义。

由于SessionID的传输没有经过加密或MAC保护(因为此时加密参数还未协商出来),sever端不能在session id中防止敏感字段,否则可能导致安全攻击。在握手结束后的消息(含Finished消息)中的session ID会被加密保护

ClientHello中的cipher suite列表给出了client支持的加密算法,并按照client偏好排序。每个cipher suite包含一个交换算法key,块加密算法,MAC算法和PRF。sever会选择一个合适的cipher suite,如果没有合适的则会handshake failure alert并关闭链接。如果cipher suite中包含server无法识别的项,server端必须忽略这些cipher suites,并处理剩余的cipher suites。

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;

ClientHello通过判断在compression_method之后是否有多余的字节来确定是否由存在扩展字段。

server_version:client建议的最低版本且server支持的最高版本,本协议使用值为3.3。TLS1.2中如果sever不支持该版本,会返回带有低版本的ServerHello,如果client同意使用该低版本,则使用该版本进行协商,否则client返回protocol_version alert并关闭链接。如果server接收到的ClientHello携带了高于server支持的最高版本的版本,则必须返回server所支持的最高版;当server接收的ClientHello中的版本低于server所支持的最高版本,如果server同意使用该低版本,则会选择不高于ClientHello.client_version中定义的最高版本(如,server支持TLS1.0 1.1和1.2,client_version为TLS 1.0,则server会使用TLS1.0处理),反之返回protocol_version alert。

random:client生成的随机数。

session_id:非空表示重用session,空表示新建session

cipher_suite:如果session_id非空,该字段至少需要包含该session已经协商好的cipher_suite

compression_methods:client支持的压缩方法,按照偏好排序。如果session id非空,该字段至少需要包含该session已经协商好的compression_method。所有的实现必须支持CompressionMethod.null。

extensions:client可能会通过扩展字段来请求server的扩展功能。

当client使用扩展请求额外的功能,且server不支持时,client可能会断开握手。server端必须接收带或不带扩展字段的ClientHello,并解析这些扩展字段,如果发现无法解析的字段,则必须返回decode_error alert。

client发送完ClientHello后会等待server端的ServerHello,非ServerHello(HelloRequest除外)的消息会被认为fatal错误。

7.4.1.3. Server Hello

在client接收到ClientHello且能够接收其中定义的cipher suite时会发送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;

server_version:与client的定义一致

random:server端生成的随机数,独立于client的随机数

session_id:server可能会发送空的session id,表示session不会被缓存且不会被重用。

其余字段与client一致

7.4.1.4. Hello Extensions

扩展字段的格式如下:

struct {
  ExtensionType extension_type;
  opaque extension_data<0..2^16-1>;
} Extension;

enum {
  signature_algorithms(13), (65535)
} ExtensionType;

extension_type:表示特定的扩展类型

extension_data:表示特定的扩展的信息

IANA维护的扩展参见Section 12

只有在ClientHello中出现的扩展才能出现在ServerHello中。如果client接收到的ServerHello中出现了ClientHello中不相关的扩展,则client必须断开链接并发送unsupported_extension fatal alert。未来可能会实现“server-oriented”的扩展,参见Hello Extensions

当ClientHello或ServerHello消息中存在多个扩展时,扩展的可以以任意顺序存在,且不能出现同一扩展类型的多个实例。

扩展可以在初始化新的session或重用session时发送。在client请求重用session时,它并不知道server端是否会支持不同的扩展,因此这种情况下,client会发送与之前发送过的扩展。

每个扩展类型需要规定在完整的握手和session重用场景下的作用。目前大部分TLS 扩展的实现中仅关注session初始化:当重用先前session的时候,sever不会处理ClientHello中的扩展,也不会将他们保存在ServerHello中。

开发extension的注意点参见Hello Extensions

7.4.1.4.1. Signature Algorithms

client使用signature_algorithm扩展来告诉server端用于数字签名时使用的signature/hash算法对。该扩展的"extension_data‘字段包含"supported_signature_algorithms"值。

enum {
  none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5),
  sha512(6), (255)
} HashAlgorithm;

enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) }
SignatureAlgorithm;

struct {
    HashAlgorithm hash;
    SignatureAlgorithm signature;
} SignatureAndHashAlgorithm;

SignatureAndHashAlgorithm
supported_signature_algorithms<2..2^16-2>;

每个SignatureAndHashAlgorithm列表包含一个hash/signature对(不是所有的hash和signature都能配对),按照偏好排序。

hash:表明需要使用的哈希算法,如MD5,SHA-1,SHA-224,SHA-256,SHA-384,SHA-512等。“none”表示为以后扩展使用,也用于仅使用signature但不使用hash的场景。

signature:表明需要使用的签名算法,如RSASSA-PKCS1-v1_5,DSA,ECDSA等,该字段必须出现在该扩展中。

如果client仅支持默认的hash和signature算法,则可能忽略signature_algorithm扩展。如果client不支持默认算法,或支持其他hash/signature算法,则必须发送signature_algorithm扩展并列出接受的算法。

如果client没有发送signature_algorithm,则server必须:

如果协商的密钥交换算法为RSA, DHE_RSA, DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA其中之一,则认为client发送了{sha1,rsa}

如果协商的密钥交换算法为DHE_DSS, DH_DSS其中之一,则认为client发送了{sha1,dsa}

如果协商的密钥交换算法为ECDH_ECDSA, ECDHE_ECDSA其中之一,则认为client发送了{sha1,ecdsa}

TLS1.2之前的版本无法识别该扩展。

server不能发送该扩展,但server必须支持接收该扩展

当重用session时,ServerHello中不能包含该扩展,且server忽略ClientHello中的该扩展。

7.4.2. Server Certificate

当两端需要使用基于证书的认证时,server端必须发送Certificate消息。该消息总是在ServerHello中发送。

Certificate消息会发送server的证书链。证书必须符合协商出来的密钥交换算法和扩展。

opaque ASN.1Cert<1..2^24-1>;

struct {
  ASN.1Cert certificate_list<0..2^24-1>;
} Certificate;

certificate_list:证书链。发送者的证书必须排在第一个,后续的证书依此(直接)认证前面一个。因为证书认证要求root keys独立认证,自签证书中指定的root CA可能会被忽略,因此远端必须拥有自签证书的root key来进行验证。

客户端响应certificate request消息的消息类型和结构与server相同。当client没有合适的证书时,可能不会发送certificate消息。server端使用证书时需要满足如下要求:

证书必须是X509v3,除非明确协商使用其他类型的证书

证书的公钥必须能够兼任协商的密钥交换算法

密钥交换算法        证书key类型

RSA/RSA_PSK        RSA公钥,证书必须能够用于加密(当出现key esage扩展时,keyEncipherment必须置位)

DHE_RSA/ECDHE_RSA    RSA公钥,证书必须能够用于签名(当出现key esage扩展时,digitalSignature必须置位)

DHE_DSS          DSA公钥,证书必须能够用于签名(结合server key exchange消息中出现的hash算法)

DH_DSS/DH_RSA       Diffie-Hellman公钥(当出现key esage扩展时,keyAggrement必须置位)

ECDH_ECDSA        ECDH-capable公钥,该公钥必须使用client支持的curve和point格式

ECDHE_ECDSA       ECDSA-capable公钥,证书必须允许key进行签名且必须使用client支持的curve和point格式

“server_name”和“trusted_ca_keys”扩展用于知道证书选择。

如果client提供了signature_algorithm扩展,那么server提供的所有证书都必须使用该扩展中出现的hash/algorithm算法对进行签名(数字签名),这也意味着当一个证书包含某个签名算法的key时,它可能被不同的签名算法进行签名(如RSA key使用DSA key进行签名)。这也是与TLS1.1不同的地方。

如果server拥有多个证书,client会根据上述原则(除此之外,还有如本地配置和偏好)挑选其中一个进行验证;如果server只有一个证书,则必须满足所有规则。

7.4.3. Server Key Exchange Message

该消息会在server certificate之后发送,且尽在server Certificate消息不能够提供用于client交换premaster secret的数据时发送。下面密钥交换算法会发送该消息

DHE_DSS DHE_RSA DH_ann

下面交换算法不能发送该消息

RSA DH_DSS DH_RSA

该消息用来给client提供后续传输premaster  secret的加密信息。client端使用Diffie-Hellman公钥以及该消息承载的数据可以完成premaster secret交换(或其他算法功能)。

  enum { dhe_dss, dhe_rsa, dh_anon, rsa, dh_dss, dh_rsa /* may be extended, e.g., for ECDH -- see [TLSECC] */ } KeyExchangeAlgorithm;

  struct {
      opaque dh_p<1..2^16-1>;
      opaque dh_g<1..2^16-1>;
      opaque dh_Ys<1..2^16-1>;
  } ServerDHParams;     /* Ephemeral DH parameters */

  dh_p
     The prime modulus used for the Diffie-Hellman operation.

  dh_g
     The generator used for the Diffie-Hellman operation.

  dh_Ys
     The server‘s Diffie-Hellman public value (g^X mod p).

  struct {
      select (KeyExchangeAlgorithm) {
          case dh_anon:
              ServerDHParams params;
          case dhe_dss:
          case dhe_rsa:
              ServerDHParams params;
              digitally-signed struct {
                  opaque client_random[32];
                  opaque server_random[32];
                  ServerDHParams params;
              } signed_params;
          case rsa:
          case dh_dss:
          case dh_rsa:
              struct {} ;
             /* message is omitted for rsa, dh_dss, and dh_rsa */
          /* may be extended, e.g., for ECDH -- see [TLSECC] */
      };
  } ServerKeyExchange;

params:serverkey exchange的参数,如下

signed_params:用于非匿名key交换,对server交换的密钥参数的签名。可以看到,签名的内容包含client和server的随机数以及交换算法的参数

如果client提供了signature_algorithm扩展,则该消息中必须使用client的signature_algorithm扩展中出现的hash/algorithm对(下面红框中的hash/algorithm算法是client 支持的)。但在实际使用中会出现不一致的情况,如client提供了DHE_DSS密钥交换但在signature_algorithm扩展中却忽略所有的DSA。为了能够正确地协商,server必须在选择前校验所有的cipher suites与signature_algorithms(即cipher suites中的签名算法与扩展不一致时)。除此之外,hash/signal算法必须与server端的证书相匹配。

 7.4.4. Certificate Request

当非匿名server需要对client进行认证时会发送该消息,跟在ServerKeyExchange后面。

enum {
  rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),
  rsa_ephemeral_dh_RESERVED(5), dss_ephemeral_dh_RESERVED(6),
  fortezza_dms_RESERVED(20), (255)
} ClientCertificateType;

opaque DistinguishedName<1..2^16-1>;

struct {
  ClientCertificateType certificate_types<1..2^8-1>;
  SignatureAndHashAlgorithm
    supported_signature_algorithms<2^16-1>;
  DistinguishedName certificate_authorities<0..2^16-1>;
} CertificateRequest;

certificate_type:client可能会提供的证书类型

rsa_sign:包含RSA key的证书

dss_sign:包含DSA key的证书

rsa_fixed_dh:包含static DH key的证书

dss_fixed_dh:包含static DH key的证书

supported_signature_algorithms:server支持的hash/signature算法,按偏好排序

certificate_authorities:可接受的CA的distinguished names(DN),DER编码格式。这些DN字段可能会指定一个root CA或子CA期望的DN,因此该消息可以用来描述已知的根以及期望的授权空间。如果certificate_authorities列表为空,则client可能会选择发送合适的ClientCertificateType类型的证书。

certificate_types和supported_signature_algorithms字段交互比较复杂,certificate_types在SSLv3版本中出现,但仍然待确定,它的大部分功能被supported_signature_algorithms取代。规则如下:

client提供的证书必须使用supported_signature_algorithms中定义的hash/signature算法进行签名

client提供的证书必须包含于certificate_types兼容的key,如果key是一个签名key,则该key必须能配合supported_signature_algorithms中的hash/signature算法使用。

由于历史原因,一些client证书类型包含用于签名这些证书的算法。如在早期的TLS版本中,rsa_fixed_dh表示使用RSA签名的证书且该证书包含一个static DH key。在TLS 1.2中,该功能被(supported_signature_algorithms)废弃,证书类型不再限制用于签名证书的算法。如,server发送的dss_fixed_dh类型的证书,以及{{sha1, dsa}, {sha1, rsa}}的签名类型,client可能会回复带DH key的证书,使用RSA-SHA1签名。

7.4.5. Server Hello Done

server发送该消息用来表示完成了key exchange的消息的交互,client可以进入密钥交互阶段。client在接收到ServerHelloDone消息后,会对server提供的证书进行校验,如果证书校验通过则继续对server的hello参数进行校验。

7.4.6. Client Certificate

该消息时client接收到ServerHelloDone消息之后发送的第一个消息,且只在server请求证书时发送。如果没有合适的证书,client也必须发送该消息,消息中不包含任何证书,即certificate_list长度为0。如果client没有发送任何证书,server可能会握手(不对client进行认证),也可能会返回handshake_faiure fatal alert。如果证书链的某些地方不可接受(如签名的CA不可知),server可能会继续握手或返回fatal alert。

该消息会传递client的证书链,server会使用该消息来校验CertificateVertify消息或计算premaster secret(non-ephemeral Diffie- Hellman)。证书必须匹配协商的cipher suite的密钥交换算法以及扩展。

证书类型必须是X509v3(除非明确指出使用其他类型的证书)。

client证书必须于CertificateRequest中的证书类型匹配。

client证书类型            证书key类型

rsa_sign              RSA公钥,证书必须能够用于签名(结合certificates verify消息中的hashsignature算法)

dss_sign              DSA公钥,证书必须能够用于签名(结合certificates verify消息中的hashsignature算法)

ecdsa_sign             ECDSA-capable公钥,证书必须能够用于签名(结合certificates verify消息中的hashsignature算法);公钥必须使用server支持的curve和point格式

rsa_fixed_dh/dss_fixed_dh      Diffie_Hellman公钥,必须与server key的premaster相同

rsa_fixed_ecdh/ecdsa_fixed_ecdh   ECDH-capable公钥,必须与server key的curve相同,必须使用server支持的point格式

如果certificates 请求中的certificate_authorities (DN)列表非空,则CA列表中的某个CA应该签发了证书链中的某个证书。

证书必须使用可接受的hash/algorithm进行签名(与7.4.4章节相同)。

7.4.7. Client Key Exchange Message

该消息在certificate之后发送,如果没有发送certificate消息,则在ServerHelloDone之后发送。

发送该消息表示premaster secret已经生成(通过直接传输RSA加密的premaster secret或传递用于两端生成相同premaster secret的Diffie_Hellman参数)

当client使用ephemeral Diffie-Hellman exponent进行密钥交换时,该消息包含了client的Diffie-Hellman public值;如果client发送的certificate包含static DH exponent,则该消息必须发送,但必须为空

struct {
  select (KeyExchangeAlgorithm) {
      case rsa:
          EncryptedPreMasterSecret;
      case dhe_dss:
      case dhe_rsa:
      case dh_dss:
      case dh_rsa:
      case dh_anon:
          ClientDiffieHellmanPublic;
  } exchange_keys;
} ClientKeyExchange;

7.4.7.1. RSA-Encrypted Premaster Secret Message

RSA用于key协商和认证,client会生成48字节的premaster secret,并将premaster secret使用server 证书提供的公钥进行加密,最后发送到server端。该结构时ClientKeyExchange的变种,且不是一个独立的消息。

struct {
  ProtocolVersion client_version;
  opaque random[46];
} PreMasterSecret;

client_version
 The latest (newest) version supported by the client.  This is
 used to detect version rollback attacks.

random
 46 securely-generated random bytes.

struct {
  public-key-encrypted PreMasterSecret pre_master_secret;
} EncryptedPreMasterSecret;

pre_master_secret:client用于生成master secret的随机值。PreMasterSecret中的版本号由ClientHello.client_version提供,而非协商的链路版本,该设计用于防止rollback攻击。不幸的是有些老的实现会使用协商出的版本号,与这些实现交互时可能会失败。

client的实现中必须在PremasterSecret中发送正确的版本号。如果ClientHello.client_version为TLS1.1或更高,server的实现中必须检查该版本号;如果版本号为1.0或更早,server可能会检查版本号,也可能不检查。

TLS server在加密premaster secret失败或遇到非期望的版本号时不能发送alert,而应该使用一个随机生成的premaster secret进行握手。

公钥加密的数据是一个0~2^16-1长度的opaque向量,因此ClientKeyExchange中使用RSA加密的PreMasterSecret之前为2个字节的长度字段(表示其可变长度大小)。EncryptedPreMasterSecret 是ClientKeyExchange的唯一数据。

7.4.7.2. Client Diffie-Hellman Public Value

当client的certificate中没有出现Diffie-Hellman public的值时发送,该消息是ClientKeyExchange的变体。当certificate中已经包含合适的Diffie-Hellman key(用于fixed_dh client认证),此时必须于发送client key exchange消息,但必须为空

 7.4.8. Certificate Verify

该消息用于提供client证书的校验,仅在client certificate由签名功能时发送(除了包含fixed Diffie-Hellman参数的证书),跟在client key exchange 消息之后发送。

struct {
   digitally-signed struct {
       opaque handshake_messages[handshake_messages_length];
   }
} CertificateVerify;

这里的handshake_messages表示从ClientHello开始的所有接收和发送的握手消息(不包含本消息),该消息级联了所有握手消息,结构如下

struct {
  HandshakeType msg_type;    /* handshake type */
  uint24 length;             /* bytes in message */
  select (HandshakeType) {
      case hello_request:       HelloRequest;
      case client_hello:        ClientHello;
      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;

该实现会要求两端缓存该消息或计算出(到接收到CertificateVerity为止的)所有可能的hash算法。server可以通过在CertificateRequest中设置限制来减小运算。

签名的hash/signature算法必须是CertificateRequest消息中提供的。除此之外,hash/signature算法必须与client端的certificate兼容。

 7.4.9. Finished

该消息在ChangeCipherSpec消息之后发送,ChangeCipherSpec表示密钥交换和认证都已经完成。

Finish是第一个使用协商的算法保护的消息,key和secrets保护的消息,接收到该消息之后必须验证消息内容的有效性,在认证成功后就可以发送应用消息。

struct {
    opaque verify_data[verify_data_length];
} Finished;

verify_data:PRF(master_secret, finished_label, Hash(handshake_messages)) [0..verify_data_length-1];在旧版本中的TLS,verify_data为12 octets,当前版本则却决于使用的cipher suite。任何没有明确指定verify_data_length的verify_data_length默认为12 octets。

finished_label:client发送的Finished消息相关的字符串为"client finished‘,server发送的为“server finished”

Hash:表示握手消息的hash,该hash与PRF使用的hash相同。cipher suite定义的PRF必须定义用于FInished计算的Hash

handshake messages:握手过程中的所有消息(除HelloRequest外)。只包括handshake层的可见数据,不包含Record层首部。

在握手过程中,如果没有在合适的实际处理Finished消息,则返回fatal 错误。值handshake_message与7.4.8章节中定义的handshake_message不同,它包含了CertificateVerify消息, 且client的handshake_message与server的也不相同(因为发送消息的一端会包含对端已经发过来的消息)

ChangeCipherSpec,alert和其他Record类型的消息不是握手消息,且不会进行hash运算(HelloRequest也不会)

 A.5. The Cipher Suite

本节定义了ClientHello和ServerHello的cipher suite使用的cipher suite。TLS初始化的cipher suite为TLS_NULL_WITH_NULL_NULL,但不能使用该值进行协商(没有提供任何保护)

CipherSuite TLS_NULL_WITH_NULL_NULL               = { 0x00,0x00 };

如下CipherSuite需要server提供用于密钥交换的RSA证书。server可能会在CertificateReuqest中请求signature-capable证书。

CipherSuite TLS_RSA_WITH_NULL_MD5                 = { 0x00,0x01 };
CipherSuite TLS_RSA_WITH_NULL_SHA                 = { 0x00,0x02 };
CipherSuite TLS_RSA_WITH_NULL_SHA256              = { 0x00,0x3B };
CipherSuite TLS_RSA_WITH_RC4_128_MD5              = { 0x00,0x04 };
CipherSuite TLS_RSA_WITH_RC4_128_SHA              = { 0x00,0x05 };
CipherSuite TLS_RSA_WITH_3DES_EDE_CBC_SHA         = { 0x00,0x0A };
CipherSuite TLS_RSA_WITH_AES_128_CBC_SHA          = { 0x00,0x2F };
CipherSuite TLS_RSA_WITH_AES_256_CBC_SHA          = { 0x00,0x35 };
CipherSuite TLS_RSA_WITH_AES_128_CBC_SHA256       = { 0x00,0x3C };
CipherSuite TLS_RSA_WITH_AES_256_CBC_SHA256       = { 0x00,0x3D };

如下Cipher suite用于。DH表示server的证书包含CA签名的Diffie-Hellman参数。DHE表示 ephemeral Diffie-Hellman,此时使用signature-capable证书(该证书u也被CA签名)签名Diffie-Hellman参数。server使用的签名算法在Cipher Suite的DHE名字之后。

CipherSuite TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA      = { 0x00,0x0D };
CipherSuite TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA      = { 0x00,0x10 };
CipherSuite TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA     = { 0x00,0x13 };
CipherSuite TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA     = { 0x00,0x16 };
CipherSuite TLS_DH_DSS_WITH_AES_128_CBC_SHA       = { 0x00,0x30 };
CipherSuite TLS_DH_RSA_WITH_AES_128_CBC_SHA       = { 0x00,0x31 };
CipherSuite TLS_DHE_DSS_WITH_AES_128_CBC_SHA      = { 0x00,0x32 };
CipherSuite TLS_DHE_RSA_WITH_AES_128_CBC_SHA      = { 0x00,0x33 };
CipherSuite TLS_DH_DSS_WITH_AES_256_CBC_SHA       = { 0x00,0x36 };
CipherSuite TLS_DH_RSA_WITH_AES_256_CBC_SHA       = { 0x00,0x37 };
CipherSuite TLS_DHE_DSS_WITH_AES_256_CBC_SHA      = { 0x00,0x38 };
CipherSuite TLS_DHE_RSA_WITH_AES_256_CBC_SHA      = { 0x00,0x39 };
CipherSuite TLS_DH_DSS_WITH_AES_128_CBC_SHA256    = { 0x00,0x3E };
CipherSuite TLS_DH_RSA_WITH_AES_128_CBC_SHA256    = { 0x00,0x3F };
CipherSuite TLS_DHE_DSS_WITH_AES_128_CBC_SHA256   = { 0x00,0x40 };
CipherSuite TLS_DHE_RSA_WITH_AES_128_CBC_SHA256   = { 0x00,0x67 };
CipherSuite TLS_DH_DSS_WITH_AES_256_CBC_SHA256    = { 0x00,0x68 };
CipherSuite TLS_DH_RSA_WITH_AES_256_CBC_SHA256    = { 0x00,0x69 };
CipherSuite TLS_DHE_DSS_WITH_AES_256_CBC_SHA256   = { 0x00,0x6A };
CipherSuite TLS_DHE_RSA_WITH_AES_256_CBC_SHA256   = { 0x00,0x6B };

server可以向client请求signature-capable证书或DH证书来对client进行认证。client提供的所有Diffie-Hellman证书必须使用server提供(或生成)的参数

如下cipher suite用于在两端都没有认证情况下的匿名Diffie-Hellman交互。该模式容易遭中间人攻击,因此使用场景比较有限。该模式不能用于TLS1.2的实现中(除非明确指出允许匿名key交互)。

CipherSuite TLS_DH_anon_WITH_RC4_128_MD5          = { 0x00,0x18 };
CipherSuite TLS_DH_anon_WITH_3DES_EDE_CBC_SHA     = { 0x00,0x1B };
CipherSuite TLS_DH_anon_WITH_AES_128_CBC_SHA      = { 0x00,0x34 };
CipherSuite TLS_DH_anon_WITH_AES_256_CBC_SHA      = { 0x00,0x3A };
CipherSuite TLS_DH_anon_WITH_AES_128_CBC_SHA256   = { 0x00,0x6C };
CipherSuite TLS_DH_anon_WITH_AES_256_CBC_SHA256   = { 0x00,0x6D };

Appendix B. Glossary

Advanced Encryption Standard (AES):对称加密算法,块加密。TLS当前仅支持128-和256-位长度的AES

authenticated encryption with additional data (AEAD):对称加密算法

block_cipher:块加密作用于一组比特位的纯文本算法,之前使用64bit,目前使用128bit,通用的块大小

cipher block chaining (CBC):在每个纯文本块加密前会于前面一个纯文本(第一个块与初始化向量-IV 进行运算)进行异或运算。解码时首先进行解密,再与前面文本块进行异或运算

Digital Signature Standard (DSS):数字签名算法

digital signature:数字签名使用公钥和单向hash函数来产生签名数据。

Data Encryption Standard:普遍使用的对称加密算法

Initialization Vector (IV):当块加密使用CBC时,首个文本块会与IV进行异或运算。

client write key:用于加密client的数据

client write MAC key:用于认证client的数据

Message Authentication Code (MAC):单向hash处理的数据,生成消息摘要。

master secret:用于生成加密key,MAC secrets和IVs

RC4:流加密。

session:session定义了可与多个链接共享的安全参数。用来避免为每个链接进行安全参数协商造成的消耗。

stream cipher:将一个key加密为keystream,并与文本进行异或运算。

F.1.1.2. RSA Key Exchange and Authentication

使用RSA,key exchange和server认证可以同时进行。server的证书中包含了公钥。注意,使用静态的RSA key可能导致该静态key保护的所有session失效。

client验证完server的证书后,会使用证书中的公钥对pre_master_secret加密。server端成功解密pre_master_secret之后会并处理Finished消息之后,server认为client对server的认证成功。

当使用RSA进行密钥协商时,client通过certificate verify消息进行认证。client会对所有处理的握手消息进行签名,这些握手消息包括server certificate(该消息使用的签名与server相关)以及ServerHello.random(该数值与当前握手交互使用的签名相关)

 F.1.1.3. Diffie-Hellman Key Exchange with Authentication

当使用Diffie-Hellman密钥交换时,server可以提供包含fixed Diffie-Hellman参数的证书或使用server KeyExchange消息发送一系列使用DSA或RSA签名的临时DH参数。这些临时参数在签名前会使用hello.random进行hash,用以防止攻击者使用老参数进行攻击。其他场景下,client可以通过验证证书和签名来确保该消息来自server。

如果client有包含fixed Diffie-Hellman参数的证书,该证书中的信息可以用于完成密钥交互。这种情况下,client和server会生成相同的DH结果(pre_master_secret)。为了防止pre_master_secret过长地滞留在内存中,在完成它的计算后应该尽快将其转化为master secret。client 的DH参数必须兼容server端的DH参数(key exchange交换而来)。

如果client使用DSA或RSA证书或其未被认证,它会在clientKeyExchange消息中发送临时的参数,后续可能使用certificate verify消息对其进行认证。

如果使用DH密钥对进行多个握手,由于client和server都拥有一个包含fixed DH密钥对的证书或server重用DH密钥,需要注意方式subgroup攻击。

小型的subgroup攻击可以通过使用DHE cipher suite并未每个握手生成新的DH私钥来解决。

由于TLS允许serve提供任意的DH groups,client应该对DH group进行校验(与本地策略比较)。

 F.1.3. Detecting Attacks Against the Handshake Protocol

攻击者可能会通过握手消息来修改两端使用的加密算法。因此,攻击者必须修改握手过程中的一个或多个消息,当发生这种情况的时候,client和server会计算出不同的hash值(消息摘要改变),此时两端不会接受对端的Finished消息。由于无法获取master_secret,攻击者无法修复Finished消息,这样就可以发现攻击。

F.1.4. Resuming Sessions

当需要重用session来建立链接时,会使session的master secret生成新的ClientHello.random和SeverHello.random。

只有在client和server双方同意的情况下才能重用session。如果一端拒绝或证书过期(或被撤销),此时应该进行完整的握手。

F.2. Protecting Application Data

master secret(使用ClientHello.random和ServerHello.random生成)用于生成加密数据的keys和MAC secrets

master_secret = PRF(pre_master_secret, "master secret",ClientHello.random + ServerHello.random)

使用MAC保护发送的数据。为了方式消息重复和篡改,使用MAC key,序列号,消息长度,消息内容以及2个固定字符串生成MAC 。消息类型字段用来确保该消息服务于某个TLS Record层。序列号用来确保能够感知到消息的删除和重复。由于序列号为64位长度,可以保证数值不会被溢出。由于使用了独立的MAC keys,一方的消息不能插入到另一方的输出中(插入后MAC会错误)。类似地,由于server write和client wirte keys是独立的,因此只会用到一次流加密keys。

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

如果攻击者获取了额加密key,那么所有的加密消息都会被读取。类似地,泄露MAC key会导致message-modification攻击。由于MAC是加密地,message-alteration同时也需要突破对MAC地加密算法。

F.4. Security of Composite Cipher Modes

TLS使用cipher suite中定义地对称加密和认证函数对传输地应用数据进行保护,目的是防止网络攻击造成数据的篡改。

当前最健壮的方式称为encrypt-then-authenticate(加密再认证),首先对数据进行加密,然后对密文使用MAC计算消息摘要。该方法使用加密和MAC函数保证数据的加密防护和完整性。前者用于防止针对明文的攻击(信息泄露),后者用于防止针对消息的攻击(链路攻击)。其次,TLS使用其他方式,称为authenticate-then-encrypt(认证再加密),首先先对明文使用MAC,然后对整个数据(明文+MAC)进行加密。这种方式已经通过使用特定加密函数和MAC函数的组合证明了其安全能力,但通常不能保证安全。特别地,在使用当前非常好的加密函数再结合某个MAC函数的情况下,无法很好地防御主动攻击。

目前已经证明在某些情况下更加适合使用authenticate-then-encrypt。其中一种情况是使用流加密时,消息pad+MAC tag的长度不可知的情况;另一种时在CBC模式下使用块加密。这些情况下,安全能力可以通过一方对明文+MAC使用CBC加密以及对每个明文和MAC使用(新的,独立的,不可预测的)IV体现出来。

TIPS:

数字签名仅对证书进行签名保护,数字签名使用签名和hash生成签名数据(Certificate和KeyExchange消息会被签名)

handshake协议用于协商cipher spec并生成master secret(master secret可以生成Finished消息,加密keys,MAC keys)

key exchange的目的是生成pre_master_secret,最终生成master secret,通过Finished来校验pre_master_secret的正确性。

https://blog.helong.info/blog/2015/09/07/tls-protocol-analysis-and-crypto-protocol-design/

http://blog.fourthbit.com/2014/12/23/traffic-analysis-of-an-ssl-slash-tls-session

https://www.linuxidc.com/Linux/2015-07/120230.htm

https://blog.cloudflare.com/keyless-ssl-the-nitty-gritty-technical-details/

https://segmentfault.com/a/1190000002554673?utm_source=tag-newest

https://blog.helong.info/blog/2015/01/23/ssl_tls_ciphersuite_intro/

原文地址:https://www.cnblogs.com/charlieroro/p/10714783.html

时间: 2024-08-12 23:53:54

tls1.2的相关文章

SQL Server 2008, 2008 R2, 2012 and 2014 完全支持TLS1.2加密传输

SQL Server 2008, 2008 R2, 2012 and 2014 完全支持TLS1.2加密传输 微软高兴地宣布所有主流SQL Server客户端驱动和SQL Server发行版已经支持Transport Layer Security 1.2简称TLS 1.2. 发布时间是 2016年1月29日,这次发布提供了SQL Server 2008, SQL Server 2008 R2, SQL Server 2012 and SQL Server 2014对TLS1.2的完全支持. 支持

网站开启https后加密协议始终是TLS1.0如何配置成TLS1.2?

p { margin-bottom: 0.1in; line-height: 120% } 要在服务器上开启 TLSv1.2,通常要求基于 OpenSSL 环境的,使用 OpenSSL 1.0+ ,推荐 OpenSSL 1.0.1+. 要求基于 Java 环境的,使用 Jdk 1.7+ . 查看openssl的版本: # openssl version -a OpenSSL 1.0.1e-fips 11 Feb 2013 built on: Wed Apr 1 13:25:30 UTC 2015

win2008 配置TLS1.2

配置TLS1.2 分为2步, 添加TLS配置和禁用老的SSL版本 配置TLS1.2 提供两种方法, 选择其中一种就行了 手动设置 找到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols右键->新建->项->新建TLS 1.1,TLS 1.2TLS 1.1和TLS 1.2 右键->新建->项->新建Server, Client在新建的Server和

jdk1.6 支持 tls1.2协议 并忽略身份验证

jdk1.6不支持tls1.2协议,jdk1.8默认支持,比较好的解决方案是升级jdk,但是升级jdk风险极大.不能升级jdk的情况下,可以使用如下方式. 引入依赖 <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.54</version> </dependency&g

Windows Server 2008 R2 下配置TLS1.2,添加自签名证书

前言 2017年1月1日起App Store上的所有App应用将强制开启ATS功能. 苹果的ATS(App Transport Security)对服务器硬性3点要求: ① ATS要求TLS1.2或者更高,TLS 是 SSL 新的别称. ② 通讯中的加密套件配置要求支持列出的正向保密. ③ 数字证书必须使用sha256或者更高级的签名哈希算法,并且保证密钥是2048位及以上的RSA密钥或者256位及以上的ECC密钥. 由于领导舍不得花钱,只能辛苦我们自己搞个不花钱的证书.在网上找了一大堆各种配置

Poodle漏洞卷土重来,TLS1.2版本受影响

谷歌安全团队在今年10月发现的一个高危SSL漏洞POODLE(贵宾犬漏洞)影响SSL V3.0版本.近日,该漏洞卷土重来,这次它甚至影响到SSL升级版——TLS协议. 漏洞概况 根据360监测结果显示,本次漏洞影响同样广泛,并且攻击者利用成本比上一次更便利,漏洞起因是由于TLS1.2没有正确校验PADDING,导致攻击者即使不先降级到SSL3.0,也能通过TLS1.2,用中间人攻击方法绕过传输层加密机制,窃取用户的敏感信息,例如cookies信息,帐号信息等,这种攻击在咖啡馆环境或局域网环境影响

转:基于TLS1.3的微信安全通信协议mmtls介绍

转自: https://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=2649286266&idx=1&sn=f5d049033e251cccc22e163532355ddf&scene=0&key=b28b03434249256b2a5d4fdf323a185a798eaf972317ca3a47ef060d35c5cd8a4ae35715466d5bb5a558e424d20bef6c&ascene=0&am

【科普】什么是TLS1.3

TLS1.3是一种新的加密协议,它既能提高各地互联网用户的访问速度,又能增强安全性. 我们在访问许多网页的时候,常常会在浏览器的地址栏上看到一个锁的图标,并使用"https"代替传统的"http".这里的"s"代表着安全.当你连接到一个HTTPS站点时,你和该站点之间的通信会被加密,这会显著提高浏览的安全性,使你的通信避开那些窥视的眼睛,并防止恶意代码的注入.HTTPS不仅应用于网站,还保护着大部分API和移动应用后台的安全. 我们把使互联网实现

win2008 r2 开启TLS1.2

Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols] [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0] [HKEY_LOCAL_MACHINE\SYS

关于在 java 8 下开启 TLS_RSA_WITH_3DES_EDE_CBC_SHA 支持 xp ie8 tls1.0 的正常访问

最近为 aioserver 增加了ssl支持. 在 myssl.com 上测试了一下,关于[客户端握手模拟]发现 ie8 xp tls1.0  这一项提示:握手失败 (服务器断开连接) 我又试了一下 baidu.com 和 taobao.com, 关于 ie8 xp tls1.0,可以握手成功,使用的[加密套件]: TLS_RSA_WITH_3DES_EDE_CBC_SHA 虽说 ie8 现在用的少,但国内应该还有一部分用户在使用,那就想办法让 java 8 支持 ie8 tls1.0 的访问吧