TLS协议

文章总结于rfc2246rfc4346rfc5246

The TLS Record Protocol

TLS记录协议(The TLS Record Protocol),是一个分层协议,每层的消息都包含长度、描述和内容。

记录协议发送数据是将消息分段转换为块、可选压缩、应用MAC(消息认证码)并传输结果;接收数据是解密、验证、解压缩、重新合并,然后传递给高层协议。

记录协议包括以下四类:

  • 握手协议(the handshake protocol)
  • 警报协议(the alert protocol)
  • 修改密码协议(the change cipher spec protocol)
  • 应用数据协议(the application data protocol)

为了方便扩展,TLS支持添加其他记录类型,任何新的记录类型应该是立即分配唯一的一个ContentType类型值,但接收端将会忽略它不理解的ContentType类型值。

Connection states

TLS连接状态(TLS Connection states),是TLS记录的操作环境协议,它指定了压缩算法、加密算法、MAC算法以及这些算法的参数(MAC秘钥、批量加密秘钥和读写方向连接的IV)。

总有四个连接状态未完成:当前读写状态和挂起的读写状态。所有记录在当前读写状态下处理,挂起状态的参数可以通过TLS握手协议进行设置。握手协议可以选择性地使任何一个待处理状态为当前状态,在这种情况下当前状态会变成挂起状态,挂起状态会重新初始化成空状态,没有通过安全参数初始化的状态是非法的。初始当前状态总是指定没有解密,压缩或者没有使用MAC。

TLS连接读写状态的安全参数是通过提供以下值来设置:

  • 连接端(connection end),在连接中此实体被认为是客户端还是服务器端;
  • 批量加密算法(bulk encryption algorithm),指明批量加密算法,包括该算法的秘钥大小,是分组密码还是流密码,如果是分组密码则指明秘钥大小,是否需要导出秘钥;
  • MAC算法(MAC algorithm),指明消息验证算法,包括返回的哈希的大小MAC算法;
  • 压缩算法(compression algorithm),指明压缩算法,包括完成压缩的所有信息;
  • 主密钥(master secret),在连接中的两个对等实体之间共享48 byte的秘钥;
  • 客户端随机数(client random),客户端提供32 byte值。
  • 服务器端随机数(server random),服务器提供的32 byte值。

利用语言表示这些参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
enum { server, client } ConnectionEnd;

enum { null, rc4, rc2, des, 3des, des40 } BulkCipherAlgorithm;

enum { stream, block } CipherType;

enum { true, false } IsExportable;

enum { null, md5, sha } MACAlgorithm;

enum { null(0), (255) } CompressionMethod;

/* The algorithms specified in CompressionMethod, BulkCipherAlgorithm, and MACAlgorithm may be added to. */

struct {
ConnectionEnd entity;
BulkCipherAlgorithm bulk_cipher_algorithm;
CipherType cipher_type;
uint8 key_size;
uint8 key_material_length;
IsExportable is_exportable;
MACAlgorithm mac_algorithm;
uint8 hash_size;
CompressionMethod compression_algorithm;
opaque master_secret[48];
opaque client_random[32];
opaque server_random[32];
} SecurityParameters;

记录层将使用安全参数生成一下六项:

1
2
3
4
5
6
client write MAC secret
server write MAC secret
client write key
server write key
client write IV (for block ciphers only)
server write IV (for block ciphers only)

server接收和处理记录时使用的是client发送的参数,反之亦然。
一旦设置了安全参数,并且秘钥已经生成,当前状态可以实例化这个连接状态,当前状态必须更新每个记录处理。
每个连接状态包括以下元素:

  • 压缩状态(compression state),压缩算法的当前状态;
  • 密码状态(cipher state),加密算法的当前状态,包括该连接的预定秘钥,如果是分组密码则运行CBC模式(TLS指定的唯一模式),这最初将包含该连接状态的IV和更新为包含加密的最后一个分组的密文或者在处理记录时解密,对于流密码,这将包含允许流继续加密或解密数据所需的任何必要状态信息;
  • MAC秘钥(MAC secret),上层生成的MAC秘钥;
  • 序列号(sequence number),每个链接态都包含一个序列号,该序列号为读和写状态分别维护,序列号是uint64类型,每当连接状态变成激活状态时,序列号必须设置为零,序列号在标记一个记录后递增,不能超过$2^{64}-1$。

Record layer

TLS记录层(TLS Record layer),接收高层协议的非空任意大小的数据。

Fragmentation

记录层将信息分割成TLSPlaintext,以$2^{14}$字节或者更少的分组携带数据的记录,客户端消息在记录层中不保留边界(多客户端相同的ContentType的消息也许会被合并成一个TLSPlaintext记录,或者单个消息也许会被分割成几个记录)。
TLSPlaintext结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct {
uint8 major, minor;
} ProtocolVersion;

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,上层协议用于处理封闭片段的类型
  • version,使用的协议版本
  • length,TLSPlaintext.fragment的大小,以字节为单位,不能超过$2^{14}$
  • fragment,应用数据,作为独立分组,由type字段指定的更高级别的协议处理

Note:可以聚合不同TLS记录层内容类型的数据,与其他内容类型相比,应用程序数据的传输优先级通常较低。

Record compression and decompression

所有记录都使用在当前会话状态中定义的压缩算法进行压缩,会有一个默认的压缩算法,初始化时定义为CompressionMethod.null。压缩算法将TLSPlaintext结构转换成TLSCompressed结构。只要连接状态处于活动状态,就会使用默认状态信息初始化压缩功能。
压缩必须是无损的,并且不可以增加内容长度超过1024byte,如果解压后的内容超过$2^{14}$字节长度的TLSCompressed.fragment,则应报告致命的解压缩失败错误。
TLSCompressed结构如下:

1
2
3
4
5
6
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压缩后的数据

Note:CompressionMethod.null操作是标识操作,不更改字段。解压缩函数负责确保消息不能导致内部缓冲区溢出。

Record payload protection

加密和MAC函数转换TLSCompressed结构进入TLSCiphertext结构。解密函数反转了过程。记录的MAC还包括序列号,以便可以检测到丢失、额外或重复的消息。
TLSCiphertext结构如下:

1
2
3
4
5
6
7
8
9
struct {
ContentType type;
ProtocolVersion version;
uint16 length;
select (CipherSpec.cipher_type) {
case stream: GenericStreamCipher;
case block: GenericBlockCipher;
} fragment;
} TLSCiphertext;

参数说明:

  • type,与TLSCompressed.type相同
  • version,与TLSCompressed.version相同
  • length,TLSCiphertext.fragment的长度,以字节为单位,不能超过$2^{14}+2048$
  • fragment,使用MAC加密TLSCompressed.fragment后的数据

Null or standard stream cipher

流密码将TLSCompressed.fragment结构转换为流TLSCiphertext.fragment结构,以及从流TLSCiphertext.fragment结构转换为流TLSCompressed.fragment结构。

1
2
3
4
stream-ciphered struct {
opaque content[TLSCompressed.length];
opaque MAC[CipherSpec.hash_size];
} GenericStreamCipher;

参数说明:

  • MAC格式,+表示级联

    1
    2
    3
    HMAC_hash(MAC_write_secret, seq_num + TLSCompressed.type +
    TLSCompressed.version + TLSCompressed.length +
    TLSCompressed.fragment));
  • seq_num,当前记录的序列号

  • hash,指定的哈希算法SecurityParamenters.mac_algorithm

Note:MAC计算在加密之前。
流密码加密整个块,包括MAC,对于不使用同步向量(如RC4)的流密码,一个记录末尾的流密码状态仅用于后续分组。如果CipherSuite是TLS_NULL_WITH_NULL_NULL,加密由身份操作组成。数据未加密并且MAC大小为零意味着不使用MAC。

