TLS 1.2详解

2023-12-24 23:41:42

TSL由多个协议组成的两层协议集合,工作与应用层和传输层之间。

TLS协议包含两层协议:记录层协议TLS Record Protocol协议)和?握手协议TLS Handshake?Protocol协议),底层采用可靠传输协议(如TCP

之所以选取这两个子协议,是因为记录层协议发挥了TLS“承上启下”的作用,它对从上层应用接收到的待传输数据进行分块、压缩、封装等处理,而后将处理后的数据传输给下层,再传输给通信的另一方;而握手协议是通信双方准备建立TLS连接通信时,用到的第一个子协议,他是TLS协议中最复杂、涉及密码技术最多的协议。

注:出于安全考虑,TLS 1.3已取消压缩

记录层协议通过如下方式实现数据的安全传输:

  1. 链路是私有的,使用对称加密方式对应用数据进行加密。对每条链路来说,对称加密使用的密钥是各自独立的(通过握手协议协商得到)。记录层协议也可以用于非加密场景;
  2. 链路是可靠的。使用消息认证码MAC)来对消息完整性进行校验,MAC使用哈希函数(SHA-1SHA-256SM3等)进行运算。记录层协议可以不使用MAC,但通常仅限于使用记录层协议作为底层协议来传输协商使用的安全参数。

记录层协议用于封装多种上层协议,如握手协议,用于在传输/接收应用数据前进行相互认证,并协商出加密算法以及使用的密钥。

握手协议使用如下三种方式提供数据的安全传输:

  1. 对端身份可以通过非对称算法或公钥,加密(RSADSASM2等)等进行认证。该认证是可选的,但通常要求至少通过一种认证方式对对端进行认证;
  2. 协商的共享密钥的过程是安全的,窃听者无法获取协商的密钥;
  3. 协商是可靠的,攻击者无法在不被链路探测到的情况下修改协商报文。

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

1.HMAC and the Pseudorandom Function(带密钥哈希消息认证码&伪随机函数)

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

注:TLS 1.3之前,密码套件的名称是以协商安全设置时使用的身份验证、加密、消息认证码和密钥交换算法组成。TLS 1.3中的密码套件格式已经修改。在TLS 1.3草案文档中,密码套件仅用于协商加密和HMAC算法。 ?

除此之外,还需要一种在生成或校验密钥时将密钥扩展为数据块的方式。伪随机函数pseudorandom functionPRF)可以通过输入密钥、种子、认证的标签(即预主密钥、客户端随机数、服务端随机数、常量字符串)之后给出任意长度的输出。伪随机函数基于HMAC。本文档所有密码套件使用的的伪随机函数均采用SHA-256的哈希函数。新的密码套件必须明确规定伪随机函数,并使用SHA-256或更健壮的hash函数。

密码套件规定了如何使用伪随机函数生成密钥材料key material块加密算法(AES等)以及MAC算法(HMAC-SHA1

每个密码套件的名称(例如TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)定义密钥交换算法批量加密算法消息认证码算法,以及一个伪随机函数

  1. 密钥交换算法,例如ECDHE_RSA,用于决定客户端与服务器之间在握手时如何身份验证。
  2. 批量加密算法,例如AES_128_GCM,用于加密消息流。它还包括密钥大小及显式和隐式初始化向量(密码学随机数)的长度。
  3. 消息认证码算法,例如SHA256,用于创建消息摘要,消息流每个数据块的加密散列。
  4. 伪随机函数,例如TLS 1.2伪随机函数使用MAC算法的散列函数来创建一个主密钥——连接双方共享的一个48字节(368比特)的私钥。主密钥在创建会话密钥(例如创建MAC)时作为一个熵来源。

使用伪随机函数首先需要定义一个扩展密钥的函数,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) + ...

其中:

  • secret是进行计算所需要的密钥;
  • seed是进行计算所需要的数据;
  • A(0) = seed;
  • A(i) = HMAC(secret,A(i-1));
  • P_hash 能够反复迭代知道产生要求长度的数据

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

使用P_hash生成伪随机函数的方式如下:

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

labelASCII字符串,如字符串 “slithy toves” 使用时应该为ASCII:73 6C 69 74 68 79 20 74 6F 76 65 73


2.The TLS Record Protocol(记录层协议

记录层协议是一个层级协议,每一层消息可能包括长度length)、描述description)和内容content)字段。记录层协议负责将需要传输的数据分布到管理的block)中,可能会对数据进行压缩(可选),在使用MAC和加密后传输结果。接收端接收到消息之后需要进行解密,校验和解压(可选)操作,然后传递到上层业务。当接收到未知的Record类型后,必须发送意外消息警报unexpected_message alert)。

本文档定义了4种使用记录层协议协议的协议,握手handshake协议报警alert协议密码规格变更change cipher spec协议应用数据application data协议

2.1 Connection States(连接状态)

记录层协议使用连接状态TLS?connection state)作为运行环境,连接状态指定了压缩算法、加密算法以及MAC算法,且拥有消息认证码密钥MAC key)和批量加密密钥bulk encryption keys)相关的参数。记录层协议始终保持4个连接状态当前读current read)、当前写current write)、挂起读pending read)、挂起写pending write)。其中,表示接收数据,表示发送数据。所有的记录都在当前读当前写状态下处理。挂起状态pending state)可以被握手协议修改,而密码规格变更消息可以选择性地将挂起状态变为当前状态current state),此时挂起状态初始化为空状态empty state)。给未初始化的状态配置参数并使之变为当前状态的方式是非法的。除了最初的当前状态外,当前状态必须包含经过协商的安全参数。初始的当前状态没有使用加密、压缩、MAC

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

连接状态的结构如下:

TLS1.1 struct {
连接端(ConnectionEnd)?实体(entity);----标识接入的实体为客户端或服务端;enum{server, client} ConnectionEnd
批量密码算法(BulkCipherAlgorithm)?bulk_cipher_algorithm;----加解密密码算法;enum{null,?对称算法} ConnectionEnd
密码类型(CipherType)?cipher_type;----表示密码算法的类型;enum {block} CipherType
uint8 key_size;----密钥长度
uint8 key_material_length;----密钥材料长度
MAC算法(MACAlgorithm)?mac_algorithm;----用于计算和校验的密码杂凑算法;enum{杂凑算法}MACAlgorithm
uint8 hash_size;----杂凑长度
压缩方法(CompressionMethod)?compression_algorithm;----数据压缩算法;enum{null(0), (255)}?CompressionMethod
opaque master_secret[48];----在协商过程中由预主密钥、客户端随机数、服务端随机数计算出的 48 字节密钥
opaque client_random[32];----由客户端产生的32字节
opaque server_random[32];----由服务端产生的32字节
} 安全参数(SecurityParameters);