CBC block cipher

对于分组密码(如RC2或DES),加密和MAC函数将TLSCompressed.fragment结构转换为块或从块转换为TLSCiphertext.fragment结构。
GenericBlockCipher结构如下:

1
2
3
4
5
6
block-ciphered struct {
opaque content[TLSCompressed.length];
opaque MAC[CipherSpec.hash_size];
uint8 padding[GenericBlockCipher.padding_length];
uint8 padding_length;
} GenericBlockCipher;

参数说明:

  • MAC,和上面介绍的一样
  • padding,添加填充以强制明文的长度为分组密码的分组长度的整数倍,最多可填充255 byte数据,只要结果是TLSCiphertext.length是分组的长度,基于对交换消息长度的分析,可能需要更长的长度来阻止对协议的攻击,每个uint8的填充数据值都应该是padding长度的值
  • padding_length,填充长度应该使得GenericBlockCipher结构的总大小是密码块长度的整倍数,合法值为0到255,这个值表示填充的长度,不包括padding_length本身。

加密数据长度(TLSCiphertext.length)大于TLSCompressed.length、CipherSpec.hash_sizepadding_length的总和。
例如:如果分组长度是8字节,则内容长度(TLSCompressed.length)是61字节,MAC长度是20字节,填充前的长度是82字节。因此,填充长度模数8必须等于6,以便总长度是8字节的偶数倍(分组长度)。填充长度可以是6、14、22等,最大254。如果填充长度是最小值6,则填充将是6个字节,每个字节包含值6。因此,GenericBlockCipher在分组之前的最后8个八位字节加密将是xx 06 06 06 06 06 06 06 06 06 06 06,其中xx是MAC的最后一个八位字节。
Note:在CBC(Cipher Block Chaining)模式下使用分组密码第一记录的IV(initialization vector)生成设置安全参数时的其他密钥和秘密,后续记录的IV是前一记录中的最后一个密文块。

Key calculation

秘钥计算(key calculation),记录协议需要一个算法来生成秘钥、IV和握手协议提供的安全参数提供的MAC秘钥。主秘钥被散列成一系列安全字节,这些安全字节被分配给当前连接状态所需的MAC秘密、密钥和非导出IV。
CipherSpecs要求客户机写入MAC秘密、服务器写入MAC秘密、客户机写入密钥、服务器写入密钥、客户机写入IV和服务器写入IV,它们按照该顺序从主秘密生成,未使用的值为空。

生成key的公式:

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

直到产生足够的输出。然后按以下方式对key_block进行分区:

1
2
3
4
5
6
client_write_MAC_secret[SecurityParameters.hash_size]
server_write_MAC_secret[SecurityParameters.hash_size]
client_write_key[SecurityParameters.key_material_length]
server_write_key[SecurityParameters.key_material_length]
client_write_IV[SecurityParameters.IV_size]
server_write_IV[SecurityParameters.IV_size]

client_write_IVserver_write_IV仅生成不可导出的分组密码。多余的key_block将会被丢弃。
可导出的加密算法(即CipherSpec.is_exportable为true),需要获得它们的最终写入的秘钥(final write keys):

1
2
3
4
5
6
7
8
9
10
11
final_client_write_key =
PRF(SecurityParameters.client_write_key,
"client write key",
SecurityParameters.client_random +
SecurityParameters.server_random);

final_server_write_key =
PRF(SecurityParameters.server_write_key,
"server write key",
SecurityParameters.client_random +
SecurityParameters.server_random);

可导出的加密算法仅通过hello message中的随机值来获取它们的IV:

1
2
iv_block = PRF("", "IV block", SecurityParameters.client_random +
SecurityParameters.server_random);

iv_block会被划分为两个IV:

1
2
client_write_IV[SecurityParameters.IV_size]
server_write_IV[SecurityParameters.IV_size]

Export key generation example

TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5要求两个加密密钥有5 byte随机字节和16 byte的MAC密钥,总共需要42个字节的密钥材料。PRF输出存储在key_block中。key_block被分区,写入密钥散布,因为这是可导出的加密算法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
key_block               = PRF(master_secret,
"key expansion",
server_random +
client_random)[0..41]
client_write_MAC_secret = key_block[0..15]
server_write_MAC_secret = key_block[16..31]
client_write_key = key_block[32..36]
server_write_key = key_block[37..41]
final_client_write_key = PRF(client_write_key,
"client write key",
client_random +
server_random)[0..15]
final_server_write_key = PRF(server_write_key,
"server write key",
client_random +
server_random)[0..15]

iv_block = PRF("", "IV block", client_random +
server_random)[0..15]
client_write_IV = iv_block[0..7]
server_write_IV = iv_block[8..15]

The TLS Handshake Protocol

TLS握手协议(The TLS Handshake Protocol)是由三个子协议组成,用于允许对等方就记录层的安全参数达成一致,认证,协商安全参数,相互报告错误情况。
握手协议负责会话协商,包括以下几个部分:

  • session identifier
    会话标识符,服务器选择一个任意的字节序列去确认一个活跃的或者可恢复的会话状态
  • peer certificate
    同级证书,每级的X509v3 [X509]证书,允许为空
  • compression method
    压缩方法,加密之前的压缩数据算法
  • cipher spec
    密码规格,
  • master secret
    主密钥,client和server共享的48 byte秘钥
  • is resumable
    标记当前session是否可以用来初始化新连接

然后,这些项用于创建安全参数,供记录层在保护应用程序数据时使用。通过TLS握手协议的恢复特性,可以使用相同的会话实例化许多连接。

Change cipher spec protocol

更改密码规范协议(Change cipher spec protocol),用于转变加密策略。该协议由单个消息组成,在当前(不是挂起)连接状态下对其进行加密和压缩。
该消息由值为1的1 byte表示:

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

client和server都可以发送交换密码规范消息,通知接收方以后的记录将会使用新协商的CipherSpec和密钥。接收到此消息后,接收方指示记录层立即将读取挂起状态(read pending state)复制到读取当前状态(read current state);在发送此消息后,发送方应立即指示记录层将写挂起状态(write pending state)设置为写活动状态(write active state)。握手期间,商定安全参数后会发送交换密码规范消息,之后才发送确认结束信息。

Alert protocol

警报协议(Alert protocol),TLS记录层支持的内容类型之一是警报类型。警报消息传达严重程度和描述信息,具有致命级别的警报消息导致连接立即终止,在这种情况下,对应于会话的其他连接可以继续,但会话标识符必须无效,从而防止失败的会话被用于建立新连接。像其他消息一样,警报消息被加密和压缩,由当前连接状态指定。
Alert结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
enum { warning(1), fatal(2), (255) } AlertLevel;

enum {
close_notify(0),
unexpected_message(10),
bad_record_mac(20),
decryption_failed(21),
record_overflow(22),
decompression_failure(30),
handshake_failure(40),
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(60),
protocol_version(70),
insufficient_security(71),
internal_error(80),
user_canceled(90),
no_renegotiation(100),
(255)
} AlertDescription;

struct {
AlertLevel level;
AlertDescription description;
} Alert;

TLS1.3

Closure alerts

关闭警报(Closure alerts),为了避免截断攻击,客户端和服务器必须共享连接即将结束的知识。任何一方都可以发起关闭消息的交换。
close_notify:此消息通知收件人,发件人不会在此连接上再发送任何消息。注意,从TLS 1.1开始,未能正确关闭连接不再要求不恢复会话。这是从TLS 1.0到与广泛的实现实践相符的改变。
任何一方都可以通过发送close_notify警报来发起关闭。在闭包警报之后接收的任何数据都被忽略。除非已经发送了某些其他致命警报,否则在关闭连接的写端之前,要求每一方发送close_notify警报。对端必须发出close_notify警报进行响应,并立即关闭连接,丢弃任何挂起的写操作。在关闭连接的读取端之前,关闭的发起者不需要等待响应的close_notify警报。
如果使用TLS的应用程序协议规定,在TLS连接关闭之后,任何数据都可以通过底层传输传输,则TLS实现必须在向应用程序层指示TLS连接已结束之前接收响应的close_notify警报。如果应用程序协议不会传输任何附加数据,但是只关闭底层的传输连接,那么实现也许选择关闭传输而不等待响应的close_notify。这个标准的任何部分都不应该用来规定TLS的使用概要文件管理其数据传输的方式,包括何时打开或关闭连接。
Note:关闭连接时,会在链路销毁前可靠地传输传输挂起消息。

Error Alerts

TLS握手协议中的错误处理非常简单。当检测到错误时,检测到错误的一方向另一方发送消息。在发送或接收到致命警报消息时,各方立即关闭连接。server和client必须丢弃所有会话表示符(session-identifiers)、秘钥和与连接相关的秘钥。因此,任何以致命警报终止的连接都不能恢复。

每当实现遇到定义为警报的条件是fatal时,它必须在关闭连接之前发送适当的警报。对于没有明确指定警报级别的所有错误,发送方可以自行决定是否将其视为fatal错误。如果实现选择发送警报,但随后打算立即关闭连接,则发出的警报级别必须是fatal级别的。

如果警报日志级别是warning,通常连接可以正常继续。如果接受方不继续连接(例如:收到不愿接受的“不重新协商(no_renegotiation alert)”警报后),它应该发送fatal警报终止连接。鉴于此,发送方通常无法知道接收方的行为。因此,当发送方希望继续连接时,warning警报并不十分有用,因此有时会省略。例如,如果对等方决定接受过期的证书(可能在与用户确认此证书之后)并希望继续连接,则它通常不会发送certificate_expired警报。

定义了一下错误警报:

  • unexpected_message
    收到一条是适当的消息,这个警报是fatal级别的,并且不应该在适当的实现之间的通信中观察到。
  • bad_record_mac
    如果接收到带有不正确MAC的记录,则返回此警报。如果发送了警报,则还必须返回此警报,因为TLSCiphertext是以无效方式解密的:要么它不是块长度的倍数,要么它的填充值在检查时不正确。此警报是fatal级别的,并且不应该在适当的实现之间的通信中观察到(除非消息在网络中被损坏)。
  • decryption_failed_RESERVED
    此警报在一些早期版本的TLS中使用,并且可能已经允许针对CBC模式[CBCATT]的某些攻击。它必须不被兼容的实现发送。
  • record_overflow
    接收长度大于$2^{14}+2048$字节的TLSCiphertext记录,或者解密为超过$2^{14}+1024$字节的TLSCompressed记录的记录。此消息总是致命的,并且不应该在适当的实现之间的通信中观察到(除非消息在网络中被损坏)。
  • decompression_failure
    解压缩函数接收到不正确的输入(例如,扩展到过长长度的数据)。这个消息总是致命的,并且不应该在适当的实现之间的通信中被观察到。
  • handshake_failure
    接收到handshake_failure警报消息表明发送方无法根据可用选项协商可接受的安全参数集。这是一个致命的错误。
  • no_certificate_RESERVED
    此警报在SSLv3中使用,但不是任何版本的TLS。它必须不被兼容的实现发送。
  • bad_certificate
    证书已损坏,包含未正确验证的签名等。
  • unsupported_certificate
    证书是不受支持的类型。
  • certificate_revoked
    证书被其签名人吊销。
  • certificate_expired
    证书已过期或当前无效。
  • certificate_unknown
    在处理证书时,还会出现一些其他的(未指定的)问题,使得证书不可接受。
  • illegal_parameter
    握手时的字段超出范围或与其他字段不一致。这个信息总是致命的。
  • unknown_ca
    接收到一个有效的证书链或部分链,但是由于CA证书无法定位或无法与已知、可信的CA匹配,所以无法接受证书。此消息总是致命的。
  • access_denied
    收到有效的证书,但是当应用访问控制时,发送方决定不进行协商。这条信息总是致命的。
  • decode_error
    由于某些字段超出指定范围或消息长度不正确,无法对消息进行解码。此消息总是致命的,并且不应该在适当的实现之间的通信中观察到(除非消息在网络中被损坏)。
  • decrypt_error
    握手密码操作失败,包括无法正确验证签名或验证已完成消息。这个信息总是致命的。
  • export_restriction_RESERVED
    此警报在早期版本的TLS中使用。它必须不被兼容的实现发送。
  • protocol_version
    客户端试图协商的协议版本已得到识别但不受支持。(例如,出于安全原因,可以避免使用旧的协议版本。)此消息总是致命的。
  • insufficient_security
    当协商失败时,返回而不是握手_.,因为服务器需要比客户端支持的密码更安全的密码。这个消息总是致命的。
  • internal_error
    与对等点无关的内部错误或协议的正确性(如内存分配失败)使得无法继续。这个信息总是致命的。
  • user_canceled
    由于与协议失败无关的原因,取消了此握手。如果用户在握手完成后取消了操作,则仅通过发送close_notify关闭连接更合适。此警报之后应发出close_notify。这个消息通常是一个警告。
  • no_renegotiation
    在初始握手之后,由客户端响应问候请求或由服务器响应客户端问候发送。这两者中的任何一个通常都会导致重新协商;当协商不合适时,接收方应以此警报作出响应。此时,原始请求者可以决定是否继续连接。其中一种情况是服务器生成了满足请求的进程;该进程在启动时可能会接收安全参数(密钥长度、身份验证等),并且在该点之后可能难以对这些参数的更改进行通信。这条消息总是一个警告。
  • unsupported_extension
    由接收扩展服务器hello的客户端发送,该扩展服务器hello包含他们没有放入相应的客户机hello的扩展。这个信息总是致命的。

Handshake Protocol Overview

会话状态的密码学参数由TLS握手协议产生,该协议在TLS记录层上操作。当TLS客户端和服务器第一次开始通信时,它们就协议版本达成一致,选择加密算法,可选地相互验证,并使用公钥加密技术生成共享秘密。
TLS握手协议包括以下步骤:

  • 交换问候消息以就算法达成一致,交换随机值,并检查会话恢复。
  • 交换必要的密码参数以允许客户端和服务器就预期秘钥达成一致。
  • 交换证书和加密信息以允许客户端和服务器进行身份验证。
  • 从预置秘钥生成主密钥并交换随机值。
  • 向记录层提供安全参数。
  • 允许客户端和服务器验证其对等方是否计算过相同的安全参数,以及是否发生了握手,而不会被攻击者篡改。

请注意,较高层不应过分依赖于TLS是否总是在两个对等体之间协商最强的可能连接。中间人攻击者可以通过多种方式尝试使两个实体下降到他们支持的最不安全的方法。该协议被设计成最小化这种风险,但是仍然存在可用的攻击:例如,攻击者可以阻止对安全服务运行的端口的访问,或者试图让对等方协商未经身份验证的连接。最基本的规则是,更高的级别必须了解它们的安全需求,并且绝不要通过比它们要求的安全性更低的信道传输信息。TLS协议是安全的,因为任何密码套件都提供它承诺的安全级别:如果与验证过证书的主机协商使用1024 bit RSA密钥交换的3DES协议,则可以预期是安全的。

这些目标是通过握手协议实现的,握手协议可以总结如下:客户端发送ClientHello消息,服务器必须使用ServerHello消息对此作出响应,否则将发生致命错误并且连接失败。ClientHello和ServerHello用于在客户端和服务器之间建立安全增强能力。ClientHello和ServerHello建立了以下属性:协议版本、会话ID、密码套件和压缩方法。此外,生成并交换两个随机值:ClientHello.random 和 ServerHello.random。