TLS1.1记录层使用安全参数来生成如下6项内容:

  1. 客户端写校验密钥(client write MAC secret
  2. 服务端写校验密钥(server write MAC secret
  3. 客户端写密钥(client write key
  4. 服务端写密钥(server? write key

GMSSL struct {
连接端(ConnectionEnd)?实体(entity);----标识接入的实体为客户端或服务端;enum{server, client} ConnectionEnd
批量密码算法(BulkCipherAlgorithm)?bulk_cipher_algorithm;----加解密密码算法;enum{null, sm1, sm4} ConnectionEnd
密码类型(CipherType)?cipher_type;----表示密码算法的类型;enum {block} CipherType
uint8 key_size;----密钥长度
uint8 key_material_length;----密钥材料长度
MAC算法(MACAlgorithm)?mac_algorithm;----用于计算和校验的密码杂凑算法;enum{sha1, sm3}MACAlgorithm
uint8 hash_size;----杂凑长度
压缩方法(CompressionMethod)?compression_algorithm;----数据压缩算法;enum{null(0), (255)}?CompressionMethod
opaque master_secret[48];----在协商过程中由预主密钥、客户端随机数、服务端随机数计算出的 48 字节密钥
opaque client_random[32];----由客户端产生的32字节
opaque server_random[32];----由服务端产生的32字节
unit8 record_iv_length;----记录IV长度
unit8 mac_length;----MAC长度
} 安全参数(SecurityParameters);

GMTLS记录层使用安全参数来生成如下4项内容:

  1. 客户端写校验密钥(client write MAC secret
  2. 服务端写校验密钥(server write MAC secret
  3. 客户端写密钥(client write key
  4. 服务端写密钥(server? write key

TLS 1.2 struct {
连接端(ConnectionEnd)?entity;----标识接入的实体为客户端或服务端;enum{server, client} ConnectionEnd
伪随机算法(PRFAlgorithm)?prf_algorithm;----通过主密钥(master secret)生成密钥的算法;enum {tls_prf_sha256}PRFAlgorithm
批量密码算法(BulkCipherAlgorithm)?bulk_cipher_algorithm;----加解密密码算法;enum{null,?对称算法} ConnectionEnd
密码类型(CipherType)?cipher_type;----表示密码算法的类型;enum {block} CipherType
uint8 enc_key_length;----加密密钥长度
uint8 block_length;----块长度
uint8 fixed_iv_length;----固定IV长度
uint8 record_iv_length;----记录IV长度
MAC算法(MACAlgorithm) mac_algorithm;----用于计算和校验的密码杂凑算法;enum{杂凑算法}MACAlgorithm
uint8 mac_length;----MAC长度
uint8 mac_key_length;----MAC密钥长度
压缩方法(CompressionMethod)?compression_algorithm;----数据压缩算法;enum{null(0), (255)}?CompressionMethod
opaque master_secret[48];----在协商过程中由预主密钥、客户端随机数、服务端随机数计算出的 48 字节密钥
opaque client_random[32];----由客户端产生的32字节
opaque server_random[32];----由服务端产生的32字节
} 安全参数(SecurityParameters);

TLS 1.2记录层使用安全参数来生成如下6项内容:

  1. 客户端写校验密钥(client write MAC key
  2. 服务端写校验密钥(server write MAC key
  3. 客户端写加密密钥(client write encryption key
  4. 服务端写加密密钥(server write encryption key
  5. 客户端写初始向量(client write IV
  6. 服务端写初始向量(server write IV
key_block = PRF(SecurityParameters.master_secret,"key expansion",SecurityParameters.server_random + SecurityParameters.client_random);

客户端写入client write)参数由服务器在接收和处理记录时使用,反之亦然。2.3介绍了根据安全参数生成这些项目的算法。

一旦设置了安全参数并生成了密钥,就可以通过将连接状态设置为当前状态来实例化连接状态。必须为处理的每个记录更新这些当前状态。每个连接状态都包括以下元素:

  • 压缩状态(compression state):压缩算法的当前状态。
  • 密码状态(cipher state):加密算法的当前状态。这将包括该连接的计划密钥。对于流密码,它还将包含允许流继续加密或解密数据所需的任何状态信息。
  • 校验密钥(MAC key):连接的校验密钥
  • 序列号(sequence number):每个连接状态都包含一个序列号,且分别由读和写状态维护。当一个连接状态connection state)变为活动状态active state)时必须置为0。序列号会在每个记录中递增。

2.2Record Layer(记录层)

2.2.1Fragmentation(分段)

记录层将数据分成2^{14}字节或者更小的片段。

每个片段结果如下:

struct {
内容类型(ContentType) type;----上层协议的类型,用于处理封装片段(fragment),结构详见
协议版本(ProtocolVersion)?version;----协议采用的版本,结构详见
uint16?length;----TLS明文片段(TLSPlaintext fragment)的长度,不能大于2^{14}
opaque fragment[TLSPlaintext.length];----将传输的应用数据,由上层协议处理的特定类型的数据,记录层协议不关心具体数据内容
} TLS明文(TLSPlaintext);

enum?{
密钥规格变更(change_cipher_spec,20),
报警(alert,21),
握手(handshake,22),
应用数据(application_data,23),(255)
} 内容类型(ContentType);

struct {
uint8 major;----主版本
uint8 minor;----次版本
} 协议版本(ProtocolVersion);

不能发送零长度的握手、报警、密钥交换数据。但可以发送零长度的应用数据(可能用于链路探测)。

TLS记录层协议中,应用数据处理的优先级很低,一般在上层协议(握手协议)处理完成后发送。

2.2.2Record Compression and Decompression(记录压缩和解缩)

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

压缩后数据结构如下:

struct {
内容类型(ContentType) type;----结构详见2.2.1
协议版本(ProtocolVersion)?version;----结构详见2.2.1
uint16?length;----以字节为单位的TLS压缩片段TLSCompressed.fragment)的长度,不大于2^{14}+1024字节
opaque fragment[TLSPlaintext.length];----TLS明文片段TLSPlaintext.fragment)的压缩形式
} TLS压缩(TLSCompressed)

解压缩函数必须保证不能造成缓存buffer)溢出;实际使用中一般禁用压缩

2.2.3Record Payload Protection(记录有效载荷保护)

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

struct {
内容类型(ContentType) type;----结构详见2.2.1
协议版本(ProtocolVersion)?version;----结构详见2.2.1
uint16?length;----以字节为单位的TLS密文片段TLSCiphertext.fragment)的长度,不大于2^{14}+1024字节
select安全参数密码类型(SecurityParameters.cipher_type) ){
? ? case streamGenericStreamCipher;---通用流密码
????case blockGenericBlockCipher;---通用分组密码
????case aeadGenericAEADCipher;---通用AEAD(Authenticated Encryption with Associated Data,关联数据的身份验证加密)密码
}fragment;----带校验码MAC)的TLSCompressed.fragment加密形式
} TLS密文(TLSCiphertext);

2.2.3.1 Null or Standard Stream Cipher(空和流密码)

流密码将TLS压缩片段TLSCompressed.fragment)结构转换为TLS密文片段TLSCiphertext.fragment)的stream)结构,如下:?

stream-ciphered struct{
opaque content?[TLSCompressed.length];---TLS压缩TLSCompressed)长度
opaque MAC?[SecurityParameters.mac_length];---安全参数校验码算法SecurityParameters.mac_algorithm)指定的MAC算法
}通用流加密(GenericStreamCipher

MAC的生成方式如下,其中“+”表示串联:

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

seq_num:序列号。每一个读写状态都分别维持一个单调递增序列号。序列号的类型为uint64,序列号初始值为零,最大不能超出2^{64}-1。序列号不能回绕。如果序列号溢出,那就必须重新开始握手。

MAC必须在被加密前计算出来,流密码对包括MAC在内的整个块进行加密。对于不使用同步向量synchronization vector)的流密码(如RC4),从一个记录末尾开始的流密码状态仅用于后续数据包。如果密码套件为TLS_NULL_WITH_NULL_NULL,加密方式则由操作类型组成(如数据没有被加密,MAC为0,即NULL的定义)。对于null)和流密码stream ciphers)来说:TLS密文长度(TLSCiphertext.length)?=?TLS压缩长度(TLSCompressed.length) + 安全参数校验码长度(SecurityParameters.mac_length)

2.2.3.2 CBC Block Cipher(密文分组链接分组密码)

CBC模式的全称是Cipher Block Chaining模式(密文分组链接模式)

分组密码(3DESAESSM4)使用加密和MAC函数将TLS压缩片段TLSCompressed.fragment)结构转换为TLS密文片段TLSCiphertext.fragment)的block)结构,如下:

struct {
opaque IV [SecurityParameters.record_iv_length];----通用分组加密(GenericBlockCipher)中传输的初始化向量,必须随机生成
block-ciphered struct {? ---分组加密结构
? ? opaque content [TLSCompressed.length];?---TLS压缩TLSCompressed)长度
????opaque MAC [SecurityParameters.mac_length];---IV的长度,其正确值与使用的密码套件相关
????uint8 padding[GenericBlockCipher.padding_length];---填充的数据,详见
????uint8 padding_length---填充长度,该长度不包括填充长度本身
};
} 通用分组加密(GenericBlockCipher);

padding:填充的数据,在数据加密前需要将数据填充为密码算法分组长度的整数倍,填充的长度不能超过255个字节。填充的每个字节的内容是填充的字节数。接收者应检查这个填充,如果出错,发送无效的记录MACbad_record_mac)报警消息。

组:对于CBC模式下的块密码(密码块链),在传输任何密文之前,了解记录的整个明文是至关重要的。否则,攻击者有可能发起[CCBATT]中描述的攻击。Canvel等人[CCBCTIME]已经证明了基于计算MAC所需时间的CBC填充的定时攻击。为了抵御这种攻击,实现必须确保记录处理时间基本相同,无论填充是否正确。通常,最好的方法是计算MAC,即使填充不正确,然后才拒绝数据包。例如,如果填充pad)看起来不正确,则实现可能假设为零长度的填充,然后计算MAC。这留下了一个小的定时信道,因为MAC性能在某种程度上取决于数据片段的大小,但由于现有MAC的大块大小和定时信号的小大小,它被认为不够大而不可利用。