实际的密钥交换使用最多四个消息:服务器证书、ServerKeyExchange、客户端证书和客户端KeyExchange。可以通过为这些消息指定格式并定义消息的使用来创建新的密钥交换方法,以允许客户端和服务器就共享的秘密达成一致。这个秘密必须很长;当前定义的密钥交换方法交换46 byte以上的秘钥。

在hello消息之后,如果要进行身份验证,服务器将在Certificate消息中发送证书。此外,如果需要,可以发送ServerKeyExchange消息(例如,如果服务器没有证书,或者它的证书仅用于签名)。如果服务器经过身份验证,则如果证书适合所选的密码套件,它可以从客户端请求证书。接下来,服务器将发送ServerHelloDone消息,指示握手的hello-message阶段已经完成。然后,服务器将等待客户端响应。如果服务器发送了CertificateRequest消息,则客户端必须发送CertificateRequest消息。现在发送ClientKeyExchange消息,该消息的内容将取决于在ClientHello和ServerHello之间选择的公钥算法。如果客户端已经发送了具有签名能力的证书,则发送数字签名的证书验证消息以显式地验证证书中私钥的拥有。

此时,客户机发送一个ChangeCipherSpec消息,客户机将挂起的Cipher Spec复制到当前Cipher Spec中。然后客户机立即在新的算法、密钥和秘密下发送Finished消息。作为响应,服务器将发送自己的ChangeCipherSpec消息,将挂起传输到当前Cipher Spec,并在新的Cipher Spec下发送Finished消息。此时,握手完成,客户端和服务器可以开始交换应用层数据。(参见下面的流程图)在第一次握手完成之前(在建立除了TLS_NULL_WITH_NULL_NULL_NULL之外的密码套件之前),不得发送应用数据。
握手消息流:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Client                                               Server

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

*表示可选发送内容
为了帮助避免通道阻塞,ChangeCipherSpec是独立的TLS协议内容类型,实际上不是TLS握手消息。
当客户端和服务器决定恢复前一会话或复制现有会话(而不是协商新的安全参数)时,消息流如下:
客户端使用要恢复的会话的会话ID发送ClientHello。然后,服务器检查其会话缓存是否匹配。如果找到匹配,并且服务器愿意在指定的会话状态下重新建立连接,它将发送具有相同会话ID值的ServerHello。此时,客户端和服务器都必须发送ChangeCipherSpec消息并直接进入Finished消息。一旦重建完成,客户端和服务器就可以开始交换应用层数据。(参见下面的流程图。)如果未找到会话ID匹配,则服务器生成新的会话ID,TLS客户机和服务器执行完全握手。
简易握手消息流:

1
2
3
4
5
6
7
8
9
Client                                                Server

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

Handshake Protocol

TLS握手协议是TLS记录协议中定义的高级客户端之一。此协议用于协商会话的安全属性。握手消息被提供给TLS记录层,在那里它们被封装在一个或多个TLSPlaintext结构中,这些TLSPlaintext结构按照当前活动会话状态的指定进行处理和传输。
Handshake结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
enum {
hello_request(0), client_hello(1), server_hello(2),
certificate(11), server_key_exchange (12),
certificate_request(13), server_hello_done(14),
certificate_verify(15), client_key_exchange(16),
finished(20), (255)
} HandshakeType;

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;

握手协议消息按照必须发送的次序呈现在下面;以意外次序发送握手消息会导致致命错误,然而,可以省略不需要的握手消息。可能会有异常:证书消息在握手中使用两次(从服务器到客户端,然后从客户端到服务器),但是仅在其第一位置进行描述。不受这些排序规则约束的一个消息是HelloRequest消息,它可以在任何时间发送,但是如果它在握手时到达,客户端应该忽略它。

Hello Messages

Hello阶段消息用于在客户端和服务器之间交换安全增强能力。当新的会话开始时,记录层的连接状态加密、散列和压缩算法被初始化为空。当前的连接状态用于重新协商消息。

Hello Request

HelloRequest消息可能随时由服务器发送,HelloRequest是一个简单的通知,客户机应该重新开始协商过程。作为响应,客户机应在方便的时候发送ClientHello消息。此消息并不旨在确定哪一方是客户端或服务器,而仅仅是为了发起新的协商。服务器不应该在客户端的初始连接时立即发送HelloRequest。此时发送ClientHello是客户端的工作。
此信息将被客户忽略,如果客户是一届会话。如果客户端不希望重新协商会话,则该消息可能被忽略,或者,如果客户端希望,则该消息可能以no_renegotiation警报响应。由于握手消息的传输优先于应用程序数据,因此预期在从客户机接收到不超过几条记录之前将开始协商。如果服务器发送HelloRequest但是没有接收到ClientHello作为响应,那么它可能会关闭带有致命警报的连接。在发送HelloRequest之后,服务器不应该重复该请求,直到随后的握手协商完成为止。
消息结构:

1
struct { } HelloRequest;

此消息不能包括在整个握手过程中维护的、在Finished消息和证书验证消息中使用的消息散列中。

Client Hello

当客户机第一次连接到服务器时,需要发送客户机Hello作为其第一条消息。客户端还可以响应HelloRequest或主动发送ClientHello,以便重新协商现有连接中的安全参数。
ClientHello消息包括随机结构,稍后在协议中使用,消息结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct {
uint32 gmt_unix_time;
opaque random_bytes[28];
} Random;

opaque SessionID<0..32>;
uint8 CipherSuite[2]; /* Cryptographic suite selector */
enum { null(0), (255) } CompressionMethod;

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;

参数说明:

  • gmt_unix_time
    根据发送方的内部时钟,标准UNIX 32位格式的当前时间和日期(从1970年1月1日午夜开始的秒数,UTC,忽略闰秒)。基本TLS协议不需要正确设置时钟;更高层或应用程序协议可以定义其他要求。注意,由于历史原因,数据元素使用GMT命名,GMT是当前世界时基UTC的前身。
  • random_bytes
    28 byte随机数。
  • client_version
    客户端希望这个session使用的TLS协议版本进行交流
  • random
    客户端生成的随机结构
  • session_id
    客户端希望用这个session_id进行连接,如果这个字段为空表示没有session_id可用,或如果客户端希望生成新的安全参数
  • cipher_suites
    这是客户机支持的密码选项列表,其中客户端的第一个首选项优先。如果session_id不为空,则数组至少包含session中的cipher_suite
  • compression_methods
    客户端支持的压缩方法列表,由客户端设置排列顺序,如果session_id不为空,它必须包含compression_method,这个列表必须存在,并且所有实现必须支持CompressionMethod.null,因此客户端和服务器将始终能够就压缩方法达成一致。
  • extensions
    客户端可以通过在扩展字段中发送数据来请求服务器的扩展功能。

ClientHello消息包括可变长度的会话标识符,如果不为空,则该值标识同一客户机和服务器之间的会话,客户机希望重用该会话的安全参数。会话标识符可能来自较早的连接,这个连接,或者来自另一个当前活动的连接。第二选择是最有效的,如果客户只有更新一个连接的随机结构和衍生价值,而第三选项可能会使它能够在不恢复全手动协议的情况下建立一个独立的安全连接。这些独立连接可以顺序地或同时发生,当协商完成的握手与完成消息的交换完成后,SessionID变为有效,并持续存在,直到由于老化或因与会话关联的连接遇到致命错误而将其删除。

警告:因为会话ID是在没有加密或即时MAC保护的情况下传输的,所以服务器必须不将机密信息放在会话标识符中,或者让虚假会话标识符的内容导致任何安全漏洞。(请注意,握手作为整体的内容,包括SessionID,受到握手结束时交换的Finished消息的保护。)

在ClientHello消息中,从客户机传递到服务器的密码套件列表包含客户机按照客户机偏好(最喜欢选择优先)的顺序支持的密码算法的组合。每个密码组定义密钥交换算法、批量加密算法(包括密钥长度)、MAC算法和PRF。服务器将选择密码套件,或者如果没有可接受的选择,则返回握手失败警报并关闭连接。如果列表包含服务器不识别、支持或希望使用的密码套件,则服务器必须忽略这些密码套件,并像往常一样处理其余的密码套件。

ClientHello包括客户机支持的压缩算法列表,根据客户机的偏好进行排序。

TLS允许将compression_methods字段写在扩展块中,可以通过确定在ClientHello结尾的compression_methods后面是否有字节来检测扩展的存在。注意,这种检测可选数据的方法不同于具有可变长度字段的正常TLS方法,但它用于在定义扩展之前与TLS兼容。

如果客户端使用扩展请求附加功能,并且该功能没有由服务器提供,则客户端可以中止握手。服务器必须接受具有和不具有扩展字段的ClientHello消息,并且(对于所有其他消息)必须检查消息中的数据量是否与这些格式之一精确匹配;如果不匹配,则必须发送致命的“decode_error”警报。

客户端发送完ClientHello消息后等待ServerHello消息,服务器返回的任何握手消息(HelloRequest除外)都被视为致命错误。

Server Hello

当服务器能够找到一组可接受的算法时,它将响应ClientHello消息发送此消息。如果找不到这样的匹配,它将以握手失败警报作为响应。
消息格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
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
    此字段将包含客户端在客户端hello中建议的字段的较低部分,以及服务器支持的最高部分。
  • random
    服务器生成的随机结构,必须不同于ClientHello.random
  • session_id
    这是与此连接对应的会话的标识。如果ClientHello.session_id不为空,服务器将会在缓存中查找匹配,如果找到匹配,并且服务器愿意使用指定的会话状态建立新连接,则服务器将使用与客户端提供的值相同的值进行响应。这表示恢复会话,并指示各方必须直接处理Finished消息。否则,该字段将包含标识新会话的不同值。服务器可以返回空的session_id以指示会话将不被缓存,因此无法恢复。如果恢复会话,则必须使用与最初协商的密码套件恢复会话。注意,即使服务器以前提供了session_id,也不需要恢复任何会话。在任何握手过程中,客户必须做好充分谈判的准备,包括谈判新的密码套件。
  • cipher_suite
    服务器从ClientHello.cipher_suites中的列表中选择的单个密码套件,对于恢复的会话,此字段是从正在恢复的会话的状态得到的值。
  • compression_method
    服务器从ClientHello.compression_methods中的列表中选择的单个压缩算法,对于恢复会话,此字段是恢复会话状态的值。
  • extensions
    扩展名列表。注意,只有客户端提供的扩展可以显示在服务器的列表中。

判断ServerHello结尾后面是否有字节compression_method表示是否存在扩展。

Hello Extensions

扩展格式:

1
2
3
4
5
6
7
8
struct {
ExtensionType extension_type;
opaque extension_data<0..2^16-1>;
} Extension;

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

参数说明:

  • extension_type,定义特定扩展类型
  • extension_data,特定类型的信息

除非相同,否则扩展类型不能出现在ServerHello中扩展类型出现在相应的ClientHello中。如果一个客户端在ServerHello中接收到它没有在相关的ClientHello中请求的扩展类型,那么它必须发送unsupported_extension致命警报中止握手。尽管如此,“server-oriented”的扩展可能在未来在这个框架中提供。客户端必须发送支持的扩展类型和空的扩展数据,服务器可以选择客户端提供的扩展。
当ClientHello或ServerHello消息中存在多个不同类型的扩展时,扩展可能以任何顺序出现,同一类型的扩展不能超过一个。
最后,请注意,扩展可以在启动新会话和请求恢复会话时发送。实际上,请求恢复会话的客户端通常不知道服务器是否会接受该请求,因此它应该发送与没有尝试恢复时发送的扩展相同的扩展。
通常,每个扩展类型的规范需要描述在完全握手和会话恢复期间扩展的效果。大多数当前TLS扩展只在会话启动时相关:当恢复旧会话时,服务器在Client Hello中不处理这些扩展,并且在Server Hello中不包括它们。然而,一些扩展可能在会话恢复期间指定不同的行为。
在这个协议中,新特性和现有特性之间可能存在微妙(但不是那么微妙)的交互,这可能导致总体安全性的显著降低。在设计新的扩展时应考虑以下因素:

  • 有些情况下,服务器不同意扩展是错误条件,有些只是拒绝支持特定的特性。通常,错误警报应该用于前者,而服务器扩展响应中的字段用于后者。
  • 扩展应该尽可能地设计成防止任何通过操纵握手消息来强制使用(或不使用)特定功能的攻击。不管是否认为该特性会导致安全问题,都应该遵循这个原则。扩展字段包含在Finished消息散列的输入中通常就足够了,但是当扩展更改在握手阶段发送的消息的含义时,需要非常小心。设计者和实现者应该知道,在握手被验证之前,活动攻击者可以修改消息并插入、移除或替换扩展。
  • 在技术上,使用扩展来改变TLS设计的主要方面是可能的,例如密码组协商的设计。不建议这样做;定义一个新版本的TLS会更合适——特别是因为TLS握手算法具有针对基于版本号的版本回滚攻击的特定保护,并且在任何主要设计中版本回滚的可能性都应该是一个重要的考虑因素。N的变化。
Signature Algorithms

客户端使用“signature_algorithms”扩展来向服务器指示在数字签名中可以使用哪些签名/散列算法对。extension_data字段包含supported_signature_algorithms值。

1
2
3
4
5
6
7
8
9
10
11
12
13
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值列出客户机愿意验证的单个散列/签名对,这些值按首选项的降序表示。注意:因为不是所有的签名算法和散列算法都可以被一个实现(例如,具有SHA-1的DSA,但不是SHA-256)接受,所以这里的算法成对列出。
参数说明:

  • hash
    这个字段表示可以使用的哈希算法。这些值分别表示支持未散列数据MD5[MD5]、SHA-1、SHA-224、SHA-256、SHA-384和SHA-512[SHS]。在签名算法不需要在签名之前进行散列的情况下,为将来的可扩展性提供了“none”值。
  • signature
    此字段指示可以使用的签名算法。这些值分别表示匿名签名RSASSA-PKCS1-v1_5[PKCS1]和DSA[DSS]以及ECDSA[ECDSA]。“匿名”值在此上下文中没有意义,但在第7.4.3节中使用。它不必出现在这个扩展名中。

这个扩展的语义有些复杂,因为密码套件指示允许的签名算法,而不是散列算法。如果客户机只支持默认的散列和签名算法(在本节中列出),那么它可能会省略signature_algorithms扩展。如果客户端不支持默认算法,或者支持其他散列和签名算法(并且它愿意使用它们来验证服务器发送的消息,即服务器证书和服务器密钥交换),他必须发送signature_algorithms扩展,列出愿意接受的算法。
如果客户端没有发送signature_algorithms扩展,服务端必须执行一下操作:

  • 如果协商的密钥交换算法是(RSA、DHE_RSADH_RSARSA_PSKECDH_RSAECDHE_RSA)之一,则表现得好像客户端已经发送了值{sha1,rsa}。
  • 如果协商的密钥交换算法是(DHE_DSSDH_DSS)之一,则表现得好像客户端已经发送了值{sha1,dsa}。
  • 如果协商的密钥交换算法是(ECDH_ECDSAECDHE_ECDSA)之一,则表现得好像客户端已经发送了值{sha1,ecdsa}。