2.2.3.3 AEAD Ciphers(关联数据的身份验证加密密码

关联数据的身份验证加密密码AEAD Cipher)(如CCMGCM)函数将TLS压缩片段TLSCompressed.fragment)结构转换为TLS密文片段TLSCiphertext.fragment)的AEADSecurityParameters.mac_algorithm)结构,如下:

struct {
opaque nonce_explicit?[SecurityParameters.record_iv_length];----随机数
aead-ciphered struct {? ---AEAD加密结构
? ? opaque content [TLSCompressed.length];?---TLS压缩TLSCompressed)长度
};
} 通用AEAD加密(GenericAEADCipher);

AEAD使用密钥key)、随机数nonce),明文plaintext)和"additional data"作为输入进行身份认证。密钥key)可以是客户端写密钥client_write_key)或服务端写密钥server_write_key),没有用到校验码密钥MAC key)。明文TLS压缩片段TLSCompressed.fragment)。额外的认证数据也被称为附加数据additional_data),定义如下:

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

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

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

为了加密和验证,AEAD密码AEAD cipher)会采用密钥key),随机数nonce)和"additional data"以及AEAD加密值AEADEncrypted value)。输出为明文或错误信息,没有完整性校验。如果解密失败,则发出无效的记录MACbad_record_mac)报警消息。

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

2.3 Key Calculation(密钥计算)

记录层协议使用算法结合握手协议提供的安全参数来生成当前状态current state)需要的密钥keys)。主密钥master secret)被分割为客户端写校验码密钥client write MAC key)、服务端写校验码密钥server write MAC key)、客户端写加密密钥client write encryption key)和服务端写加密密钥server write encryption key),这四个密钥按照字节顺序分割,未使用的值为空。一些AEAD密码会使用到客户端写初始向量client write IV)和服务端写初始向量server write IV)。

密钥校验码密钥MAC keys)生成后,主密钥作为熵。此处使用伪随机函数PRF)生成密钥

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]      ----服务端写初始向量

当前客户端写初始向量服务端写初始向量仅用于AEAD密码

主密钥算法如下:

master_secret = PRF(pre_master_secret,      ----预主密钥
????????????????????"master secret",
                     ClientHello.random +   ----客户端随机数
?                    ServerHello.random     ----服务端随机数
)[0..47];

记录层协议使用连接状态connection state)来维护加密状态,它使用握手协议来获取安全参数并生成相应的密钥,最后对数据进行加密传输。


3 The TLS Handshaking Protocols(TLS握手协议)

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

握手协议handshake Protocols)用来协商会话session),包含以下项目:

  1. 会话标识符session identifier):服务端选择的随机序列号,用于标记激活的或是重用的会话
  2. 对等方证书peer certificates):对端的X509 v3证书。
  3. 压缩方法compression method):加密前使用的压缩算法
  4. 密码规范cipher spec):指定用于生成密钥的伪随机函数,块加密算法(nullAESSM4等)和MAC算法(如HMAC-SHA1HMAC-SM3)。
  5. 主密钥master secret):客户端和服务端的48字节的共享密钥
  6. 是否重用is resumable):用于标记该会话是否能被重用

这些参数用来生成安全参数。可以通过握手协议的重用特性,使用相同的会话来初始化链接。

3.1?Change Cipher Spec Protocol(密码规格变更协议

密码规格变更协议change cipher spec Protocol)主要用于通知密码规格cipher spec)的改变,即通知对方使用刚协商好的安全参数来保护接下来的数据。该消息用当前的压缩算法压缩并用当前的密码规格加密,如果是首次协商,该消息为明文。

该消息的长度为一个字节,其值为1。客户端和服务端都要在安全参数协商完毕之后(完成Finished)消息前)、握手结束消息之前发送此消息。

对于刚协商好的密钥,写密钥在此消息发送之后立即启用;读密钥在收到吃消息之后立即启用。

密码变更消息结构定义如下:

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

客户端服务端通过发送密码规格变更ChangeCipherSpec)消息来通知对端后续记录records)将使用新协商的密码规格密钥加密保护。接收端接收到该消息后,会通知记录层读挂起状态read pending state)中的内容拷贝到读当前状态read current state)中。在发送完该消息之后,发送端必须将写挂起状态write pending state)中的内容拷贝到写激活状态write active state)中。

协商好的安全参数会保存在写挂起状态中,当一端需要使用新的加密算法时,会发送密码规格变更消息,并立即将写挂起状态中的内容覆盖到写当前状态write current state)中,接收端则修改读状态read state)的内容。

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

3.2 Alert Protocol(警报协议)

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

struct {
AlertLevel level;----报警级别,结构详见
AlertDescription description; ----报警说明,结构详见
}?Alert

enum {
warning?(1),?----警告
fatal?(20),?----致命
(255)
}?报警等级AlertLevel

enum{
close_notify (0),----关闭通知
unexpected_message (10),?----意外信息
bad_record_mac (20),?----无效的记录消息校验码
decryption_failed_RESERVED (21),?----解密失败 - 保留字段
record_overflow (22),?----记录溢出
decompression_failure (30),?----解压失败
handshake_failure (40),?----握手失败
no_certificate_RESERVED (41),?----无证书 - 保留字段
bad_certificate (42),?----无效证书
unsupported_certificate (43),?----不支持的证书
certificate_revoked (44),?----证书已撤销
certificate_expired (45),?----证书已过期
certificate_unknown (46),?----未知证书
illegal_parameter (47),?----非法参数
unknown_ca (48),?----未知证书颁发机构
access_denied (49),?----拒绝访问
decode_error (50),?----解码错误
decrypt_error (51),?----解密错误
export_restriction_RESERVED (60),?----导出限制?- 保留字段
protocol_version (70),?----协议版本
insufficient_security (71),?----安全性不足
internal_error (80),?----内部错误
user_canceled (90),?----用户取消
no_renegotiation (100),?----禁止重新协商
unsupported_extension (110),?----不支持的扩展
(255)
}?报警说明AlertDescription

3.2.1 Closure Alerts(关闭报警)

除非出现致命报警,客户端和服务端任何一方在结束连接之前都应发送关闭通知信息。对于该消息的发出方,该消息通知对方不再发送任何数据,可以等待接收方回应的关闭通知消息后关闭本次连接,也可以立即关闭本次连接。对于接收方,收到该消息后应回应一个关闭通知消息,任何关闭本次连接,不再接收和发送数据。

3.2.2. Error Alerts(错误报警)

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

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

当接收到警告warning)级别的报警,通常可以继续保持链接。作为发送端,由于通常无法判断接收端接收到警告级别的报警的动作,因此通常警告的报警作用不大。

错误报警表
错误报警表

2.3 Handshake Protocol Overview(握手协议概述)

TLS握手产生会话状态session state)所需要的加密参数。当客户端服务端开始通信时,会协商产生版本号,加密算法(可能会进行相互认证),以及使用公钥生成共享密钥。

TLS握手涉及如下过程:

  • 交换hello消息来协商密码套件,交换随机数,决定是否会话(session)重用;
  • 交换必要的参数,协商预主密钥;
  • 交换证书或链间通信协议IBC),用来验证对方;
  • 使用预主密钥premaster secret)和交换的随机数生成主密钥master secret);
  • 给记录层提供安全参数;
  • 验证双方计算的安全参数的一致性、握手过程的真实性和完整性。

需要注意的是上层协议不能过度依赖于TLS可以始终提供健壮的安全链接。实际上有很多中间人攻击可以导致两端切换到它们所支持的最不安全方法。TLS协议用来最小化攻击,但实际中存在很多可能的攻击方式,例如:攻击者可以阻止对运行安全服务的端口的访问,或者试图让对等方协商未经身份验证的连接。使用TLS的基本原则是上层协议必须知道所需要的安全需求,并且不能在低于安全需求的链路上传输信息。因此在使用满足需求的密码套件cipher suite)前提下,可以认为链路是安全的。

握手过程如下:客户端发送客户端问候ClientHello)消息给服务端服务端应回应服务端问候ServerHello)消息,否则产生一个致命错误并且断开连接。客户端问候ClientHello)和服务端问候ServerHello)用于在客户端和服务端进行基于 RSAECCIBC的密码算法协商,以及确定安全传输能力,包括协议版本、会话标识、密码套件等属性,并且产生和交换随机数。

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