注意:这与TLS 1.1不同,TLS 1.1没有显式的规则,但实际上可以假设对等点支持MD5和SHA-1。
注意:这个扩展对于1.2之前的TLS版本没有意义。如果客户提供以前的版本,则必须不提供。然而,即使客户机确实提供了它,在[TLSEXT]中指定的规则也要求服务器忽略它们不理解的扩展。
服务器必须不发送此扩展。TLS服务器必须支持接收这个扩展。在执行会话恢复时,此扩展不包括在Server Hello中,并且服务器忽略Client Hello(如果存在)中的扩展。

Server Certificate

每当约定的密钥交换方法使用证书进行身份验证时,服务器必须发送Certificate消息(这包括本文档中定义的除DH_anon之外的所有密钥交换方法)。此消息将始终紧跟在ServerHello消息之后。
此消息将服务器的证书链传递给客户端。证书必须适合协商的密码套件的密钥交换算法和任何协商的扩展。
消息结构:

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

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

参数说明:

  • certificate_list
    证书序列,发件人的证书必须排在第一位。下面的每个证书必须直接对它前面的证书进行认证。因为证书验证要求独立分发根密钥,所以在假定远程端必须已经拥有根密钥以便在任何情况下对其进行验证的情况下,指定根证书授权的自签名证书可能从链中省略。

客户端对证书请求消息的响应将使用相同的消息类型和结构。注意,如果客户机没有适当的证书来响应服务器的认证请求,那么它可能没有发送证书。
注意:PKCS#7[PKCS7]没有用作证书向量的格式,因为没有使用PKCS#6[PKCS6]扩展证书。此外,PKCS#7定义了一个SET而不是一个SEQUENCE,这使得解析列表的任务更加困难。

以下规则适用于服务器发送的证书:

  • 除非明确协商,否则证书类型必须是X.509v3(例如,[TLSPGP])。
  • 最终实体证书的公钥(和相关限制)必须与所选的密钥交换算法兼容。
    |秘钥交换算法|证书秘钥类型|
    |:-:|-|
    |RSA RSA_PSK|RSA公钥;证书必须允许密钥用于加密(如果存在密钥使用扩展名,则必须设置密钥加密位)。注意:RSA_PSK在[TLSPSK]中定义。|
    |DHE_REA ECDHE_RSA|RSA公钥;证书必须允许使用密钥在服务器密钥中使用的签名方案和哈希算法进行签名(如果存在密钥使用扩展,则必须设置数字签名位)交换消息。注意:ECDHE_RSA在[TLSECC]中定义。|
    |DHE_DSS|DSA公钥;证书必须允许将密钥用于使用将在服务器密钥交换消息中使用的散列算法进行签名。 |
    |DH_DSS DH_RSA| Diffie-Hellman公钥;如果存在密钥使用扩展名,则必须设置密钥协定位。|
    |ECDH_ECDSA ECDH_RSA| ECDH的公钥;公钥必须使用客户端支持的曲线和点格式,如[TLSECC]中所述。|
    |ECHDE_ECDSA| ECDSA-capablede公钥,该认证必须允许使用将在服务器密钥交换消息中使用的哈希算法对密钥进行签名。公钥必须使用客户端支持的曲线和点格式,如[TLSECC]中所述。 |

server_nametrusted_ca_keys扩展常用来指导证书的选择。
如果客户端提供了signature_algorithms扩展,则服务器提供的所有证书必须由出现在该扩展中的哈希/签名算法对签名。注意,这意味着包含用于一个签名算法的密钥的证书可以使用不同的签名算法(例如,使用DSA密钥签名的RSA密钥)进行签名。这与TLS 1.1不同,TLS 1.1要求算法相同。注意,这也意味着DH_DSSDH_RSAECDH_ECDSAECDH_RSA密钥交换算法不限制用于对证书进行签名的算法。固定DH证书可以用扩展中出现的任何哈希/签名算法对签名。名称DH_DSSDH_RSAECDH_ECDSAECDH_RSA是历史名称。
如果服务器有多个证书,则根据上述标准选择其中一个证书(除了其他标准,如传输层端点、本地配置和首选项等。)如果服务器只有一个证书,它应该尝试验证它满足这些条件。
注意,有些证书使用当前不能与TLS一起使用的算法和/或算法组合。例如,不能使用具有RSASSA-PSS签名密钥的证书(SubjectPublicKeyInfo中的id-RSASSA-PSS OID),因为TLS没有定义对应的签名算法。
由于为TLS协议指定了指定新密钥交换方法的密码套件,因此它们将暗示证书格式和所需的编码密钥信息。

Server Key Exchange Message

服务器秘钥交换协议此消息将在服务器证书消息(或ServerHello消息,如果是匿名协商)之后立即发送。
ServerKeyExchange消息只有在服务器证书消息(如果发送)不包含足够的数据以允许客户端交换预置机密时才由服务器发送。
对于以下密钥交换方法,这是正确的:DHE_DSSDHE_RSADH_anon,发送用于以下密钥交换方法的ServerKeyExchange消息是不合法的:RSA、DH_DSSDH_RSA。其他密钥交换算法,如[TLSECC]中定义的那些算法,必须指定是否发送ServerKeyExchange消息;如果发送消息,则指定其内容。

此消息传递密码信息,以允许客户机通信预告秘密,一种Diffie-Hellman公钥,客户端用它可以完成密钥交换(其结果是预制秘密)或某些其他算法的公钥。
消息体结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
enum { dhe_dss, dhe_rsa, dh_anon, rsa, dh_dss, dh_rsa
/* may be extended, e.g., for ECDH -- see [[TLSECC](https://tools.ietf.org/html/rfc5246#ref-TLSECC ""Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS)"")] */
} 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 */

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](https://tools.ietf.org/html/rfc5246#ref-TLSECC ""Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS)"")] */
};
} ServerKeyExchange;

参数说明:

  • dh_p
    用于Diffie-Hellman操作的素数。
  • dh_g
    用于Diffie-Hellman操作的生成器。
  • dh_Ys
    服务器Diffie-Hellman的公共值$g^X$ mod p。
  • params
    服务器的密钥交换参数。
  • signed_params
    对于非匿名密钥交换,在服务器的密钥交换参数上的签名。

如果客户端提供了signature_algorithms扩展,则签名算法和哈希算法必须是该扩展中列出的一对。注意,这里存在不一致的可能性。在。注意,这里存在不一致的可能性。例如,客户机可以提供DHE_DSS密钥交换,但是从signature_algorithms扩展中省略任何DSA对。为了正确地协商,服务器在选择任何候选密码套件之前,必须对照signature_algorithms扩展检查它们。这虽然有些不雅观,但是是一种折衷方案,旨在最小化对原始密码套件设计的更改。
此外,哈希和签名算法必须与服务器端实体证书中的密钥兼容。RSA密钥可以与任何允许的哈希算法一起使用,如果存在,则受证书中的限制。
因为DSA签名不包含任何散列算法的安全指示,所以如果多个散列可用于任何密钥,则存在散列替换的风险。目前,DSA仅在被用作SHA-1。DSS[DSS-3]的未来修订预计允许使用使用DSA的其他摘要算法,以及摘要应用于每个算法和密钥大小。此外,[PKIX]的未来修订可能指定证书机制,以指示DSA将使用哪些摘要算法。
由于为包括新的密钥交换算法的TLS定义了附加的密码组,所以服务器密钥交换消息将在与密钥交换算法相关联的证书类型没有为客户端提供足够的信息来交换预置秘密的情况下发送。

Certificate Request