服务端发送完hello消息之后,接着发送自己的证书消息、服务端密钥交换消息。如果服务端需要验证客户端的身份,则向客户端发送证书请求CertificateRequest)消息。然后发送服务端 hello完成消息,表示hello消息阶段已经结束,服务端等待客户端的返回消息。如果服务端发送了一个证书请求消息,客户端必须返回一个证书消息。然后客户端发送密钥交换消息,消息内容取决于客户端问候ClientHello)消息和服务端问候ServerHello)消息协商出的密钥交换算法。如果客户端发送了证书消息,那么也应发送一个带数字签名的证书验证消息服务端验证客户端的身份。

接着客户端发送密码规格变更ChangeCipherSpec)消息,此时客户端会将挂起状态pending state)中的密码规范Cipher Spec)拷贝到当前状态Current state)中,然后客户端立即使用刚协商的算法和密钥keys?&?secrets),加密并发送握手结束Finished)消息。服务端则回应密码规格变更消息,使用刚协商的算法和密钥,加密并发送握手结束消息。至此握手过程结束,服务端和客户端可以开始数据安全传输。应用数据不能先于首次握手(非TLS_NULL_WITH_NULL_NULL密码套件)发送。

      Client                                               Server

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

* 表示可选或依赖于上下文关系的消息,不是每次都发送
[]不属于握手协议消息

服务端密钥交换是独立的TLS消息,不属于握手消息的一部分

如果客户端和服务端决定重用之前的会话,可不必重新协商安全参数。客户端发送客户端问候ClientHello)消息,并且带上要重用的会话标识Session ID)。如果服务端有匹配的会话存在,服务端则使用相应的会话状态接受连接,发送一个具有相同会话标识服务端问候ServerHello)???????消息。然后客户端和服务端各自发送密码规格变更消息和握手结束消息。至此握手过程结束,服务端客户端可以开始数据安全传输。如果服务端没有匹配的会话标识,服务端会生成一个新的会话标识进行一个完整的握手过程。

      Client                                                Server

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

2.4 Handshake Protocol(握手协议)

TLS握手协议属于TLS握手协议的上层协议。该协议用于协商会话session)的安全属性。握手(Handshake)消息在TLS记录(record)层之下运行,被封装成一个或多个TLS明文TLSPlaintext)结构,并被当前会话状态state)处理。

握手协议消息的发送顺序必须遵从如下规则:乱序的握手消息会导致致命fatal)错误;不需要的握手消息可以被忽略;唯一一个不需要受发送顺序限制的消息是问候请求HelloRequest),但客户端端进行握手过程中接收到后应该忽略该消息。

2.4.1 Hello Messages(问候消息

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

2.4.1.1. Hello Request(问候请求

服务端可以在任何时候发送(问候请求HelloReques消息。

该消息用来通知客户端进行协商,客户端会发送客户端问候ClientHello)消息进行响应。该消息不能用于判定哪一端开始建立链接(仅用于初始化协商)。如果客户端正在握手协商中或者客户端不同意建立链接,客户端会发送禁止重新协商no_renegotiation警告alert)。如果服务端发送问候请求HelloRequest)并没有受到任何响应,服务端可能会关闭链接并发送致命报警fatal alert)。

此消息不得包含在整个握手过程中维护的消息哈希中,并在完成finished)消息和证书验证certificate verify)消息中使用。

2.4.1.2 Client Hello(客户端问候)

客户端发送客户端问候ClientHello)消息来初始化握手协商,ClientHello也用于响应服务端发送的问候请求HelloRequest)消息。

struct {
ProtocolVersion client_version;----客户端在这个会话中使用的协议版本,详见
Random random;----客户端产生的随机信息,结构详见
SessionID session_id;----客户端在连接中使用的会话标识,定义为:opaque SessionID<0..32>,详见
CipherSuite cipher_suites<2..2^{16}-2>;----密码套件列表,定义为:unit8?CipherSuite[8],详见
CompressionMethod compression_methods<1..2^8?-1>;----压缩算法列表,定义为:enum{null(0), (255)}CompressionMethod
selectextensions_present)?{? ---客户端可能会通过扩展字段来请求服务端的扩展功能
? ? case false:?
????? ? ? ? ?struct { }
????case true
????? ? ? ? ?Extension extensions<0..2^{16}-1>;---扩展功能,详见
};
} 客户端问候(ClientHello);

client_version客户端建议的最低版本且服务端支持的最高版本。TLS 1.2中如果服务端不支持该版本,会返回带有低版本的ServerHello,如果客户端同意使用该低版本,则使用该版本进行协商,否则客户端返回协议版本报警protocol_version alert)并关闭链接。如果服务端接收到的ClientHello携带了高于服务端支持的最高版本的版本,则必须返回服务端所支持的最高版;当服务端接收的ClientHello中的版本低于服务端所支持的最高版本,如果服务端同意使用该低版本,则会选择不高于ClientHello.client_version中定义的最高版本(如服务端支持TLS1.0TLS1.1TLS1.2client_versionTLS 1.0,则服务端会使用TLS1.0处理),反之返回协议版本报警protocol_version alert)。

客户端问候ClientHello)包含一个随机数的结构体。如下:

struct {
? ? uint32 gmt_unix_time;?----标准unix?32位格式的发送端的内部时钟时间,未规定该时钟的精度。
????opaque random_bytes[28]----生成的28字节的安全随机数,随机数并不是会话标识session ID
}?随机数Random);

session_idsession_id是一个可变长字段,其值有服务端决定。如果没有可以重用的会话标识或希望协商安全参数,该字段应为空。如果该字段非空,该值表示了服务端需要重用的会话,此时会话标识可以来自于①先前的连接标识,②当前的连接标识,③其他处于连接状态的连接标识。第2种用于当客户端仅需要更新随机数的场景;第3种用于建立独立的安全链接(而无需进行完整的握手过程)。当握手协商通过交换完成Finished)后保存的会话标识超时,或遇到致命fatal)错误时,会话标识会变为无效状态。会话标识的实际内容由服务端定义。会话标识生成后应一致保持到被超时删除或与这个会话相关的连接遇到致命错误被关闭。一个会话失效或被关闭时则与其相关的连接都应被强制关闭。

注:由于会话标识的传输没有经过加密或MAC保护(因为此时加密参数还未协商出来),服务端端不能在会话标识中放置敏感字段,否则可能导致安全攻击。在握手结束后的消息(含完成(Finished)消息)中的会话标识会被加密保护。

cipher_suites:客户端问候ClientHello)中的密码套件cipher suite)列表给出了客户端支持的加密算法,并按照客户端偏好排序。每个密码套件包含一个密钥key)交换算法、一个加密算法、一个校验MAC)算法和PRF服务端会选择一个合适的密码套件,如果没有合适的则会发送握手失败警告handshake failure alert)并关闭链接。如果密码套件中包含服务端无法识别的项,服务端必须忽略这些密码套件,并处理剩余的密码套件

extensionsClientHello通过判断在compression_method之后是否有多余的字节来确定是否由存在扩展字段。当客户端使用扩展请求额外的功能,但服务端不支持时,客户端可能会断开握手。服务端必须接收带或不带扩展字段的ClientHello,并解析这些扩展字段,如果发现无法解析的字段,则必须返回解码错误警报decode_error alert)。

客户端发送完ClientHello后会等待服务端ServerHello,非ServerHelloHelloRequest除外)的消息会被认为致命fatal)错误。

2.4.1.3 Server Hello(服务端问候)

服务端接收到客户端问候ClientHello)且能够接受其中列出的cipher suite时会发送ServerHello

struct {
ProtocolVersion server_version;----服务端在这个会话中使用的协议版本
Random random;----服务端产生的随机信息,结构与客户端随机数相同
SessionID session_id
CipherSuite cipher_suites
CompressionMethod compression_methods<1..2^8?-1>
selectextensions_present)?{??
? ? case false:?
????? ? ? ? ?struct { }
????case true
????? ? ? ? ?Extension extensions<0..2^{16}-1>
};
} 服务端问候(ServerHello);

注:服务端可能会发送空的会话标识session id),表示会话session)不会被缓存且不会被重用。

2.4.1.4 Hello Extensions(问候扩展)

扩展字段的格式如下:

struct {
? ?ExtensionType extension_type;----特定的扩展类型,
? ?opaque extension_data<0..2^{16}-1>;----特定的扩展的信息
}扩展(Extension);

enum {
? ?signature_algorithms(13),(65535)
}扩展类型(ExtensionType);

IANA维护的扩展参见Section 12

只有在ClientHello中出现的扩展才能出现在ServerHello中。如果客户端接收到的ServerHello中出现了ClientHello中不相关的扩展,则客户端必须断开链接并发送不支持的扩展致命警告unsupported_extension fatal alert)。未来可能会实现面向服务端server-oriented)的扩展,参见Hello Extensions

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

扩展可以在初始化新的会话session)或重用会话时发送。在客户端请求重用会话时,由于它并不知道服务端是否会支持不同的扩展,这种情况下,客户端会发送之前发送过的扩展。

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

开发extension的注意点参见Hello Extensions

2.4.1.4.1 Signature Algorithms(签名算法)

客户端使用签名算法signature_algorithm)扩展来告诉服务端用于数字签名时使用的签名/哈希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)列表包含一个签名/哈希signature/hash)对,按照偏好排序。

注:不是所有的哈希Hash)和签名Signature)都能配对

?

由于密码套件Cipher Suite)中也定义了允许的签名算法,但没有定义哈希算法,因此在结合该扩展使用时会变得比较复杂,参见2.4.2和2.4.3章节

如果客户端仅支持默认的哈希签名算法,则可能忽略签名算法signature_algorithm)扩展。如果客户端不支持默认算法,或支持其他签名/哈希signature/hash)算法,则必须发送签名算法扩展并列出接受的算法。

如果客户端没有发送签名算法扩展,则服务端必须:

  • 如果协商的密钥交换算法为RSADHE_RSADH_RSARSA_PSKECDH_RSAECDHE_RSA其中之一,则认为客户端发送了{ sha1rsa }
  • 如果协商的密钥交换算法为DHE_DSSDH_DSS其中之一,则认为客户端发送了{ sha1dsa }
  • 如果协商的密钥交换算法为ECDH_ECDSAECDHE_ECDSA其中之一,则认为客户端发送了{ sha1ecdsa }

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

服务端不能发送该扩展,但服务端必须支持接收该扩展。

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

2.4.2 Server Certificate(服务端证书)

?当两端需要使用基于证书的认证时,服务端端必须发送证书Certificate)消息。该消息总是紧跟在ServerHello消息之后。当选中的密码套件使用RSAECC(基于椭圆曲线数学的公开密钥加密算法)或ECDHE(短暂椭圆曲线迪菲-赫尔曼算法)算法时,本消息的内容为服务端的签名证书和加密证书;当选中的密码套件使用IBC(基于标识的密码)或IBSDH(基于标识的超奇异迪菲-赫尔曼算法)算法时,本消息的内容为服务端表示和IBC公共参数,用于客户端和服务端协商IBC公共参数。

证书消息会发送服务端证书链certificate chain)。证书必须符合协商出来的密钥交换算法和扩展。?

证书消息结构如下:

opaque ASN.1Cert<1..2^{24}-1>;
struct {
? ?ASN.1Cert certificate_list<0..2^{24}-1>;----证书序列(链),签名证书在前,加密证书在后详见
}数字证书(Certificate);

opaque ASN.1IBCParam<1..2^{24}-1>;
struct {
? ?opaque ibc_id<0..2^{16}-1>;----服务端标识
? ?ASN.1IBCParam ibc_parameter;----IBC公共参数,遵循ASN.1编码
}数字证书(Certificate);

certificate_list:这是一个证书序列(链)。发件人的证书必须位于列表的第一位。以下每个证书都必须直接验证其前面的证书。因为证书验证要求独立分发根密钥root keys),因此在假设远程端必须已经拥有该证书才能在任何情况下对其进行验证的情况下,指定根证书颁发机构root certificate authority)的自签名证书可能会从链中省略。

客户端响应证书请求certificate request)消息的消息类型和结构与服务端相同。当客户端没有合适的证书时,可能不会发送证书。

证书必须是X509v3,除非明确协商使用其他类型的证书;证书类型必须能适用于已经确定的密钥交换算法。密钥交换算法和证书密钥类型的关系如下所示:

       TLS1.2密钥交换算法与证书密钥类型关系表

密钥交换算法         |  证书key类型
--------------------|-------------------------------------------------------------------------------------------------
RSA/RSA_PSK         | RSA公钥,证书必须能够用于加密(当出现密钥使用(key esage)扩展时,则必须设置密钥加密(keyEncipherment)位)
DHE_RSA/ECDHE_RSA   | RSA公钥,证书必须能够用于签名(当出现密钥使用(key esage)扩展时,则必须设置数字签名(digitalSignature)位)
DHE_DSS             | DSA公钥,证书必须能够用于签名(结合服务端密钥交换(server key exchange)消息中出现的哈希算法)
DH_DSS/DH_RSA?      |?Diffie-Hellman公钥,(当出现密钥使用(key esage)扩展时,则必须设置密钥协议(keyAggrement)位)
ECDH_ECDSA/ECDH_RSA | ECDH-capable公钥,该公钥必须使用客户端支持的曲线(curve)和(point)格式
ECDHE_ECDSA         | ECDSA-capable公钥,证书必须允许密钥用于使用将在服务端密钥交换(server key exchange)消息中使用的哈希
?                   | 算法进行签名。该公钥必须使用客户端支持的曲线(curve)和(point)格式


       GMTLS密钥交换算法与证书密钥类型关系表

     密钥交换算法       |       证书key类型
-----------------------|------------------------------------------
      RSA              |       RSA公钥,必须使用加密证书中的公钥
      IBC              |       服务端标识和IBC公共参数
      IBSDH            |       服务端标识和IBC公共参数
      ECC              |       ECC公钥,必须使用加密证书中的公钥
      ECDHE            |       ECC公钥,必须使用加密证书中的公钥

注:ECDHElliptic Curve Diffie-Hellman具备椭圆曲线迪菲-赫尔曼);ECDSAElliptic Curve Digital Signature Algorithm椭圆曲线数字签名算法

server_nametrusted_ca_keys扩展用于指导证书选择。

如果客户端提供了签名算法signature_algorithm)扩展,那么服务端提供的所有证书都必须使用该扩展中出现的哈希/签名hash/signature)算法对进行数字签名,请注意,这意味着可以使用不同的签名算法(例如,使用DSA密钥签名的RSA密钥)对包含一个签名算法的密钥的证书进行签名。这也是与TLS1.1不同的地方,后者要求算法相同。注意,这也意味着DH_DSSDH_RSAECDH_ECDSAECDH_RSA密钥交换算法不限制用于签署证书的算法。固定DH证书可以使用扩展中出现的任何哈希/签名算法对进行签名。

?

如果服务端拥有多个证书,客户端会根据上述标准(以及其他标准,如传输层端点、本地配置和首选项等)挑选其中一个进行验证;如果服务端只有一个证书,则必须满足所有标准。

注:有些证书使用当前无法与TLS一起使用的算法/and/or)算法组合。例如,不能使用具有RSASSA-PSS签名密钥的证书(SubjectPublicKeyInfo中的id-RSASSA-PSS OID),因为TLS没有定义相应的签名算法。

由于为TLS协议指定了指定新密钥交换方法的密码套件,因此它们将包含证书格式和所需的编码密钥信息。

2.4.3 Server Key Exchange Message(服务器密钥交换消息)

该消息会在服务端证书server certificate)之后发送,且仅在服务端证书消息不足以提供用于客户端交换预主密钥premaster secret)的数据时,服务端才会发送服务端密钥交换ServerKeyExchange)消息。密钥交换算法会发送该消息:DHE_DSSDHE_RSADH_anon

以下密钥交换方法发送服务端密钥交换消息是不合法的:RSADH_DSSDH_RSA

此消息传递密码信息,以允许客户端传递预主密钥迪菲-赫尔曼Diffie-Hellman)公钥,客户端可以使用该公钥完成密钥交换(结果是预主密钥)或其他算法的公钥。

TLS 1.2?服务端密钥交换消息结构如下:

enum {
dhe_dssdhe_rsadh_anonrsadh_dssdh_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>;----服务端迪菲-赫尔曼公共值(g^Xmod p
} 服务端DH参数(ServerDHParams);----短暂迪菲-赫尔曼参数(Ephemeral DH parameters

struct {
? ? selectKeyExchangeAlgorithm)?{
? ? ? ? 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 {};
? ? };
} 服务端密钥交换(ServerKeyExchange);

注:signed_params签名内容包含客户端服务端的随机数以及交换算法的参数。

如果客户端提供了签名算法signature_algorithm)扩展,则该消息中必须使用客户端签名算法扩展中出现的哈希/签名hash/signature)算法对。但在实际使用中会出现不一致的情况,如客户端提供了DHE_DSS密钥交换但在签名算法扩展中却忽略所有的DSA(即扩展中忽略了密码套件cipher suite)中定义的签名算法)。为了能够正确地协商,服务端必须在选择前校验所有的密码套件签名算法扩展(即密码套件中的签名算法与扩展不一致时)。除此之外,哈希/签名算法必须与服务端证书中的密钥key)相匹配。RSA密钥可能与任何哈希算法兼容(签名与哈希组合的方式受限于证书)。

GMTLS? 服务端密钥交换消息结构如下:

enum {?
? ? ECDHEECCIBSDHIBCRSA
} 密钥交换算法(KeyExchangeAlgorithm);

struct {
? ? selectKeyExchangeAlgorithm)?{
? ? ? ? case ECDHE
? ? ? ? ? ? ServerECDHEParams params;---服务端的密钥交换参数,详见
? ? ? ? ? ? digitally-signed struct {
? ? ? ? ? ? ? ??opaque client_random?[32];
????????????????opaque server_random?[32];
????????????????ServerECDHEParams params
? ? ? ? ? ? } signed_params
? ? ? ? case ECC
? ? ? ? ? ? digitally-signed struct {
? ? ? ? ? ? ? ??opaque client_random?[32];
????????????????opaque server_random?[32];
????????????????opaque ?ASN.1Cert<1..2^{24}-1>
? ? ? ? ? ? } signed_params
? ? ? ? case IBSDH
? ? ? ? ? ? ServerIBSDHParams params;---服务端的密钥交换参数,密钥交换参数格式参见SM9算法
? ? ? ? ? ? digitally-signed struct {
? ? ? ? ? ? ? ??opaque client_random?[32];
????????????????opaque server_random?[32];
????????????????ServerIBSDHParams params
? ? ? ? ? ? } signed_params
? ? ? ? case IBC
? ? ? ? ? ? ServerIBCParams params;---服务端的密钥交换参数,密钥交换参数格式参见SM9算法
? ? ? ? ? ? digitally-signed struct {
? ? ? ? ? ? ? ??opaque client_random?[32];
????????????????opaque server_random?[32];
????????????????ServerIBCParams params
????????????????opaque IBCEncryptionKey[1024];---服务端的加密公钥,长度为1024字节
? ? ? ? ? ? } signed_params
? ? ? ? case RSA
? ? ? ? ? ? digitally-signed struct {
? ? ? ? ? ? ? ??opaque client_random?[32];
????????????????opaque server_random?[32];
????????????????opaque ?ASN.1Cert<1..2^{24}-1>
? ? ? ? ? ? } signed_params;---详见
? ? };
} 服务端密钥交换(ServerKeyExchange);

ServerECDHEParams服务端的密钥交换参数,当使用SM2算法时,交换的参数见GM/T 0009,其中服务端的公钥不需要交换,客户端直接从服务端的加密证书中获取。

签名参数signed_params:当密钥交换方法为ECDHEIBSDHIBC时,签名参数是服务端对双方随机数和服务端密钥交换参数的签名;当密钥交换方法为ECCRSA时,signed_params是服务端对双方随机数和服务端加密证书的签名。

2.4.4 Certificate Request(证书请求)

?当非匿名服务端需要对客户端进行认证,则应发送此消息,要求客户端发送自己的证书。

该消息紧跟在服务端密钥交换ServerKeyExchange)之后。

enum {
? ? rsa_sign(1),----包含RSA密钥的证书
? ? dss_sign(2),----包含DSA密钥的证书
? ? rsa_fixed_dh(3),----包含static DH密钥的证书,详见
? ? dss_fixed_dh(4),----包含static DH密钥的证书,详见
? ? rsa_ephemeral_dh_RESERVED(5),
? ? dss_ephemeral_dh_RESERVED(6),
? ? fortezza_dms_RESERVED(20),
? ? ecdsa_sign(64),
? ? ibc_params(80),(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);

静态DH算法static DH):静态DH算法是一种有一方的私钥是静态的,也就是每次密钥协商的时候有一方的私钥都是一样的密钥交换算法。这种算法通常用于服务器方固定,而客户端的私钥则是随机生成的。在静态DH算法中,DH交换密钥只有客户端的公钥是变化的,而服务端公钥是不变的。然而,这种方法已经被废弃了,因为它不具备前向安全性。随着时间延长,黑客可能会截获海量的密钥协商过程的数据,并利用这些数据暴力破解出服务器的私钥,从而计算出会话密钥,导致之前截获的加密数据被破解。

支持的签名算法supported_signature_algorithms):服务端支持的哈希/签名hash/signature)算法,按偏好排序。

证书认证机构certificate_authorities,即CA):可接受的CA可分辨名称distinguished names,即DN),DER编码格式。这些DN字段可能会指定一个根CAroot CA)或从属CAsubordinate CA)期望的DN,因此该消息可以用来描述已知的根以及期望的授权范围。如果证书认证机构certificate_authorities)列表为空,则客户端可能会选择发送合适的客户端证书类型ClientCertificateType类型的证书。

证书类型certificate_types)和支持的签名算法supported_signature_algorithms)字段交互比较复杂,证书类型SSLv3版本中出现,但仍然待确定,它的大部分功能被supported_signature_algorithms取代。规则如下:

客户端提供的证书必须使用支持的签名算法中定义的哈希/签名hash/signature)算法进行签名

客户端提供的证书必须包含于证书类型兼容的密钥key),如果密钥是一个签名密钥,则该密钥必须能配合支持的签名算法中的哈希/签名算法使用。

由于历史原因,一些客户端证书类型包含用于签名这些证书的算法。如在早期的TLS版本中,rsa_fixed_dh表示使用RSA签名的证书且该证书包含一个static DH密钥。在TLS 1.2中,该功能被支持的签名算法supported_signature_algorithms)废弃,证书类型不再限制用于签名证书的算法。如:服务端发送的dss_fixed_dh类型的证书,以及{{sha1dsa},{sha1rsa}}的签名类型,客户端可能会回复带DH密钥的证书,使用RSA-SHA1签名。

2.4.5 Server Hello Done(服务端问候结束)

服务端发送该消息用来表示完成了密钥交换key exchange)消息的交互,客户端可以进入密钥交换阶段。客户端在接收到服务端问候结束(ServerHelloDone)消息后,会对服务端提供的证书进行校验,如果证书校验通过则继续对服务端问候Hello)参数进行校验。

2.4.6 Client Certificate(客户端证书)

该消息是客户端接收到服务端问候结束(ServerHelloDone)消息之后发送的第一个消息,且只在服务端请求证书时发送。如果没有合适的证书,客户端也必须发送该消息,消息中不包含任何证书,即证书列表certificate_list)长度为0。如果客户端没有发送任何证书,服务端可能会握手(不对客户端进行认证),也可能会返回握手失败致命报警handshake_faiure fatal alert)。如果证书链的某些地方不可接受(如签名的CA不可知),服务端可能会继续握手或返回致命报警fatal alert)。

该消息会传递客户端的证书链,服务端会使用该消息来校验证书认证CertificateVertify)消息或采用非短暂迪菲-赫尔曼算法non-ephemeral Diffie-Hellman)计算预主密钥premaster secret)(non-ephemeral Diffie-Hellman)。证书必须匹配协商的密码套件cipher suite)的密钥交换算法以及扩展。

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

客户端证书必须于证书请求CertificateRequest)中的证书类型匹配。

       TLS1.2密钥交换算法与证书密钥类型关系表