如果适合所选择的密码套件,非匿名服务器可以可选地从客户端请求证书。如果发送此消息,则该消息将立即跟随ServerKeyExchange消息(如果发送;否则,此消息跟随服务器的证书消息)。
消息结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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_types
    客户端提供能够的证书类型列表:rsa_sign(包含RSA密钥的证书),dss_sign(包含DSA密钥的证书),rsa_fixed_dh(包含静态DH密钥的证书),dss_fixed_dh(包含静态DH密钥的证书)。
  • supported_signature_algorithms
    服务器能够验证的哈希/签名算法对的列表,按首选项的降序列出。
  • certificate_authorities
    可接受的certificate_authorities的区分名称[X501]的列表,以DER编码格式表示。这些区分名称可以为根CA或从属CA指定所需的区分名称;因此,此消息可用于描述已知根以及所需的授权空间。如果certificate_authorities列表是空的,那么客户端可以发送任何适当的ClientCertificateType的证书,除非存在相反的外部安排。

certificate_typessupported_signature_algorithms字段相互作用很复杂。certificate_types自SSLv3以来就一直存在于TLS中,但有些未指定。大部分功能都被supported_signature_algorithms,适用以下规则:

  • 客户端提供的任何证书都必须使用在supported_signature_algorithms中找到的哈希/签名算法对进行签名。
  • 客户端提供的最终实体证书必须包含与certificate_types兼容的密钥。如果密钥是签名密钥,那么它必须与supported_signature_algorithms中的一些哈希/签名算法对一起使用。
  • 由于历史原因,某些客户端证书类型的名称包括用于对证书进行签名的算法。例如,在早期版本的TLS中,rsa_fixed_dh表示用RSA签名并包含静态DH密钥的证书,在TLS 1.2中,这个功能已经被supported_signature_algorithms淘汰了,并且证书类型不再限制用于对证书进行签名的算法。例如,如果服务器发送dss_fixed_dh证书类型和{sha1,dsa},{sha1,rsa}签名类型,则客户端可能使用包含静态DH密钥的证书来回复,该证书用RSA-SHA1签名。

注意:不能使用列出为RESERVED的值。它们用于SSLv3。匿名服务器请求客户端身份验证是一个致命的handshake_failure警报。

Server Hello Done

服务器发送ServerHelloDone消息以指示ServerHello的结束和相关消息。在发送此消息之后,服务器将等待客户端响应。此消息意味着服务器已经完成发送消息以支持密钥交换,并且客户端可以继续进行其密钥交换阶段。在接收到ServerHelloDone消息后,客户端应验证服务器是否提供了有效的证书(如果需要),并检查服务器hello参数是否可接受。
消息结构:

1
struct { } ServerHelloDone;

Client Certificate

这是客户端在接收到ServerHelloDone消息之后可以发送的第一条消息,此消息仅在服务器请求证书时发送。如果没有合适的证书可用,客户端必须发送包含没有证书的证书消息,也就是说certificate_list结构长度为零。如果客户端没有发送任何证书,服务器可以自行决定在没有客户端身份验证的情况下继续握手,或者使用致命的handshake_failure警报进行响应。所以,如果证书链不可接受(它不是由一个已知的、可信任的CA签名的),服务器可以自行决定是否继续握手(考虑未经认证的客户端)或者发送致命警报。
此消息将客户端的证书链传送到服务器;服务器将在验证CertificateVerify消息(当客户端身份验证基于签名时)或计算预秘钥(对于非临时Diffie-Hellman)时使用它。证书必须适合协商的密码套件的密钥交换算法和任何协商的扩展。

特性:

  • 证书类型必须是X.509v3,除非是经过明确的协商
  • 最终实体证书的公钥(和相关限制)必须与CertificateRequest中列出的证书类型兼容:
    | Client Cert. Type| Certificate Key Type|
    |-|-|
    |rsa_sign |RSA公钥,证书必须允许密钥用于使用证书验证消息中将使用的签名方案和哈希算法进行签名。|
    |dss_sign |DSA 公钥,证书必须允许使用密钥使用将在证书验证消息中使用的哈希算法进行签名。|
    |ecdsa_sign |ECDSA-capable公钥,证书必须允许用将在证书验证消息中使用的散列算法使用密钥进行签名;公钥必须使用服务器支持的曲线和点格式。|
    |rsa_fixed_dh dss_fixed_dh |Diffie-Hellman公钥,必须使用和服务器秘钥相同的参数|
    |rsa_fixed_ecdh ecdsa_fixed_ecdh |ECDH-capable公钥必须使用与服务器密钥相同的曲线,并且必须使用服务器支持的点格式。|

  • 如果证书请求消息中的certificate_authorities列表不是空的,则证书链中的一个证书应该由所列出的CA之一颁发。

  • 证书必须使用可接受的哈希/签名算法对签名。注意,这放宽了先前版本的TLS中对证书签名算法的限制。

注意,与服务器证书一样,有些证书使用当前不能与TLS一起使用的算法/算法组合。

Client Key Exchange Message

这个消息总是由client发送,如果发送了客户端证书消息,则必须立即跟随它。它必须是客户端收到ServerHelloDone消息后发送的第一条消息。
利用该消息,通过直接传输RSA加密的秘密或通过传输.ie-Hellman参数来设置预置秘密,该参数将允许每一方商定相同的预置秘密。当客户端使用的是临时的Diffie-Hellman参数,然后此消息包含客户端的Diffie-Hellman公共值。如果客户端正在发送包含静态DH指数的证书(它正在fixed_dh客户端身份验证),则必须发送此消息,但必须为空。
消息的选择取决于选择了哪个密钥交换方法,消息结构:

1
2
3
4
5
6
7
8
9
10
11
12
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;

RSA-Encrypted Premaster Secret Message

如果RSA用于密钥协商和认证,则客户端生成一个48 byte的预置密钥,使用服务器证书中的公钥对其进行加密,并将结果发送给加密的预置密钥消息。此结构是ClientKeyExchange消息的变体,本身不是消息。
消息结构:

1
2
3
4
5
6
7
8
struct {
ProtocolVersion client_version;
opaque random[46];
} PreMasterSecret;

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

参数说明:

  • client_version
    客户端支持的最新版本。这是用来检测版本回滚攻击的。
  • random
    46 byte的安全生成随机数
  • pre_master_secret
    该随机值由客户端生成,用于生成主秘密

PreMasterSecret的值是有客户端的ClientHello.client_version提供的,不是通过协商得到的。这是为了设置回滚攻击设置的。有些旧版本使用的是协商定义的,因此,检查版本号可能导致无法与此类不正确的客户端实现互操作。
客户端实现必须始终在PreMasterSecret中发送正确的版本号。如果ClientHello.client_version是TLS 1.1或更高版本,则服务器实现必须检查版本号,如下面的注释中所述。如果版本号是TLS 1.0或更高版本,服务器实现应该检查版本号,但是可能有一个配置选项来禁用该检查。注意,如果检查失败,则PreMasterSecret应该按照以下描述进行随机化。

注意,如果客户端在原始pre_master_secret中发送了错误的版本,则用ClientHello.client_version显式构造pre_master_secret将生成一个无效的master_secret。另一种方法是将版本号不匹配视为PKCS-1格式错误,并完全随机化预播者秘密:

  1. 生成48 byte随机字符串R
  2. 解密消息以恢复明文M
  3. 如果PKCS#1填充不正确,或者消息长度M不完全是48字节:pre_master_secret = ClientHello.client_version || R
    否则,如果clienthello.client _<=TLS 1.0版本的版本号,校验和冰没有禁止:pre_master_secret = M
    否则,如果M[0..1] != ClientHello.client_version:premaster secret = R
    否则:pre_master_secret = ClientHello.client_version || M[2..47]