密钥交换算法               |  证书key类型
--------------------------|-------------------------------------------------------------------------------------------------
rsa_sign                  | RSA公钥,证书必须能够用于签名(结合证书验证(certificate verify)消息中的哈希签名(hashsignature)算法)
dss_sign                  | DSA公钥,证书必须能够用于签名(结合证书验证(certificate verify)消息中的哈希签名(hashsignature)算法)
ecdsa_sign                | ECDSA-capable公钥,证书必须能够用于签名(结合证书验证(certificate verify)消息中的哈希签名
?                         | (hashSignature)算法);公钥必须使用服务端支持的曲线(curve)和(point)格式
rsa_fixed_dh/dss_fixed_dh |?Diffie_Hellman公钥,必须与服务端密钥(server key)的预主密钥(premaster)相同
ECDH_ECDSA/ECDH_RSA       | ECDH-capable公钥,该公钥必须使用客户端支持的曲线(curve)和(point)格式
rsa_fixed_ecdh/           | ECDH-capable公钥,必须与服务端密钥(server key)的曲线(curve)相同,必须使用服务端支持的(point)格式
ecdsa_fixed_ecdh          | 

       GMTLS密钥交换算法与证书密钥类型关系表

     密钥交换算法       |       证书key类型
-----------------------|------------------------------------------
      RSA              |       RSA公钥,必须使用加密证书中的公钥
      IBC              |       服务端标识和IBC公共参数
      IBSDH            |       服务端标识和IBC公共参数
      ECC              |       ECC公钥,必须使用加密证书中的公钥
      ECDHE            |       ECC公钥,必须使用加密证书中的公钥

如果证书请求Certificate Request)消息中的证书认证机构certificate_authorities)列表不为空,则证书链中的一个证书应由其中一个列出的CA颁发。

证书必须使用可接受的哈希/签名算法对hash/signature algorithm pair)进行签名,这放宽了以前版本的TLS中对证书签名算法的限制。

2.4.7 Client Key Exchange Message(客户端密钥交换消息)

如果服务端请求客户端证书,本消息紧跟于客户端证书Client Certificate)消息之后,否则本消息是客户端己收到服务端问候完成Server Hello Done)消息后所发送的第一条消息。

发送该消息表示预主密钥premaster secret)已经生成,通过RSA加密后直接传输或通过Diffie-Hellman参数的传输来设置预主秘密,该参数将允许每一方同意相同的预主秘密。

如果密钥交换算法使用RSA算法、ECC算法和IBC算法,本消息中包含预主密钥,该预主密钥由客户端产生,采用服务端的加密公钥进行加密。当服务端收到加密后的预主密钥后,利用相应的私钥进行解密,获取所述预主密钥的明文。如果是IBC算法,客户端利用获取的服务端标识和IBC公开参数,产生服务端公钥。如果是RSA算法,建议使用PKCS#1?版本1.5对RSA加密后的密文进行编码。若甫哦密钥交换算法使用ECDHE算法或IBSDH算法,本消息中包含计算预主密钥的客户端密钥交换参数。

当客户端使用短暂的Diffie-Hellman指数时,则此消息包含客户端的Diffie-Hellman公共值。如果客户端正在发送包含静态DH指数static DH exponent)的证书,即它正在进行fixed_DH客户端身份验证,则此消息必须发送,但必须为空。

?

struct {
? ? selectKeyExchangeAlgorithm)?{
? ? ? ? case rsa
? ? ? ? ? ? EncryptedPreMasterSecret;----加密的预主密钥
? ? ? ? case dhe_dss
? ? ? ? case dhe_rsa
? ? ? ? case dh_dss
? ? ? ? case dh_rsa
? ? ? ? case dh_anon
? ? ? ? ? ? ClientDiffieHellmanPublic;----客户端的Diffie-Hellman公钥
? ? }exchange_keys
}?客户端密钥交换(ClientKeyExchange);

GMTLS? 服务端密钥交换消息结构如下:

struct {
? ? selectKeyExchangeAlgorithm)?{
? ? ? ? case ECDHE
? ? ? ? ? ? Opaque ClientECDHEParams<1..2^{16}-1>;----客户端ECDHE参数,详见
? ? ? ? case IBSDH
? ? ? ? ? ? Opaque ClientIBSDHParams<1..2^{16}-1>;----使用IBSDH算法时,客户端的密钥交换参数
? ? ? ? case ECC
? ? ? ? ? ? opaque ECCEncryptedPreMasterSecret<0..2^{16}-1>;----使用ECC加密算法时,用服务端加密公钥加密的预主密钥
? ? ? ? case IBC
? ? ? ? ? ? opaque IBCEncryptedPreMasterSecret<0..2^{16}-1>;----使用IBC加密算法时,用服务端加密公钥加密的预主密钥
? ? ? ? case RSA
? ? ? ? ? ? opaque RSAEncryptedPreMasterSecret<0..2^{16}-1>;----RSA加密的预主密钥,详见
? ??}exchange_keys
} 服务端密钥交换(ServerKeyExchange);

ClientECDHEParams:使用ECDHE算法时,要求客户端发送证书。客户端的密钥交换参数,当使用SM2算法时,交换的参数见GM/T 0009。其结构如下:
struct {
? ? ECParameters? curve_params;----EC参数
? ? ECPoint public;----EC
} 客户端ECDHE参数ClientECDHEParams);
如果使用SM2算法,第一个参数不校验。

RSAEncryptedPreMasterSecret:使用RSA加密算法时,用服务端加密公钥加密的预主密钥。预主密钥的数据结构如下:
struct {
? ? ProtocolVersion client_version;----客户端所支持的版本号。服务端要检查这个值是否与客户端问候ClientHello中所发送的值相匹配
? ? opaque random[46];----46字节的随机数
} 预主密钥PreMasterSecret);

2.4.7.1 RSA-Encrypted Premaster Secret Message

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

struct {
? ? ProtocolVersion client_version;----客户端所支持的版本号。服务端要检查这个值是否与客户端问候ClientHello中所发送的值相匹配
? ? opaque random[46];----46字节的随机数
} 预主密钥PreMasterSecret);

struct {
? ? public-key-encrypted PreMasterSecret pre_master_secret;----客户端用于生成主密钥master secret)的随机值,详见
} 加密预主密钥EncryptedPreMasterSecret);

pre_master_secret预主密钥PremasterSecret)中的版本号由ClientHello.client_version提供,而非协商的链路版本,该设计用于防止回滚rollback)攻击。不幸的是有些老的实现会使用协商出的版本号,与这些实现交互时可能会失败。

客户端的实现中必须在预主密钥中发送正确的版本号。如果ClientHello.client_versionTLS1.1或更高,服务端的实现中必须检查该版本号;如果版本号为1.0或更早,服务端可能会检查版本号,也可能不检查。

TLS 服务端在加密预主密钥失败或遇到非期望的版本号时不能发送报警alert),而应该使用一个随机生成的预主密钥进行握手。

公钥加密的数据是一个0~2^{16}-1长度的不透明向量opaque vector),因此客户端密钥交换ClientKeyExchange)中使用RSA加密的预主密钥PreMasterSecret)之前为2个字节的长度字段(表示其可变长度大小)。加密预主密钥EncryptedPreMasterSecret客户端密钥交换的唯一数据。

2.4.7.2 Client Diffie-Hellman Public Value(客户端DH公钥值)

客户端数字证书certificate)中没有出现迪菲-赫尔曼公钥Diffie-Hellman public)的值时发送,该消息是客户端密钥交换的变体。当数字证书中已经包含合适的迪菲-赫尔曼密钥Diffie-Hellman key),用于固定DHfixed_dh客户端认证,此时必须于发送客户端密钥交换client key exchange)消息,但必须为空。

2.4.8 Certificate Verify(证书验证)

该消息用于提供客户端证书的校验,证明客户端拥有这些证书的私钥。仅在客户端证书client certificate)由签名功能时发送(除了包含fixed Diffie-Hellman参数的证书),跟在客户端密钥交换client key exchange)消息之后发送。

TLS1.2 Certificate Verify结构

struct {
? ? digitally-signed struct?{
? ? ? ? opaque handshake_messages?[handshake_messages_length]
? ? }
} 证书验证CertificateVerify);

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

struct {
? ? HandshakeType msg_type;----握手类型(handshake type
? ? uint24 length; ----消息中的字节
? ? selectHandshakeType)?{
? ? ? ? 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)为止的所有可能的哈希算法。服务端可以通过在证书请求CertificateRequest)中设置限制来减小运算。

签名的哈希/签名算法hash/signature algorithm)必须是证书请求消息中提供的。除此之外,哈希/签名算法必须与客户端端的数字证书兼容。

GMSSL Certificate Verify结构

struct {
? ? Signature?signature?
} 证书验证CertificateVerify);

Signature?的结构如下:

enum?{?
? ? rsa_sha1,rsa_sm3,ecc_sm3?,ibs_sm3
} 签名算法SignatureAlgorithm);
struct {
? ? selectSignature?Algorithm){
? ? ? ? case rsa_sha1?:
? ? ? ? ? ? digitally-signed?struct {
? ? ? ? ? ? ? ? opaque sha1_hash?[20];
? ? ? ? ? ? };
? ? ? ? case rsa_sm3 :
? ? ? ? ? ? digitally-signed?struct {
? ? ? ? ? ? ? ? opaque sm3_hash?[32];
? ? ? ? ? ? };
? ? ? ? case ecc_sm3 :
? ? ? ? ? ? digitally-signed?struct {
? ? ? ? ? ? ? ? opaque sm3_hash?[32];
? ? ? ? ? ? };
? ? ? ? case ibs_sm3 :
? ? ? ? ? ? digitally-signed?struct {
? ? ? ? ? ? ? ? opaque sm3_hash?[32];
? ? ? ? ? ? };
? ? }?body
}?签名(Signature);

sm3_hashsha1_hash是指哈希运算的结果,运算的内容是值客户端问候(ClientHello)开始直到本消息为止(不包括本消息)的所有握手有关的消息(加密证书要包在签名计算中),包括握手消息的类型和长度域。

2.4.9 Finished(结束)

服务端客户端各自在密码规格变更ChangeCipherSpec)消息之后发送本消息,用于验证密钥交换过程是否成功,并校验握手过程的完整性。

本消息用本次握手过程协商出的算法和密钥保护。

本消息的接收方必须校验消息内容的正确性。一旦一方发送了握手结束消息,并且接收到了对方握手结束消息并通过校验。就可以使用该连接进行数据安全传输。

struct {
? ? opaque verify_data?[?verify_data_length ];----校验数据,详见
} 结束(Finished);

verify_data:该数据产生方法如下:
PRF(master_secretfinished_labelHash(?handshake_messages?)) [0..verify_data_length-1];
在旧版本中的TLSverify_data为12字节octets),当前版本则却决于使用的密码套件。任何没有明确指定验证数据长度verify_data_length)的默认为12字节

1 octet = 8 bit

finished_label:客户端发送的结束消息相关的字符串为“client finished”,服务端发送的为“server finished

Hash:表示握手消息的哈希,该哈希伪随机算法PRF)使用的哈希相同。密码套件定义的PRF必须定义用于结束计算的Hash,若为GMSSL哈希算法为SM3

handshake_messages:指自客户端问候Client Hello)开始直到本消息为止(不包括本消息、密码规格变更消息(ChangeCipherSpec))的所有与握手有关的消息,包括握手消息的类型和长度域。不包含记录Record)类型、问候请求HelloRequest)的消息。

在握手过程中,如果没有在合适的时机处理结束Finished)消息,则返回致命fatal)错误。

A.5 The Cipher Suite(密码套件)

本节定义了客户端问候ClientHello)和服务端问候ServerHello)使用的密码套件cipher suite)。TLS初始化的密码套件TLS_NULL_WITH_NULL_NULL,但不能使用该值进行协商(没有提供任何保护)

CipherSuite TLS_NULL_WITH_NULL_NULL               = { 0x00,0x00 };

如下密码套件需要服务端提供用于密钥交换的RSA证书。服务端可能会在证书请求(CertificateReuqest)中请求具有签名的能力或功能的证书signature-capable certificate)。?

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 };

如下密码套件用于。DH表示服务端的证书包含CA签名的迪菲-赫尔曼算法Diffie-Hellman)参数。DHE表示短暂迪菲-赫尔曼算法ephemeral Diffie-Hellman),此时使用具有签名的能力或功能的证书signature-capable certificate)(该证书也被CA签名)签名迪菲-赫尔曼算法参数。服务端使用的签名算法在密码套件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 };

服务端可以向客户端请求具有签名的能力或功能的证书signature-capable certificate)或DH证书来对客户端进行认证。客户端提供的所有Diffie-Hellman证书必须使用服务端提供(或生成)的参数

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

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 };

目前使用最多的密码套件TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256解析如下:

ECDHE为密钥交互算法,RSA为签名算法,AES_123_GCM为使用GCM模式下的128位AEAD加密算法,SHA256为创建消息摘要的MAC算法

密码套件的详细定义可以参考Cipher suite definitionsTLS密码套件(cipher suite)-CSDN博客Cipher suite definitions

B. 术语表

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数字签名使用公钥和单向哈希函数来产生签名数据。

Data Encryption StandardDES):数据加密标准,普遍使用的对称加密算法

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

client write key:用于加密客户端的数据

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

Message Authentication Code (MAC):消息认证码,单向哈希处理的数据,生成消息摘要。

master secret主密钥,用于生成用于数据加密和完整性校验的会话密钥

RC4:流加密。

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

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

F.1.1.2 RSA Key Exchange and Authentication(RSA密钥交换和身份验证)

使用RSA密钥交换key exchange)和服务端认证可以同时进行。服务端的证书中包含了公钥。注意,使用静态的RSA密钥可能导致该静态密钥保护的所有会话session)失效。

客户端验证完服务端的证书后,会使用证书中的公钥对预主密钥pre_master_secret)加密。服务端成功解密预主密钥之后会并处理结束Finished)消息之后,服务端认为客户端服务端的认证成功。

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

F.1.1.3 Diffie-Hellman Key Exchange with Authentication(带身份验证的迪菲-赫尔曼密钥交换)

当使用Diffie-Hellman密钥交换时,服务端可以提供包含固定DHfixed Diffie-Hellman)参数的证书或使用服务端密钥交换ServerKeyExchange)消息发送一系列使用DSARSA签名的临时DH参数。这些临时参数在签名前会使用问候随机数hello.random)进行哈希,用以防止攻击者使用老参数进行攻击。其他场景下,客户端可以通过验证证书和签名来确保该消息来自服务端

如果客户端有包含固定DHfixed Diffie-Hellman)参数的证书,该证书中的信息可以用于完成密钥交互。这种情况下,客户端服务端会生成相同的DH结果,即预主密钥pre_master_secret)。为了防止预主密钥过长地滞留在内存中,在完成它的计算后应该尽快将其转化为主密钥master secret)。客户端DH参数必须兼容服务端DH参数。

如果客户端使用DSARSA证书或其未被认证,它会在客户端密钥交换clientKeyExchange)消息中发送临时的参数,后续可能使用证书验证certificate verify)消息对其进行认证。

如果使用DH密钥对进行多个握手,由于客户端服务端都拥有一个包含固定DHfixed Diffie-Hellman)密钥对的证书或服务端重用DH密钥,需要注意方式子群攻击Subgroup attack)。

小型的子群攻击攻击可以通过使用DHE密码套件DHE cipher suite)并未每个握手生成新的DH私钥来解决。

?由于TLS允许服务端提供任意的DH组DH groups),客户端应该对DH组进行校验(与本地策略比较)。

F.1.3 Detecting Attacks Against the Handshake Protocol(检测针对握手协议的攻击)

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

F.1.4 Resuming Sessions(重用会话)

?当需要重用会话session)来建立链接时,会使会话主密钥master secret)生成新的ClientHello.randomSeverHello.random

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

F.2 Protecting Application Data(保护应用程序数据)

使用使用ClientHello.randomServerHello.random生成的主密钥master secret)用于生成用于数据加密和完整性校验的会话密钥

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

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

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

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

F.4 Security of Composite Cipher Modes(复合密码模式的安全性)

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

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

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

TLS使用块加密+HMAC,流加密+HMAC以及AEAD(Authenticated-Encryption With Additional data,附加数据的身份验证加密)来保证数据的完整和安全。TLS1.3中废除了除AEAD外的加密方式

注:

  • 实际中基本不使用流加密方式
  • 密钥交换(key exchange)的目的是生成预主密钥pre_master_secret),最终生成主密钥master secret),通过结束Finished)来校验预主密钥的正确性。
  • 数字签名过程为发送端首先使用哈希算法生成消息摘要,然后使用签名算法生成数字签名;接收端为上述逆过程

参考:

https://www.cnblogs.com/charlieroro/p/10714783.html

【精选】《商用密码应用与安全性评估》第一章 密码基础知识-小结_商用密码应用安全性评估-CSDN博客

RFC 5246 TLS1.2

中华人民共和国密码行业标准 GM/T 0024-2014 SSL VPN 技术规范

百度百科相关词条

TLS密码套件(cipher suite)-CSDN博客

IBM Documentation

文章来源:https://blog.csdn.net/Hyacinth12138/article/details/134341722
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。