Client Diffie-Hellman Public Value

Certificate Verify

Finished

Cryptographic Computations

Computing the Master Secret

RSA

Diffie-Hellman

Mandatory Cipher Suites

Application Data Protocol

Security Considerations

Appendix

Protocol Data Structures and Constant Values

Record Layer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
struct {
uint8 major;
uint8 minor;
} ProtocolVersion;

ProtocolVersion version = { 3, 3 }; /* TLS v1.2*/

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;

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

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

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

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;

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

Change Cipher Specs Message

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

Alert Messages

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
enum { warning(1), fatal(2), (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), /* new */
(255)
} AlertDescription;

struct {
AlertLevel level;
AlertDescription description;
} Alert;

Handshake Protocol

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
enum {
hello_request(0), client_hello(1), server_hello(2),
certificate(11), server_key_exchange (12),
certificate_request(13), server_hello_done(14),
certificate_verify(15), client_key_exchange(16),
finished(20)
(255)
} HandshakeType;

struct {
HandshakeType msg_type;
uint24 length;
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;

Hello Messages

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
struct { } HelloRequest;

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

opaque SessionID<0..32>;

uint8 CipherSuite[2];

enum { null(0), (255) } CompressionMethod;

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;

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;

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

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

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

Server Authentication and Key Exchange Messages

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
opaque ASN.1Cert<2^24-1>;

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

enum { dhe_dss, dhe_rsa, dh_anon, rsa,dh_dss, dh_rsa
/* may be extended, e.g., for ECDH -- see [[TLSECC](https://tools.ietf.org/html/rfc5246#ref-TLSECC ""Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS)"")] */
} 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 */
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](https://tools.ietf.org/html/rfc5246#ref-TLSECC ""Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS)"")] */
} 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>;
DistinguishedName certificate_authorities<0..2^16-1>;
} CertificateRequest;

struct { } ServerHelloDone;

Client Authentication and Key Exchange Messages

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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;

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

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

enum { implicit, explicit } PublicValueEncoding;

struct {
select (PublicValueEncoding) {
case implicit: struct {};
case explicit: opaque DH_Yc<1..2^16-1>;
} dh_public;
} ClientDiffieHellmanPublic;

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

Handshake Finalization Message

1
2
3
struct {
opaque verify_data[verify_data_length];
} Finished;

The Cipher Suite

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
CipherSuite TLS_NULL_WITH_NULL_NULL               = { 0x00,0x00 };

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

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

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

The Security Parameters

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
enum { null(0), (255) } CompressionMethod;

enum { server, client } ConnectionEnd;

enum { tls_prf_sha256 } PRFAlgorithm;

enum { null, rc4, 3des, aes } BulkCipherAlgorithm;

enum { stream, block, aead } CipherType;

enum { null, hmac_md5, hmac_sha1, hmac_sha256, hmac_sha384,
hmac_sha512} MACAlgorithm;

/* Other values may be added to the algorithms specified in
CompressionMethod, PRFAlgorithm, BulkCipherAlgorithm, and
MACAlgorithm. */

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;
uint8 mac_length;
uint8 mac_key_length;
CompressionMethod compression_algorithm;
opaque master_secret[48];
opaque client_random[32];
opaque server_random[32];
} SecurityParameters;

Cipher Suite Definitions

Cipher Suite Key Exchange Cipher Mac
TLS_NULL_WITH_NULL_NULL NULL NULL NULL
TLS_RSA_WITH_NULL_MD5 RSA NULL MD5
TLS_RSA_WITH_NULL_SHA RSA NULL SHA
TLS_RSA_WITH_NULL_SHA256 RSA NULL SHA256
TLS_RSA_WITH_RC4_128_MD5 RSA RC4_128 MD5
TLS_RSA_WITH_RC4_128_SHA RSA RC4_128 SHA
TLS_RSA_WITH_3DES_EDE_CBC_SHA RSA 3DES_EDE_CBC SHA
TLS_RSA_WITH_AES_128_CBC_SHA RSA AES_128_CBC SHA
TLS_RSA_WITH_AES_256_CBC_SHA RSA AES_256_CBC SHA
TLS_RSA_WITH_AES_128_CBC_SHA256 RSA AES_128_CBC SHA256
TLS_RSA_WITH_AES_256_CBC_SHA256 RSA AES_256_CBC SHA256
TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA DH_DSS 3DES_EDE_CBC SHA
TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA DH_RSA 3DES_EDE_CBC SHA
TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA DHE_DSS 3DES_EDE_CBC SHA
TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA DHE_RSA 3DES_EDE_CBC SHA
TLS_DH_anon_WITH_RC4_128_MD5 DH_anon RC4_128 MD5
TLS_DH_anon_WITH_3DES_EDE_CBC_SHA DH_anon 3DES_EDE_CBC SHA
TLS_DH_DSS_WITH_AES_128_CBC_SHA DH_DSS AES_128_CBC SHA
TLS_DH_RSA_WITH_AES_128_CBC_SHA DH_RSA AES_128_CBC SHA
TLS_DHE_DSS_WITH_AES_128_CBC_SHA DHE_DSS AES_128_CBC SHA
TLS_DHE_RSA_WITH_AES_128_CBC_SHA DHE_RSA AES_128_CBC SHA
TLS_DH_anon_WITH_AES_128_CBC_SHA DH_anon AES_128_CBC SHA
TLS_DH_DSS_WITH_AES_256_CBC_SHA DH_DSS AES_256_CBC SHA
TLS_DH_RSA_WITH_AES_256_CBC_SHA DH_RSA AES_256_CBC SHA
TLS_DHE_DSS_WITH_AES_256_CBC_SHA DHE_DSS AES_256_CBC SHA
TLS_DHE_RSA_WITH_AES_256_CBC_SHA DHE_RSA AES_256_CBC SHA
TLS_DH_anon_WITH_AES_256_CBC_SHA DH_anon AES_256_CBC SHA
TLS_DH_DSS_WITH_AES_128_CBC_SHA256 DH_DSS AES_128_CBC SHA256
TLS_DH_RSA_WITH_AES_128_CBC_SHA256 DH_RSA AES_128_CBC SHA256
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 DHE_DSS AES_128_CBC SHA256
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 DHE_RSA AES_128_CBC SHA256
TLS_DH_anon_WITH_AES_128_CBC_SHA256 DH_anon AES_128_CBC SHA256
TLS_DH_DSS_WITH_AES_256_CBC_SHA256 DH_DSS AES_256_CBC SHA256
TLS_DH_RSA_WITH_AES_256_CBC_SHA256 DH_RSA AES_256_CBC SHA256
TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 DHE_DSS AES_256_CBC SHA256
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 DHE_RSA AES_256_CBC SHA256
TLS_DH_anon_WITH_AES_256_CBC_SHA256 DH_anon AES_256_CBC SHA256
Cipher Type Key Material IV Size Block Size
NULL Stream 0 0 N/A
RC4_128 Stream 16 0 N/A
3DES_EDE_CBC Block 24 8 8
AES_128_CBC Block 16 16 16
AES_256_CBC Block 32 16 16
MAC Algorithm mac_length mac_key_length
NULL N/A 0 0
MD5 HMAC-MD5 16 16
SHA HMAC-SHA1 20 20
SHA256 HMAC-SHA256 32 32

类型说明:

  • Type,指示这是流密码还是在CBC模式下运行的块密码。
  • Key Material,用于生成写键的key_block的字节数。
  • IV Size,需要为初始化向量生成的数据量。流密码为零;等于块密码的块大小(这等于securityParameters.record_iv_length)
  • Block Size,块密码在一个块中加密的数据量;在CBC模式下运行的块密码只能对其块大小的偶数倍进行加密。