TLS协议的主要目标是在两个通信应用之间提供私密性和数据完整性。这个协议由两层组成:TLS记录协议和TLS握手协议。最低层是基于一些可靠传输协议(如TCP)的TLS记录协议。TLS记录协议提供的连接安全有两个基本性质:
TLS记录协议被用于封装各种高层协议。其中一种被封装的协议就是TLS握手协议,这种协议允许服务器和客户端在应用层协议传输或接收第一个字节数据之前验证彼此的身份并协商一个加密算法和加密密钥。TLS握手协议提供的连接安全有三个基本性质:
TLS的一个优点是它是应用层协议无关的。高层协议可以透明地置于TLS协议之上。然而,TLS标准不会指定协议怎样为TLS增加安全性。关于怎样初始化TLS握手、怎样理解认证证书交换的相关决策,则留给运行于TLS之上的协议的设计者和实现者做判断。
略。
本文是TLS 1.1协议的一个修订,修订内容包括提升了灵活性,尤其是密码学算法的协商。主要变更如下:
TLS协议的目标按照优先级排列如下:
本文和TLS协议自身都是基于由Netscape公布的SSL 3.0规范。TLS和SSL3.0的差异并不是引人注目的,但差异也足够大到使各种版本的TLS和SSL3.0不能互操作(虽然每种协议都包含一个机制使其能够兼容之前的版本)。本文的主要目标读者是协议实现者和对协议进行密码分析的人员。这个思想贯穿于这篇规范的制定过程,并致力于反应这两部分群体(译者注:协议实现和协议分析人员)的需求。出于这个原因,很多算法相关的数据结构和规则被包含在文档的内容中(或附录中),以方便获取。
本文并不致力于提供任何关于服务定义和接口定义的细节,虽然处于维护安全的坚实性,它确实有选择地覆盖了一些策略的区域。
本文使用另外的表示方法处理数据格式。下面会用到非常基础甚至是有些随便定义的陈述语法。这种语法源自多处。虽然它在语法上像编程语言C,在语法和目的上像XDR(External Data Representation),但有太多相似之处是有风险的。这种陈述语言只用于将TLS文档化,在这个特定目标外该语言不会有普遍的应用。
所有数据条目的描述都是被显示指定的。基本数据块大小是1个字节(即8位)。多个字节数据条目是字节的串联,从左到右,从上到下。从字节流的角度 看,一个多字节条目(在例子中是一个数值)的组织方式(使用C的记法)如下:
value = (byte[0] << 8*(n-1)) | (byte[1] << 8*(n-2)) | ... | byte[n-1];
对于多字节数值这个字节序是普通的网络字节序或大端格式。
注释以"/*"开头,以"*/"结束。
可选组件通过将其包含在"[[ ]]" 双括号中来表示。
包含未解释数据的单字节实体属于opaque类型。
一个向量(一维数组)是一个同类数据元素的流。向量的大小可能在编写文档时指定或留待运行时确定。在任何情况下,向量的长度都是指字节数而非元素数。定义一个新类型T'(是一个固定长度的类型T的向量)的语法是:
T T'[n];
这里,T'在数据流中占据了n个字节,而n是多个类型T的大小。向量的长度并不包含在编码流中。
在下面的例子中,Datum被定义为协议不能理解的3个连续字节, 而Data是三个连续的Datum,共占据9个字节。
opaque Datum[3]; /* 三个未知字节 */
Datum Data[9]; /* 3个连续的3字节向量 */
变长向量的定义是通过指定一个合法长度的子范围来实现(使用符号
T T'
在下面的例子中会强制要求一个向量必须包含300-400个字节的opaque类型数据,它不能为空。实际长度域占用两个字节,一个uint16,这足以代表数值400(见4.4节)。另外一方面,更长的向量可以描述多达800字节的数据,或400个uint16类型的元素,这个向量可以为空。它的编码会包含一个两字节的实际长度域设置在向量前。一个编码向量的长度必须是单个元素长度的偶数倍(例如,一个17字节长的uint16类型的向量是非法的)。
opaque mandatory<300..400>; /*长度域是2字节,不能为空 */
uint16 longer<0..800>; /* 0-400 16-bit 无符号整数 */
基本数字数据类型是一个无符号字节(uint8)。所有更大的数字数据类型都被组织成固定长度的字节序列并如4.1节中所描述的那样被串联,且同样是无符号的。下面是预定义的数字类型。
uint8 uint16[2];
uint8 uint24[3];
uint8 uint32[4];
uint8 uint64[8];
本篇规范中的所有的数值都是以网络字节序(大端)存储;一个uint32类型的十六进制字节01 02 03 04等于十进制数16909060。
需要注意的是在一些情况下(如DH参数)有必要将整数以不透明(译注:opaque)向量的形式表示。在这种情况下,它们代表无符号整数(即,最开始的0字节是不需要的,即使设置了最有意义的bit(译注:不明白这个bit是什么))。
另外一种少见的数据类型是枚举。一个枚举类型的域仅能表示定义时声明的值。每个定义都是一个不同的类型。只有相同类型的枚举能被指定或比较。一个枚举的每个元素必须被指定一个值,就像下面的例子所表明的。既然枚举类型的元素并不是有序的,它们能够被以任意顺序指定任意独一的值。
enum { e1(v1), e2(v2), ... , en(vn) [[, (n)]] } Te;
一个枚举在字节流中占据的空间足够存储其定义的最大有序数值。下面的定义会使用1个字节来表示Color类型的域。
enum { red(3), blue(5), white(7) } Color;
一个选择是指定一个值但不关联标记以强制定义枚举的大小,这样无需定义一个多余的元素.在下面这个例子中,Taste在字节流中会消耗2个字节, 但只能表示数值1,2,或4。
enum { sweet(1), sour(2), bitter(4), (32000) } Taste;
一个枚举类型的元素的名称被局限于定义的类型。在第一个例子中,对枚举的第二个元素的完全合格的引用是Color.blue,如果赋值的目标是被 很好地指定(译注:这句话的意思应该不会引起冲突,或叫二义性),则这样的格式是不需要的。
Color color = Color.blue; /* 过度指定, 合法 */
Color color = blue; /* 正确, 类型隐藏 */
对于不能转化为外部表示的枚举(原文:external representation),其数字信息会被忽略。
enum { low, medium, high } Amount;
出于方便,结构体类型可以由原始类型构建。每个规范声明了一个新的、独特的类型。定义的语法很像C语言:
struct {
T1 f1;
T2 f2;
...
Tn fn;
} [[T]];
结构体内的域可以用类型的名字来描述,使用类似于枚举的语法。例如,T.f2引用了前面定义的结构的第二个域。结构体的定义可以嵌套。
基于从环境中获得的知识,定义的结构体可以有一些变量。选择符必须是一个定义了结构体中变量取值范围的枚举类型。在select中声明的每个枚举元素必须有一个case条件。Case条件有受限的通过条件:如果两个case条件中间紧紧相连,它们中间没有域,则它们拥有相同的域。因此,在下面的例子中,"orange"和"banana"都包含V2,注意这是TLS 1.2中的一个新的语法。
变量结构体的体可以添加一个标签用于引用。通过这个机制变量可以在运行时被选择,并不会被描述语言所限制。
struct {
T1 f1;
T2 f2;
....
Tn fn;
select (E) {
case e1: Te1;
case e2: Te2;
case e3: case e4: Te3;
....
case en: Ten;
} [[fv]];
} [[Tv]];
例如:
enum { apple, orange, banana } VariantTag;
struct {
uint16 number;
opaque string<0..10>; /*变长*/
} V1;
struct {
uint32 number;
opaque string[10]; /* 固定长度 */
} V2;
struct {
select (VariantTag) { /*selector的值是隐晦的 */
case apple:
V1; /* 变量体, tag = apple */
case orange:
case banana:
V2; /*变量体, tag = orange or banana */
} variant_body; /* 变量的可选标签*/
} VariantRecord;
5个密码操作(数字签名,流密码加密,块密码加密,AEAD(authenticated encryption with)加密,公钥加密)分别被命名为:数字签名、流加密、块加密、aead加密和公钥加密。一个域的密码处理流程是通过将一个经过适当合适的关键字放在域类型规范之前来指定的。密码密钥由当前连接的状态所暗示的(见6.1节)
一个数字签名元素被编码为一个DigitallySigned结构:
struct {
SignatureAndHashAlgorithm algorithm;
opaque signature<0..2^16-1>;
} DigitallySigned;
algorithm域指定了所使用的算法(见7.4.1.4.1节对这个域的定义)。需要注意的是algorithm域的介绍是改变自以前的版本。signature是一个使用这些算法对内容元素进行的数字签名。这些内容本身不会出现在网络通路上但可以被简单计算出来。signature的长度由签名算法和密钥确定。
对于RSA签名,opaque向量包含使用RSASSA-PKCS1-v1_5签名方案(定义在[PKCS1]中)所生成的signature。 正如在[PKCS1]中所讨论的,DigestInfo必须是DER编码的[X680][X690]。对于没有参数的hash算法(包括SHA- 1),DigestInfo.AlgorithmIdentifier.parameters 域必须是NULL,但实现上必须接受有无参数和NULL参数。需要注意的是TLS早期版本使用了一个不同的RSA签名方案,这个方案不包含 DigestInfo编码。对于DSA,20字节的SHA-1hash会通过数字签名算法来直接运行,不需要添加额外的hash算法。这样产生了两个值,r和s。DSA签名 是一个opaque向量,像上面的一样,其内容是DER编码的:
Dss-Sig-Value ::= SEQUENCE {
r INTEGER,
s INTEGER
}
注意:在当前的术语中,DSA是指数字签名算法,DSS是指NIST标准。在原始的SSL和TLS规范中,DSS使用得很广泛。本文使用 "DSA"是指算法,"DSS"是中标准,在代码中使用"DSS"是考虑到历史的连续性。
在流密码加密中,明文是同源自一个密码学安全密钥的伪随机数生成器的相同数量的输出进行的异或运算。
在块密码加密中,每个明文块被加密为一个密文块。所有的密文块都以CBC(密码分组链接模式)模式生成。所有的分块加密的元素都会是密文块长度的精确倍数。
在AEAD加密中,明文同时被加密和进行完整性保护。输入可以是任意长度,aead加密输出通常比输入大,以容纳完整性检验值。
在公钥加密中,一个公钥算法所加密的数据只有使用匹配的私钥才能解密。一个公钥加密元素被编码成一个opaque向量<0..2^16-1>,这里的长度由加密算法和密钥指定。
RSA加密可以使用RSAES-PKCS1-v1_5加密机制实现,这个机制在[PKCS1]中定义。
在下面的例子中,
stream-ciphered struct {
uint8 field1;
uint8 field2;
digitally-signed opaque {
uint8 field3<0..255>;
uint8 field4;
};
} UserType;
内部结构(field3和field4)的内容被用作签名/hash算法的输入,然后整个结构体被流密码加密。这个结构体的长度按字节计算,等于field1和field2所用的2个字节,加上签名和hash算法用的2个字节,加上签名长度用的两个字节,加上签名算法输出的长度。签名的长度是已知的,因为签名所用的算法和密钥在编码或解码这个结构体之前就已经知道了。
出于规范性,固定类型的常量的定义可以通过声明一个所需类型的符号并赋值来实现。
不确定的类型(译注:原文是“Under-specified types”)(opaque,变长向量,和包含opaque类型的结构体)不能被赋值。多元素结构体或向量的赋值可以忽略域的名称。
例如:
struct {
uint8 f1;
uint8 f2;
} Example1;
Example1 ex1 = {1, 4}; /* 设置 f1 = 1, f2 = 4 */
TLS记录层使用一个有密钥的信息验证码(MAC)来保护信息的完整性。本文中定义的密码算法族使用了一个被称为HMAC(在[HMAC]中描 述)的MAC算法,它基于一个hash函数。如果必要的话其它密码算法族可以定义它们自己的MAC算法。
此外,为了进行密钥生成或验证,需要一个MAC算法对数据块进行扩展以增加机密性。这个伪随机函数(PRF)将机密信息(secret),种子和 身份标签作为输入,并产生任意长度的输出。
在本节中,我们基于HMAC定义了一个PRF。这个使用SHA-256 hash函数的PRF被用于所有的密码算法族,这些密码算法在本文中定义,或在本文之前、在TLS1.2的协商阶段就发表的TLS文献中定义。新的密码算 法族必须显式指定一个PRF,通常应该将SHA-256或更强的标准hash算法与TLS PRF一同使用。
首先,我们定义一个数据扩展函数,P_hash(secret, data),它使用一个hash函数扩展一个secret和种子,形成任意大小的输出:
P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
HMAC_hash(secret, A(2) + seed) +
HMAC_hash(secret, A(3) + seed) + ...
这里"+"是指级联。
A()被定义为:
A(0) = seed
A(i) = HMAC_hash(secret, A(i-1))
必要时P_hash可以被多次迭代,以产生所需数量的数据。例如,如果P_SHA256被用于产生80字节的数据,它应该被迭代3次(通过 A(3)),产生96字节的输出数据;最终迭代产生的最后16字节会被丢弃,留下80字节作为输出数据。
TLS的PRF可以通过将P_hash运用与secret来实现:
PRF(secret, label, seed) = P_
label是一个ASCII字符串。它应该以严格地按照它被给出的内容进行处理,不包含一个长度字节或结尾添加的空字符。例 如,label"slithy toves"应该通过hash下列字节的方式被处理:
73 6C 69 74 68 79 20 74 6F 76 65 73
(译注:上述数据是字符串"slithy toves"的十六进制格式)
TLS记录协议是一个层次化的协议。在每一层中,消息都可能包含长度、描述、内容等域。记录协议承载被发送的消息,将数据分片为可管理的块,有选择地压缩数据,应用MAC,加密,传输最终数据。接收到的数据被解密,验证,解压缩,重组,然后传递给高层客户。
本文中描述了4个使用记录协议的协议:握手协议,告警协议,更改密码规格协议,和应用数据协议。为了支持TLS协议扩展,额外的记录内容类型也可以被记录协议支持。新的记录内容类型值被IANA在TLS内容类型注册(在12节描述)中分配。
应用不能发送本文没有定义的记录类型,除非经过一些扩展的协商。如果一个TLS实现接收到一个非期望的记录类型,它应该发送一个“unexpected_message”告警消息。
任何被设计用来使用TLS的协议必须严格地设计以处理可能的攻击。从实践上来说,者意味着协议设计者必须意识到TLS能够实现何种安全特性,无法提供或不能安全地依赖后者。
特别需要注意的是一个记录消息的类型和长度不能使用加密保护。如果这个信息本身是敏感的,应用设计者可能会希望采取一些措施(填充,覆盖流量(译注:原文是“cover traffic”))以减小信息泄露。
一个TLS连接的状态就是TLS记录协议的操作环境。 它指定了一个压缩算法,一个加密算法,一个MAC算法。此外,这些算法的参数必须是已知的:用于连接的读、写两个方向的MAC密钥和块加密密钥。 逻辑上总是有4个状态比较突出:当前读和写状态,挂起的读和写状态。所有的记录协议都在当前的读写状态下处理。挂起状态的安全参数可以通过TLS 握手协议来设置,而ChangeCipherSpec可以有选择地设置当前状态为挂起状态,在这种情况下适当的当前状态被设置,并被挂起状态所替 代; 挂起状态随后会被重新初始化为一个空状态。将一个状态未经安全参数的初始化就设置为一个当前状态是非法的。初始当前状态一直会指定不使用加密,压缩或 MAC。
一个TLS连接读写状态的安全参数可以通过提供如下值来设定:
连接终端
在这个连接中这个实体被认为是“client”或“server”。
PRF算法
被用于从主密钥生成密钥的算法(见5和6.3节)。
块加密算法
被用于块加密的算法。本规范包含了这种算法的密钥长度,它是成块加密,流加密,或AEAD加密,密文的块大小(如果合适的话),和显示和隐式初始化向量 (或nonces)的长度
MAC算法
被用于消息验证的算法。本规范包含了MAC算法返回值的长度。
压缩算法
用于数据压缩的算法。被规范必须包含算法执行压缩所需的所有信息。
主密钥
在连接的两端之间共享的48字节密钥
客户端随机数
由客户端提供的32字节随机数
服务器随机数
由服务器提供的32字节随机数
上述参数通过描述语言定义如下:
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;
enum { null(0), (255) } CompressionMethod;
/* CompressionMethod, PRFAlgorithm,
BulkCipherAlgorithm, 和 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;
记录层会使用安全参数产生如下的6个条目(其中的一些并不是所有算法都需要的,因此会留空):
client write MAC key
server write MAC key
client write encryption key
server write encryption key
client write IV
server write IV
当server端接收并处理记录时会使用client写参数,反之亦然。使用安全参数来生成这些条目的算法在6.3节中描述。
一旦安全参数被设定且密钥被生成,连接状态就可以将它们设置为当前状态来进行初始化。这些当前状态必须在处理每一条记录后更新。每个连接状态包含如下元素:
压缩状态
压缩算法的当前状态
密码状态
加密算法的当前状态。这个状态由连接的预定密钥组成。对于流密码,这个状态也将包含对流数据进行加解密所需的任何必要的状态信息。
MAC密钥
当前连接的MAC密钥,以前述方式生成。
序列号
每个连接状态包含一个序列号,读状态和写状态分别维持一个序列号。当一个连接的状态被激活时序列号必须设置为0.序列号的类型是uint64,所以序列号大小不会
超过2^64-1。序列号不能回绕。如果一个TLS实现需要回绕序列号,则必须重新协商。一个序列号在每条记录信息被发送之后增加:特别地,在一个特殊连接状态下发送的
第一条记录消息必须使用序列号0.
TLS记录层以任意大小的非空块从高层接收无解释的数据。
记录层将信息块分片为携带2^14字节或更小的大块数据的TLSPlaintext。client信息边境并不在记录层保留(即,多个同一内容类型的client信息会被合并成一个TLSPlaintext,或者一个消息会被分片为多个记录)。
struct {
uint8 major;
uint8 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
协议的版本。本文所描述的TLS 1.2的版本是{3, 3}。版本值3.3是基于历史的,因为TLS 1.0使用的是{3, 1}(见附录A.1.)。需要注意的是一个支持多个版本TLS的client在收到ServerHello之前可能并不知道版本是什么。关于ClientHello中使用什么样的记录层版本号的讨论见附录E。
length
接下来的TLSPlaintext.fragment的长度(以字节计) 。这个长度不能超过2^14.
fragment
应用数据。这种数据是透明的并且作为一个独立的块由type域所指定的高层协议来处理。
实现上不能发送fragments长度为0的握手,报警,或ChangeCipherSpec内容类型。发送fragment长度为0的应用数据在进行流量分析时是有用的。
注意:不同TLS记录层内容类型的数据可能是交错的。应用数据的传输优先级通常低于其它内容类型。然而, 记录必须以记录层能提供保护的顺序传递到网络中。接收者必须接收并处理一条连接中在第一个握手报文之后交错的应用层流量.
所有的记录都使用在当前会话状态中定义的压缩算法进行压缩。这里的压缩算法必须一直是激活的;然而,初始时它被定义为CompressionMethod.null。压缩算法将一个TLSPlaintext结构转换为一个TLSCompressed结构,在连接状态被激活时压缩函数会由默认状态信息进行初始化。[RFC3749]描述了用于TLS的压缩算法.
压缩必须是无损的,也不能增加内容的长度超过1024字节。如果解压函数遇到一个TLSCompressed.fragment,其解压后的函数超过2^14字节,则必须报告一个fatal压缩失败错误。
struct {
ContentType type; /* 与TLSPlaintext.type相同 */
ProtocolVersion version;/* 与TLSPlaintext.version相同 */
uint16 length;
opaque fragment[TLSCompressed.length];
} TLSCompressed;
length
接下来的TLSCompressed.fragment的长度(以字节计) 。这个长度不能超过2^14 + 1024.
fragment
TLSPlaintext.fragment压缩后的形态
注意:一个CompressionMethod.null的操作是恒等操作,不改变任何域。
实现注意:解压函数需要保证消息不会导致内部缓存溢出。
解密和MAC函数将一个TLSCompressed结构转换为TLSCiphertext,加密函数实现相反的过程。记录的MAC包含了一个序列号用于感知丢失、增加和重复的消息。
struct {
ContentType type;
ProtocolVersion version;
uint16 length;
select (SecurityParameters.cipher_type) {
case stream: GenericStreamCipher;
case block: GenericBlockCipher;
case aead: GenericAEADCipher;
} fragment;
} TLSCiphertext;
type
这个type域与TLSCompressed.type相同.
version
这个version域与TLSCompressed.version相同.
length
接下来的TLSCiphertext.fragment的长度(以字节计) 。这个长度不能超过2^14 + 2048.
fragment
TLSCompressed.fragment的加密形态, 加上MAC.
6.2.3.1. 空加密或标准流加密
流加密(包括BulkCipherAlgorithm.null;见附录A.6)将TLSCompressed.fragment结构转换为流的TLSCiphertext.fragment结构,或相反的操作。
stream-ciphered struct {
opaque content[TLSCompressed.length];
opaque MAC[SecurityParameters.mac_length];
} GenericStreamCipher;
MAC用如下方式产生:
MAC(MAC_write_key, seq_num +
TLSCompressed.type +
TLSCompressed.version +
TLSCompressed.length +
TLSCompressed.fragment);
这里的“+”表示连接。
seq_num
这个记录的序列号
MAC
由SecurityParameters.mac_algorithm指定的MAC算法
需要注意的是MAC在加密之前计算。流加密算法加密整个块,包括MAC。对于不使用同步向量的流加密算法(如RC4),流加密算法状态在一个记录的结束后就简单地用于随后的包。如果密码算法族是TLS_NULL_WITH_NULL_NULL,加密则由同一性操作构成(即数据不加密,MAC大小是0,意味着不使用MAC)。对于空算法和流加密,TLSCiphertext.length是TLSCompressed.length加上SecurityParameters.mac_length。
6.2.3.2. CBC块加密
对于块加密算法(如3DES或AES),加密和MAC函数将TLSCompressed.fragment转换为TLSCiphertext.fragment结构块或相反的操作。
struct {
opaque IV[SecurityParameters.record_iv_length];
block-ciphered struct {
opaque content[TLSCompressed.length];
opaque MAC[SecurityParameters.mac_length];
uint8 padding[GenericBlockCipher.padding_length];
uint8 padding_length;
};
} GenericBlockCipher;
MAC的产生方法与6.2.3.1节相同.
IV
初始化向量(IV)应该随机产生,并且必须是不能预测的。需要注意的是在TLS1.1以前的版本是没有IV域的。以前的记录中最后一个密文块(CBC的剩 余)被用作IV。使用随机IV是为了阻止在[CBCATT]中描述的攻击。对于块加密,IV的长度是 SecurityParameters.record_iv_length的值,这个值等于 SecurityParameters.block_size。
padding
Padding用于强制明文的长度是块加密块长度的整数倍,它可能是任意长度,最长是255字节,只要它使得TLSCiphertext.length是 块长度的整数倍。超出必要的长度在挫败基于对交换消息长度的分析进行的协议攻击时可能是必要的。在padding数据向量中的每个uint8必须 被填充padding的长度值。接收者必须检查这个pading且必须使用bad_record_mac alert来暗示padding错误。
padding_length
Padding的长度必须使GenericBlockCipher的总长度是密码块长度的整数倍。合法的取值范围是从0到255,包含0和 255.这个长度指定了padding域的长度但不包含padding_length域的长度。
密文数据的长度(TLSCiphertext.length)大于SecurityParameters.block_length, TLSCompressed.length, SecurityParameters.mac_length, 和padding_length之和。
例如:如果块长度是8字节,内容长度(TLSCompressed.length)是61字节,MAC长度是20字节,则填充签订长度是82字节 (不包含IV)。为了使总长度是8字节(块长度)的偶数倍,填充长度模8必须等于6.即填充长度可以是6, 14, 22, 以此类推, 直到254.如果有必要使填充长度最小,为6,则填充必须是6字节,每个字节都赋值为6.因此,GenericBlockCipher在块加密前的最后8 个字节可能是 xx 06 06 06 06 06 06 06, 这里xx是MAC的最后一个字节。
注意:对于CBC模式(密文分组链接模式)的块加密,关键的是记录的整个明文在传输任何密文之前就已被知道。否则,攻击者可能会发动 [CBCATT]中描述的攻击。
实现注记:Canvel et al. [CBCTIME]阐述了一个基于MAC计算时间的针对CBC填充的定时攻击。为了防御此类攻击,实现上必须确保物理填充是否正确记录的处理时间是基本一 样的。通常,做到这一点最好的方式是即使填充是错的也要计算MAC,然后只能丢弃整个包。例如,如果填充不正确,实现上可能假定一个0长度的填充 然后计算MAC。这样会留下一个小的定时通道,因为MAC计算性能某种程度上依赖于数据分片的长度,但不能确信这个长度会大到足够被利用,这是因 为现存MAC的块大而定时信号的长度小。
6.2.3.3. AEAD加密
对于AEAD[AEAD]加密(如:[CCM]或[GCM]),AEAD函数将TLSCompressed.fragment结构转换为AEAD TLSCiphertext.fragment结构或相反。
struct {
opaque nonce_explicit[SecurityParameters.record_iv_length];
aead-ciphered struct {
opaque content[TLSCompressed.length];
};
} GenericAEADCipher;
AEAD加密的输入有:单个密钥,一个nonce,一块明文,和被包含在验证检查中的“额外数据”(在[AEAD]2.1节中描述)。密钥是 client_write_key或者the server_write_key。不使用MAC密钥。
每个AEAD密码族必须指定提供给AEAD操作的nonce是如何构建的,GenericAEADCipher.nonce_explicit部 分的长度是什么。在很多情况下,使用在[AEAD]3.2.1节中描述的部分隐藏的nonce技术是合适的;record_iv_length就 是GenericAEADCipher.nonce_explicit的长度。在这种情况下,隐式部分应该作为client_write_iv和 server_write_iv从 key_block中(在6.3节中描述)推导出来,显示部分被包含在GenericAEAEDCipher.nonce_explicit中。
明文是TLSCompressed.fragment。
额外的验证数据(我们表示为additional_data)定义如下:
additional_data = seq_num + TLSCompressed.type +
TLSCompressed.version + TLSCompressed.length;
这里“+”表示连接。
AEAD的输出由AEAD加密操作所产生的密文输出构成。长度通常大于TLSCompressed.length,在量上会随着AEAD加密的不同而不同。因为加密可能包含填充,开销的大小可能会因TLSCompressed.length值二不同。每种AEAD加密不能产生大于1024字节的长度扩张。象征性地,
AEADEncrypted = AEAD-Encrypt(write_key, nonce, plaintext,
additional_data)
为了解密和验证,加密算法将密钥、nonce、“额外数据”和AEADEncrypted的值作为输入。输出要么是明文要么是解密失败导致的错误。这里没有分离完整性检查。即:
TLSCompressed.fragment = AEAD-Decrypt(write_key, nonce,
AEADEncrypted,
additional_data)
如果解密失败,会产生一个bad_record_mac致命警报。
记录协议需要一个算法从握手协议提供的安全参数中生成当前连接状态(见附录A.6)所需的密钥。
主密钥被扩张为一个安全字节序列,它被分割为一个客户端写MAC密钥,一个服务端写MAC密钥,一个客户端写加密密钥,一个服务端写加密密钥。它们中的每一个都是从字节序列中以上述顺序生成。未使用的值是空。一些AEAD加密可能会额外需要一个客户端写IV和一个服务端写IV(见6.2.3.3节)。
生成密钥和MAC密钥时,主密钥被用作一个熵源。
为了生成密钥数据,计算
key_block = PRF(SecurityParameters.master_secret,
"key expansion",
SecurityParameters.server_random +
SecurityParameters.client_random);
直到产生足够的输出。然后,key_block会按照如下方式分开:
client_write_MAC_key[SecurityParameters.mac_key_length]
server_write_MAC_key[SecurityParameters.mac_key_length]
client_write_key[SecurityParameters.enc_key_length]
server_write_key[SecurityParameters.enc_key_length]
client_write_IV[SecurityParameters.fixed_iv_length]
server_write_IV[SecurityParameters.fixed_iv_length]
目前,client_write_IV和server_write_IV只能由[AEAD]3.2.1节中描述的隐式nonce技术生成。
实现注记:当前定义的密码协议族使用最多的是AES_256_CBC_SHA256。它需要2 x 32字节密钥和2 x 32字节MAC密钥,它们从128字节的密钥数据中产生。
TLS拥有3个子协议用于支持通信双方协商记录层安全参数,确认它们自己的身份,实例化以协商的安全参数,彼此报告错误状况。
握手协议负责协商一个会话,这个会话由以下元素组成:
session identifier
由Server端选取的一个任意字节的序列用于辨识一个活动的或可恢复的连接状态。
peer certificate
对端的X509v3 [PKIX]证书。这个元素的状态可以是空
compression method
在加密之前压缩数据的算法
cipher spec
指定用于产生密钥数据的伪随机函数(PRF),块加密算法(如:空,AES等),和MAC算法(如:HMAC-SHA1)。它也定义了密码学属性如mac_length. (常见定义见附录A.6)
master secret
client和server之间共享的48字节密钥
is resumable
一个用于标识会话是否能被用于初始化新连接的标签
这些元素元素随后会被用于产生安全参数并由记录层在保护应用数据时使用。利用TLS握手协议的恢复特性,使用相同的会话可以实例化许多连接。
变更密码规范协议存在的目的是通告加密策略的改变。这个协议由单个消息组成,这个消息会被在当前(并非挂起)连接状态下被加密和压缩。这个消息由一个值为1的单个字节构成。
struct {
enum { change_cipher_spec(1), (255) } type;
} ChangeCipherSpec;
ChangeCipherSpec消息可以被client和server发送,以通告接收方随后的记录消息将会由新协商的密码规范和密钥所保护。接收到这个消息会导致接收者指示记录层立即将读挂起状态复制到读当前状态。发送此消息后,发送这必须立即指示记录层将写挂起状态转变为写活动状态(见6.1节)。ChangeCipherSpec消息在握手过程中发送,但要在安全参数协商完毕之后,且在验证结束消息发送之前。
注意:如果发生一个重握手事件而连接的数据还在流动,通信的双方可能使用旧的CipherSpec继续发送数据。然而,一旦ChangeCipherSpec被发送了,新的CipherSpec必须被使用。先发送ChangeCipherSpec的一方不知道另外一方已经完成了新密钥数据的计算(例如,如果它执行一个耗时的公钥操作)。因此,在接收方必须缓存数据时会存在一个小的窗口时间。事实上,在现代机器中这个间隔会相当短。
TLS记录层支持的内容类型之一是报警类型。报警消息传递了消息的严重程度(警告或致命)和报警的描述。致命类型的报警消息会导致连接的立即结束。在这种情况下,与会话对应的其它连接可以继续,但会话描述符必须非法化,以阻止失败的会话被用于建立新的连接。像其它消息一样,报警消息按照当前连接的状态指定的那样被加密和压缩。
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),
(255)
} AlertDescription;
struct {
AlertLevel level;
AlertDescription description;
} Alert;
客户端和服务器必须共享连接结束的信息以避免截断攻击每一方都可以发起关闭消息的交换
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管理其数据传输的使用模式,包括何时打开或关闭连接。
注意:假定在销毁传输之前关闭一个连接会可靠地投递挂起的数据。
在TLS握手协议中错误处理非常简单。当探测到一个错误时,探测方发送一个消息给另一方。发送或接收到一个致命警报消息之后,双方立即关闭连接。服务端和客户端必须忘记任何会话描述符,密钥,和与一个失败的连接相关的机密信息。因此,任何一个被一个致命警报终结的连接都不能被恢复。
一个实现无论何时遭遇到一个被定义为致命警报的消息时,它必须在关闭者连接之前发送合适的警报。对于所有警报级别没有显式指定的错误,发送方可以自行决定是否将其作为致命错误对待。如果实现选择发送一个警报但意图随后立即关闭连接,它必须以致命警报等级发送这个警报。
如果收到一个警告级别的警报时,通常连接可以正常继续。如果接收方决定不再继续连接(例如,收到一个它不愿意接收的no_renegotiation警报),它应该发送一个致命警报终结连接。鉴于此,发送防通常不会知道接收方会有何行为。因此,警告类警报在发送方向继续连接时不是很有用,并且因此有时会被忽略。例如,如果一个对端决定接受一个证书过期警报(也许是在与用户确认之后)并想继续连接,它通常不会发送一个certificate_expired警报。
定义了如下类型的错误警报:
unexpected_message
收到一个不合适的消息。这个警报一直是致命类型且不应该在正确的实现之间被观察到。
bad_record_mac
如果收到一个记录其MAC是不正确的,则会返回这种警报。当因为一个TLSCiphertext以不正确的方式解密(或者它不是块长度的偶数倍,或它的填充值被检查出不正确)而发送一个警报时此类警报也必须发送。这个消息通常是致命类型并且不应该在正确的实现之间被观察到(除非消息在网络中损坏)。
decryption_failed_RESERVED
这种警报被用于一些早期版本的TLS,且可能导致针对CBC模式的特定攻击。在兼容型实现中不能发送此消息。
record_overflow
收到一个TLSCiphertext的消息其长度大于2^14+2048字节,或一个记录被解密为TLSCompressed后其长度大于2^14+1024字节。这个消息通常是致命类型并且不应该在正确的实现之间被观察到(除非消息在网络中损坏)。
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
由于一些域超出指定范围或消息长度不正确导致消息不能被解码. 这个消息通常是致命类型的且绝不能在两个合理的TLS实现之间通信时出现(除非消息在网络中损坏)
decrypt_error
一个握手密文操作失败, 包括不能正确验证签名或一个结束消息. 这个消息一直是致命的。
export_restriction_RESERVED
这个alert曾被用于一些TLS的早期版本. 在兼容实现中不能发送此消息.
protocol_version
Client端试图协商的协议版本号版本被支持(例如, 旧的协议版本由于安全原因被废弃). 这个消息一直是致命的。
insufficient_security
当一个协商由于server需要比client能够支持的更安全的算法族而失败时取代handshake_failure消息返回, 这个消息一直是致命的.
internal_error
一个与对端或协议正确性都无关的内部错误(例如内存分配错误)使得协议无法继续执行.这个消息一直是致命的.
user_canceled
这次握手由于一些与协议错误无关的原因被取消.如果在握手完成后用户才取消操作,只通过发送一个close_notify消息来关闭连接更合适. 这个alert应该跟随一个close_notify. 这个消息通常是一个警告.
no_renegotiation
由客户端发送用于响应一个hello请求,或由服务端发送用于在初始化握手时响应一个client hello.二者中的任何一个都通常会导致重协商;当重协商不合适时, 接收端应答用此警报来响应.这时,原始请求者能决定是否继续连接.一种适当的情况是一个server产生一个进程以满足一个请求;这个进程能在启动时接收安全参数(密钥长度,认证等),在这个阶段之后再修改这些参数则可能会很困难.这个消息通常是一个警告.
unsupported_extension
由客户端发送;该客户端接收到了server hello消息中包含的一个扩展,但这些扩展不能被放入相应的client hello消息中.这个消息一直是致命的.
新的Alert的值在12章中由IANA指定.
会话状态的加密参数由TLS握手协议产生, 这个协议在TLS记录层之上运行. 当一个TLS client和server开始通信时, 它们就协议版本达成一致, 选择加密算法, 彼此选择验证, 使用公钥加密技术产生共享密钥.
TLS握手协议包含如下几步:
- 交互Hello消息以协商算法, 交互随机数, 检查会话恢复.
- 交互必要的密码参数以允许client和server协商预主密钥
- 交换证书和密码信息以允许client和server进行身份认证
- 从预主密钥和交互的随机数中产生主密钥
- 提为记录层供安全参数
- 允许client和server验证它们的对端已经计算出了相同的安全参数, 而且握手过程不被攻击者篡改.
需要注意的是更高层不能过度依赖TLS是否一直为连接双方协商最强的连接.中间人攻击者有很多方法能够使两个协商者降级到使用它们能够支持的最低的安全方法. 协议被用来最小化这种风险, 但仍然会存在攻击: 例如, 一个攻击者能够阻止访问一个安全服务运行的端口, 或试图使对端协商一个未经认证的连接. 基础的规则是更高层必须认识到它们的安全需求是什么, 且绝对不能在一个不满足它们的安全需求的通道上传输信息. 如果任何密码族能够提供它们承诺的安全等级则TLS协议是安全的: 如果你使用一个1024位的RSA密钥交换和验证过证书的主机来协商3DES, 你可以期待它是安全的.
这些目标的达成是靠握手协议实现的, 这个协议总结如下: client发送一个ClientHello消息, server必须回应一个ServerHello消息或产生一个验证错误并且连接会失败. ClientHello和ServerHello用于在client和server之间建立安全性增强的能力. ClientHello和ServerHello建立了如下的属性: 协议版本, 会话ID, 密码协议族, 压缩算法. 此外, 产生并交换两个随机数: ClientHello.random和ServerHello.random.
实际的密钥交换使用了最多4个消息: server Certificate, ServerKeyExchange, client Certificate,和ClientKeyExchange.新的密钥交换方法可以通过这些方法产生:为这些消息指定一个格式, 并定义这些消息的用法以允许client和server就一个共享密钥达成一致. 这个密钥必须很长;当前定义的密钥交换方法交换的密钥大于46字节.
在hello消息之后, server会在Certificate消息中发送它自己的证书, 如果它即将被认证. 此外, 一个ServerKeyExchange消息会被发送, 如果需要的话(例如, 如果server没有证书, 或者它的证书只用于签名).如果server被认证过了, 它可能会要求client发送证书, 如果对于已选择的密码协议族来说是合适的话.接下来, server会发送ServerHelloDone消息, 意味着握手的hello消息阶段完成. server将会等待client的响应. 如果server以及发送了一个CertificateRequest消息, client必须发送Certificate消息. 现在ClientKeyExchange消息需要发送, 这个消息的内容取决于ClientHello和ServerHello之间选择的公钥算法. 如果client发送了一个带签名能力的证书, 一个数字签名的CertificateVerify消息需要发送以显式验证证书中私钥的所有权.
这时, client发送一个ChangeCipherSpec消息, 并且复制待定的Cipher Spec到当前的Cipher Spec中. 然后client在新算法, 密钥确定后立即发送Finished消息. 作为回应, server会发送它自己的ChangeCipherSpec消息, 将待定的Cipher Spec转换为当前的Cipher Spec, 在新的Cipher Spec下发送Finished消息. 这时, 握手完成, client和server可以开始交换应用层数据(见下面的流程图). 应用数据一定不能在第一个握手完成前(在一个非TLS_NULL_WITH_NULL_NULL类型的密码协议族建立之前)发送.
Client Server
ClientHello -------->
ServerHello
Certificate*
ServerKeyExchange*
CertificateRequest*
<-------- ServerHelloDone
Certificate*
ClientKeyExchange
CertificateVerify*
[ChangeCipherSpec]
Finished -------->
[ChangeCipherSpec]
<-------- Finished
Application Data <-------> Application Data
Figure 1. 一个完整的握手消息流程
* 意味着可选或并不会一直被发送的条件依赖性消息.
注记: 为了有助于避免pipeline stalls, ChangeCipherSpec是一种独立的TLS协议内容类型, 并且事实上不是一种TLS消息.
当client和server决定继续一个以前的会话或复制一个现存的会话(取代协商新的安全参数)时, 消息流如下:
Client使用需要恢复的当前会话的ID发送一个ClientHello. Server检查它的会话缓存以进行匹配. 如果匹配成功, 并且server愿意在指定的会话状态下重建连接, 它将会发送一个带有相同会话ID值的ServerHello消息. 这时, client和server必须都发送ChangeCipherSpec消息并且直接发送Finished消息. 一旦重建立完成, client和server可以开始交换应用层数据(见下面的流程图). 如果一个会话ID不匹配, server会产生一个新的会话ID, 然后TLS client和server会完成一个完整的握手.
Client Server
ClientHello -------->
ServerHello
[ChangeCipherSpec]
<-------- Finished
[ChangeCipherSpec]
Finished -------->
Application Data <-------> Application Data
Figure 2. 一个简化的握手的消息流程
每个消息的内容和含义会在下面的章节中详细阐述.
TLS握手协议是TLS记录协议的一个已定义的高层客户端. 这个协议用于协商一个会话的安全属性. 握手消息提供给TLS记录层, 这里它们会被封装在一个或多个TLSPlaintext结构中, 这些结构按照当前活动会话状态所指定的方式被处理和传输.
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;
握手协议消息在下文中会以发送的顺序展现;以未期望的顺序发送握手消息会导致一个致命错误。然而,不需要的握手消息会被忽略。需要注意的是例外的顺序是:证书消息在握手(从server到client,然后从client到server)过程中会使用两次,但只在它第一次出现的位置处描述。不被这些顺序所约束的一个消息是HelloRequest消息,它可以在任何时间发送,但如果在握手中间到达应该被client忽略。
新的握手消息类型由IANA指定,正如12节中所描述的。
hello阶段的消息用于在client和server之间交互安全增强能力。当一个新的会话开始时,记录层连接状态加密,hash,和压缩算法被初始化为空。当前连接状态用于重协商消息。
7.4.1.1. Hello Request
当这个消息即将被发送时:
HelloRequest消息可以在任何时间由server发送。
这个消息的含义:
HelloRequest是一个简单的通告client应该开始重协商流程。 在响应过程中,client应该在方便的时候发送一个ClientHello消息。这个消息并不是意图确定哪端是client或server,而仅仅是发起一个新的协商。server不应该在client发起连接后立即发送一个HelloRequest。这时发送一个ClientHello消息是client的工作。
如果client当前正在协商一个会话时这个消息会被client忽略。 这个消息会被client忽略,如果client不想重新协商一个会话,或client希望响应一个no_renegotiation警报。因为握手消息意图先于应用数据被传送, 它希望协商会在少量记录消息被client接收之前开始。 如果server发送了一个HelloRequest但没有收到一个ClientHello响应,它应该用一个致命警报关闭连接。
在发送一个HelloRequest之后, server不应该重复这个请求直到随后的握手协商完成。
这个消息的结构:
struct { } HelloRequest;
这个消息不能被包含在握手消息中维护的消息hash中, 也不能用于结束的消息和证书验证消息。
7.4.1.2. Client Hello
当这个消息被发送时:
当一个client第一次连接一个server时, 它被要求发送ClientHello作为第一个消息。 Client也能发送一个ClientHello作为对HelloRequest的响应,或用于自身的初始化以便在一个已有连接中重新协商安全参数。
这个消息的结构是:
ClientHello消息包含一个随机数据结构, 它会随后被用于协议中.
struct {
uint32 gmt_unix_time;
opaque random_bytes[28];
} Random;
gmt_unix_time
依据发送者内部时钟以标准UNIX 32位格式表示的当前时间和日期(从1970年1月1日UTC午夜开始的秒数, 忽略闰秒). 基本TLS协议不要求时钟被正确设置; 更高层或应用层协议可以定义额外的需求. 需要注意的是, 出于历史原因, 数据元素使用格林尼治时间命名, 它是当前时间通用时间基准世界协调时间(UTC)的前任.
random_bytes
由一个安全的随机数生成器产生的28个字节数据.
ClientHello消息包含一个变长的会话标识符. 如果非空, 这个值标识了同一对client和server之间的会话, client希望重新使用这个会话中server的安全参数.
会话标识符可能来自于一个早期的连接, 本次连接, 或来自另一个当前活动的连接. 第二个选择是有用的, 如果client只是希望更新随机数据结构并且从一个连接中导出数值; 第三个选择使得在无需重复全部握手协议的情况下就能够建立若干独立的安全连接.这些独立的连接可能先后或同时建立. 一个SessionID在握手协商完成成为有效的, 通过交互Finished消息得以完善, 并坚持到由于老化或在一个与会话有个的连接上遭遇致命错误导致它被删除为止. SessionID的实际内容由server定义.
opaque SessionID<0..32>;
警告: 由于SessionID在传输时没有加密或直接的MAC保护, server一定不能将机密信息放在会话标识符中或使伪造的会话标识符的内容违背安全原则.(需要注意的是握手的内容作为一个整体, 包括SessionID, 是由在握手结束时交换的Finished消息保护的).
密码族列表, 在ClientHello消息中从client传递到server, 以client所倾向的顺序(最喜爱的在最先)包含了client所支持的密码算法. 每个密码族定义了一个密钥交互算法, 一个块加密算法(包括密钥长度), 一个MAC算法, 和一个随机数生成函数. Server将选择一个密码协议族, 如果没有可以接受的选择, 在返回一个握手失败警报然后关闭连接. 如果列表包含了server不能识别, 支持或希望使用的密码协议族, server必须忽略它们, 并正常处理其余的部分.
uint8 CipherSuite[2]; /* Cryptographic suite selector */
ClientHello保护了client所支持的压缩算法列表, 按照client的倾向排序.
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;
TLS 允许在compression_methods域之后的extensions块中添加扩展. 通过查看compression_methods后面是否有多余的字节在ClientHello结尾处就能探测到扩展的存在. 需要注意的是这种探测可选数据的方法与正常的TLS变长域不一样, 但它用于与TLS一起定义的扩展兼容.
client_version
client愿意在本次会话中使用的TLS协议的版本. 这个应当是client所能支持的最新版本(值最大), 对于本规范来说, 这个版本应该是3.3(关于后向兼容性的细节见附录E).
random
一个client所产生的随机结构.
session_id
Client希望在本次连接中所使用的会话ID. 如果没有session_id或client想生成新蛋安全参数, 则这个域是空.
cipher_suites
Client所支持的密码选项列表, client最倾向使用的排在最先. 如果session_id域不空(意味着一个会话恢复请求), 这个向量必须至少包含那条会话中的cipher_suite. 数值在附录A.5中定义.
compression_methods
这是client所支持的压缩算法的列表, 按照client所倾向的顺序排列. 如果session_id域不空(意味着一个会话恢复请求), 它必须包含那条会话中的compression_method.这个向量中必须包含, 所有的实现也必须支持CompressionMethod.null. 因此, 一个client和server将一直能就压缩算法协商一致.
extensions
Clients可以通过在扩展域中发送数据来请求server的扩展功能. 实际的"Extension"的格式在7.4.1.4节中定义.
如果一个client使用扩展来请求额外的功能, 并且这个功能Server并不支持, 则client可以中止握手. 一个server必须接受带有或不带扩展域的ClientHello消息, 并且(对于其它所有消息也是一样)它必须检查消息中数据的数量是否精确匹配一种格式; 如果不是, 它必须发送一个致命"decode_error"警报.
发送ClientHello消息之后, client会等待ServerHello消息. Server返回的任何握手消息, 除HelloRequest外, 均被作为一个致命错误.
7.4.1.3. Server Hello
当这个消息被发送时:
Server将发送这个消息作为对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;
通过查看compression_methods后面是否有多余的字节在ServerHello结尾处就能探测到扩展的存在.
server_version
这个域将包含client在client hello消息中建议的较低版本和server所能支持的最高版本. 本规范中的版本是3.3. (关于后向兼容性的细节见附录E)
random
这个结构由server产生并且必须独立于ClientHello.random.
session_id
这是与本次连接相对应的会话的标识. 如果ClientHello.session_id非空, server将在它的会话缓存中进行匹配查询. 如果匹配项被找到, 且server愿意使用指定的会话状态建立新的连接, server会将与client所提供的相同的值反馈回去. 这意味着恢复了一个会话并且规定双方必须在Finished消息之后继续进行通信. 否则这个域会包含一个不同的值以标识新会话. Server会返回一个空的session_id以显示会话将不再被缓存从而不会被恢复. 如果一个会话被恢复了, 它必须使用原来所协商的密码协议族. 需要注意的是没有要求server恢复任何会话, 即使它之前提供了一个session_id.Client必须准备好进行一次完整的协商 -- 包括协商新的密码协议族-- 在任意一次握手中.
由server在ClientHello.cipher_suites中所选择的单个密码族. 对于被恢复的会话, 这个域的值来自于被恢复的会话状态.
compression_method
由server在ClientHello.compression_methods所选择的单个压缩算法. 对于被恢复的会话, 这个域的值来自于被恢复的会话状态.
extensions
扩展的列表. 需要注意的是只有由client给出的扩展才能出现在server的列表中.
7.4.1.4. Hello Extensions
扩展的格式为:
struct {
ExtensionType extension_type;
opaque extension_data<0..2^16-1>;
} Extension;
enum {
signature_algorithms(13), (65535)
} ExtensionType;
这里:
- "extension_type" 表示特定的扩展类型.
- "extension_data" 包含的信息却决于特定的扩展类型.
扩展的初始集合在一个文档[TLSEXT]中定义.The initial set of extensions is defined in a companion document[TLSEXT]. 扩展类型列表由INAM赋值维护,详见12节.
一个扩展不能出现在ServerHello中除非同样的扩展类型出现在对于的ClientHello中. 如果一个client在ServerHello中收到一个扩展类型但在相关的ClientHello中并没有请求, 它必须用一个unsupported_extension致命警报来丢弃握手.
尽管如此, "面向server"的扩展将来可以在这个框架中提供. 这样的一个扩展(比如, 类型x的扩展)可能要求client首先发送一个类型x的扩展在ClientHello中, 并且extension_data为空以表示它支持扩展类型. 在这个例子中, client提供了理解扩展类型的能力, server基于client提供的内容与其进行通信.
当ClientHello或ServerHello中有多个不同类型的扩展存在时, 这些扩展可能会以任意顺序出现. 一个类型不能拥有超过一个扩展.
最后, 需要注意的是扩展可能在开始一个新会话和要求恢复一个会话是发送. 的确, 一个请求恢复会话的client通常不会知道server是否会接受这个请求, 因此它应该像它不打算回复会话时那样发送扩展.
通常, 每个扩展类型的规范需要描述扩展对全部握手流程和会话恢复的影响. 大多数当前的TLS扩展仅当一个会话被初始化时才是相关联的: 当一个旧的会话被恢复时, server不会处理Client Hello中的扩展, 也不会将其包含在Server Hello中. 然而, 一些扩展可以在会话恢复时指定不同的行为.
在这个协议的新特性与现存特性之间会有一些敏感(以及不很敏感)的交互产生, 这可能会导致整体安全性的显著降低. 当设计新的扩展时应考虑下列事项:
- 一些情况下一个server没有就一个扩展协商一致是错误情况, 一些情况下则简单地拒绝支持特定特性. 通常,错误警报应该用于前者, server扩展中的一个域用于响应后者.
- 扩展应该尽可能在设计上阻止任何通过操纵握手消息来强制使用(或不使用)一个特殊特性进行的攻击. 无论这个特性是否被确认会导致安全问题, 这个原则都应该被遵循.
通常扩展域扩展域都会被包含在Finished消息的hash输入中, 但需要给予极大关注的是在握手阶段扩展改变了发送消息的含义. 设计者和实现者应该注意的事实是, 握手被认证后, 活动的攻击者才能修改消息并插入, 移动或替换扩展.
- 使用扩展来改变TLS设计的主要方面在技术上是可能的; 例如密码族协商的设计. 这种做法并不被推荐; 更合适的做法是定义一个新版本的TLS -- 尤其是TLS握手算法有特定的保护方法以防御基于版本号的版本回退攻击, 版本回退攻击的可能性应该在任何主要的修改设计中都是一个有意义的考量.
7.4.1.4.1. 签名算法
Client使用"signature_algorithms"扩展来向server表明哪个签名/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值都列出了一个client愿意使用的hash/签名对.这些值根据倾向使用的程度按降序排列.
注: 由于并不是所有的签名算法和hahs算法都会被一个实现所接受(例如: DSA接受SHA-1, 不接受SHA-256), 所有算法是按对列出.
hash
这个域表明可能使用的hash算法. 这些值分别表明支持无hash, MD5, SHA-1, SHA-224, SHA-256, SHA-384, 和SHA-512. "none"值用于将来的可扩展性, 以防一个签名算法在签名之前不需要hash.
signature
这个域表明使用哪个签名算法. 这些值分别表示匿名签名, RSASSA-PKCS1-v1_5, DSA和ECDSA. "anonymous"值在这个上下文中是无意义的, 但会在7.4.3节中使用. 它不能出现在这个扩展之中.
这个扩展的语义某种程度上有些复杂, 因为密码族表明允许的签名算法而不是hash算法. 7.4.2和7.4.3节描述了合适的规则.
如果client只支持默认的hash和签名算法(本节中所列出的), 它可以忽略signature_algorithms扩展. 如果client不支持默认的算法, 或支持其它的hash和签名算法(并且它愿意使用他们来验证server发送的消息, 如:server certificates和server key exchange), 它必须发送signature_algorithms扩展, 列出它愿意接受的算法.
如果client不发送signature_algorithms扩展, server必须执行如下动作:
- 如果协商后的密钥交换算法是(RSA, DHE_RSA,DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA)中的一个, 处理行为同client发送了 {sha1,rsa};
- 如果协商后的密钥交换算法是(DHE_DSS, DH_DSS)中的一个, 处理行为同client发送了{sha1,dsa}.
- 如果协商后的密钥交换算法是(ECDH_ECDSA,ECDHE_ECDSA)中的一个, 处理行为同client发送了{sha1,ecdsa}.
注: 这个对于TLS 1.1是一个变更, 且没有显式的规则, 但在实现上可以假定对端支持MD5和SHA-1.
注: 这个扩展对早于1.2版本的TLS是没有意义的, 对于之前的版本Client不能这个扩展. 然而, 即使client提供了这个扩展, [TLSEXT]中明确的规则要求server如果不能理解扩展则忽略之.
Server不能发送此扩展, TLS server必须支持接收此扩展.
当进行会话恢复时, 这个扩展不能被包含在Server Hello中, 且server会忽略Client Hello中的这个扩展(如果有).
当这个消息即将被发送时:
server必须发送一个Certificate,无论何时协商一致的密钥交换算法使用证书进 行认证(包括除DH_anon外本文中定义的所有密 钥交互算法)。 这个消息一直是 紧随在ServerHello消息之后。
这个消息的含义是:
这个消息传达了server的证书链给client.
证书必须适用于已协商的密码族的密钥交互算法和任何已协商的扩展。
这个消息的结构是:
opaque ASN.1Cert<1..2^24-1>;
struct {
ASN.1Cert certificate_list<0..2^24-1>;
} Certificate;
certificate_list
这是一个证书序列(链)。发送者的证书必须在列表的第一个位置。每个随后的证 书必须直接证明它前面的证书。 因为证书验证需要被独立发布的根密钥, 确定了 根证书权威的自签名证书可以被在链中忽略, 如果远程终端已经拥有了它以便在 任何情况下来验证它。
相同的消息类型和结果将用于client端对一个证书请求消息的响应. 需要注意的是一个client可能不发送证书, 如果它没有合适的证书来发送以响应server的认证请求.
注: PKCS #7不会被用做证书向量的格式因为PKCS #6扩展的证书没有被使用. 同样, PKCS #7定义了一个集合而非一个序列, 使得解析列表的任务变得更难完成.
如下的规则会被应用于server发送的证书:
- 证书类型必须是X.509v3, 除非显式协商了其它的类型(如, [TLSPGP]).
- 终端实体证书的公钥(和相关的限制)必须与选择的密钥交互算法兼容.
密钥交换算法. 证书类型
RSA RSA公钥; 证书必须允许密钥用于加密(keyEncipherment位必须被设置, 如果密钥使用扩展存在的话).
RSA_PSK 注:RSA_PSK 定义于[TLSPSK]
DHE_RSA RSA公钥;证书必须允许密钥使用server密钥交互消息中的签名机制和hash算法进行签名
ECDHE_RSA (如果密钥用法扩展存在的话,digitalSignature位必须设置)
注: ECDHE_RSA定义于[TLSECC].
DHE_DSS DSA公钥; 证书必须允许密钥使用server密钥交互消息中的签名机制和hash算法进行签名
DH_DSS Diffie-Hellman公钥; 如果密钥用法扩展存在的话,keyAgreement位必须设置
DH_RSA
ECDH_ECDSA ECDH-capable公钥; 公钥必须使用一个能够被client支持的曲线和点格式, 正如[TLSECC]中描述的那样
ECDH_RSA
ECDHE_ECDSA ECDSA-capable公钥; 证书必须允许密钥使用server密钥交互消息中的签名机制和hash算法进行签名;
公钥必须使用一个能够被client支持的曲线和点格式, 正如[TLSECC]中描述的那样
- "server_name"和"trusted_ca_keys"扩展[TLSEXT]被用于指导证书选择.
如果client提供了一个"signature_algorithms"扩展, 则所有由server提供的证书必须由出现在这个扩展中的一个hash/签名算法对进行签名. 需要注意的是这意味着一个包含了一个签名算法密钥的证书应该被一个不同的签名算法签名(例如, RSA密钥被DSA密钥签名). 这个与TLS 1.1不同, 后者要求算法是相同的. 需要注意的是这也意味着DH_DSS, DH_RSA, ECDH_ECDSA, 和ECDH_RSA密钥交换算法并不限制用于对证书签名的算法. 固定的DH证书可以被出现在扩展中的任意hash/签名算法对签名. DH_DSS, DH_RSA, ECDH_ECDSA, 和ECDH_RSA是历史上的名称.
如果server有多个证书, 它基于上述标准(此外其它的标准有:传输层端点, 本地配置和偏好等)选择其中一个.如果server只有一个证书, 它应该尝试使这个证书符合这些标准.
需要注意的是有很多证书使用无法与TLS兼容的算法或算法组合. 例如, 一个使用RSASSA-PSS签名密钥的证书(在SubjectPublicKeyInfo中是id-RSASSA-PSS OID)不能被使用因为TLS没有定义相应的签名算法.
正如密钥族指定了用于TLS协议的新的密钥交换方法, 它们也指定了证书格式和要求的编码的按键信息.
当这个消息即将被发送时:
这个消息会紧随在server证书消息之后发送(或erverHello消息, 如果是一个匿名协商的话);
ServerKeyExchange消息由server发送, 但仅在server证书消息(如果发送了)没有包含足够的数据以允许client交换一个预密钥时. 这个限制对于如下的密钥交换算法是成立的:
DHE_DSS
DHE_RSA
DH_anon
对于如下密钥交换算法发送ServerKeyExchange是非法的:
RSA
DH_DSS
DH_RSA
其它的密钥交换算法, 如在[TLSECC]中所定义的那些, 必须指定是否发送ServerKeyExchange; 如果消息发送了, 则必须指定发送内容.
这个消息的含义是:
这个消息传递了密码信息以允许client就预主密钥进行交互: 一个client用于完成一个密钥交换的Diffie-Hellman公钥(结果就是预主密钥)或一个其它算法的公钥
这个消息的结构是:
enum { dhe_dss, dhe_rsa, dh_anon, rsa, dh_dss, dh_rsa
/* 可以被扩展, 例如, 对于ECDH -- 见 [TLSECC] */
} KeyExchangeAlgorithm;
struct {
opaque dh_p<1..2^16-1>;
opaque dh_g<1..2^16-1>;
opaque dh_Ys<1..2^16-1>;
} ServerDHParams; /* 瞬时DH参数 */
dh_p
用于Diffie-Hellman操作的素模数
dh_g
用于Diffie-Hellman操作的生成器
dh_Ys
Server的Diffie-Hellman公开数值 (g^X mod p)
struct {
select (KeyExchangeAlgorithm) {
case dh_anon:
ServerDHParams params;
case dhe_dss:
case dhe_rsa:
ServerDHParams params;
digitally-signed struct {
opaque client_random[32];
opaque server_random[32];
ServerDHParams params;
} signed_params;
case rsa:
case dh_dss:
case dh_rsa:
struct {} ;
/* 消息忽略rsa, dh_dss, 和dh_rsa */
/* 可以被扩展, 例如, 对于ECDH -- 见 [TLSECC] */
};
} ServerKeyExchange;
params
Server密钥交互参数
signed_params
对于非匿名密钥交换, 这是一个对server密钥交互参数的签名
如果client已经提供了"signature_algorithms"扩展, 签名算法和hash算法必须成对出现在扩展中. 需要注意的是这里可能会有不一致的可能, 例如, client可能提供DHE_DSS密钥交换但在"signature_algorithms"扩展中忽略任何与DSA配对的组合.为了达成正确的协商, server必须在选择密码族之前检查任何"signature_algorithms"扩展之外的候选密码族. 这在某种程度上并不优雅, 但却是一个折中方案, 用以最小化对原始密码族设计的修改.
此外, hash和签名算法必须与位于server的终端实体证书中的密钥相兼容. RSA密钥可以与任何允许的hash算法配合使用, 并服从任何证书的约束(如果有的话).
由于DSA签名不包含任何安全的表明hash算法类型的方式, 如果多个hash可以与任何密钥共同使用时会存在hash被替换的风险(译者注: 不理解). 当前, DSA[DSS]可以只与SAH-1一起使用. 将来修订版的DSS [DSS-3]可以预期允许DSA与使用其它的摘要算法一起使用, 正如摘要算法应对与每个密钥的大小进行配合的指导原则一样. 此外, 将来修订版的[PKIX]可以指定证书机制以表明哪些摘要算法将与DSA一起使用.
作为额外为TLS定义的包含新的密钥交换算法的密码族, server密钥交换消息会被发送, 当且仅当与密钥交互算法关联的证书类型没有提供足够的信息供client交换一个预主密钥.
当这个消息即将被发送时:
一个非匿名的server能有选择地请求一个client发送的证书, 如果选定的密码族合适的话. 这个消息, 如果发送了, 将紧随在ServerKeyExchange消息之后(如果发送了; 否则, 这个消息跟随在server的证书消息之后).
这个消息的结构是:
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
client可以提供的证书类型的列表.
rsa_sign 一个包含RSA密钥的证书
dss_sign 一个包含DSA密钥的证书
rsa_fixed_dh 一个包含静态DH密钥的证书
dss_fixed_dh 一个包含静态DH密钥的证书
supported_signature_algorithms
一个hash/签名算法对列表供server选择, 按照偏好降序排列
certificate_authorities
可接受的certificate_authorities著名名称[X501]的列表, 以DER编码的格式体现. 这些著名的名称可以为一个根CA或一个次级CA指定一个期望的著名名称; 因此, 这个消息可以被用于描已知的根述和期望的认证空间. 如果certificate_authorities列表为空, 则client可以发送ClientCertificateType中的任意证书, 除非有一些属于相反情况的外部设定.
certificate_types和supported_signature_algorithms域的交互关系某种程度上有些复杂, certificate_type自从SSLv3开始就在TLS中存在, 但某种程度上并不规范. 它的很多功能被supported_signature_algorithms所取代. 应用下述规则:
- Client提供的任何证书必须使用在supported_signature_algorithms中存在的hash/签名算法对来签名
- Clinet提供的终端实体的证书必须包含一个与certificate_types兼容的密钥. 如果这个密钥是一个签名密钥, 它必须能与supported_signature_algorithms中的一些hash/签名算法对一起使用
- 出于历史原因, 一些client证书类型的名称包含了签名证书的算法. 例如, 在早期版本的TLS中, rsa_fixed_dh意味着一个用RSA和包含一个静态DH密钥签名的证书. 在TLS 1.2中, 这个功能被supported_signature_algorithms废除, 证书类型不再限制签名证书的算法. 例如, 如果server发送了dss_fixed_dh证书类型和{{sha1, dsa}, {sha1, rsa}}签名类型, client可以回复一个包含一个静态DH密钥的证书, 用RSA-SHA1签名
新的ClientCertificateType值由INAN指定, 在12节中描述.
注: 被列为保留类型的值不能被使用. 它们在SSLv3中使用.
注: 一个匿名server请求认证client会产生一个致命的handshake_failure警告.
当这个消息将被发送时:
ServerHelloDone消息已经被server发送以表明ServerHello及其相关消息的结束. 发送这个消息之后, server将会等待从client发送的响应.
这个消息的含义:
这个消息意味着server发送完支持密钥交换的消息, client能继续它的密钥交换处理.
在收到ServerHelloDone消息之后, client应当验证server提供的是否是有效的证书, 如果有要求的话, 检查server hello参数是否可以接受.
这个消息的结构:
struct { } ServerHelloDone;
当这个消息将被发送时:
这是client在收到一个ServerHelloDone消息后发送的第一个消息. 这个消息只能在server请求一个证书时发送. 如果没有合适的证书, client必须发送一个不带证书的证书消息. 即, certificate_list结构体的长度是0. 如果client不发送任何证书, server可以自行决定或者继续握手不验证client, 或者回复一个致命handshake_failure警报. 而且, 如果证书链某些方面不能接受(例如, 它没有被一个知名的可信CA签名), server可以自行决定继续握手(考虑到client无认证)或发送一个致命警报.
Client证书使用7.4.2节中定义的Certificate结构发送.
这个消息的含义:
这个消息传递client的证书链给server; 当验证CertificateVerify消息时(当client的验证基于签名时)server会用它来验证或计算预主密钥(对于非瞬时Diffie-Hellman). 证书必须适用于已协商的密码族的密钥交换算法, 和任何已协商的扩展.
尤其是:
- 证书类型必须是X.509v3, 除非显示协商其它类型(例如, [TLSPGP]).
- 终端实体证书的公钥(和相关的限制)应该与列在CertificateRequest中的证书类型兼容:
Client Cert. Type 证书密钥类型
rsa_sign RSA公钥;证书必须允许这个密钥被用于签名, 并与签名方案和hash算法一起被用于证书验证消息.
dss_sign DSA公钥;证书必须允许这个密钥被用于签名, 并与hash算法一起被用于证书验证消息.
ecdsa_sign 能胜任ECDSA的公钥;证书必须允许这个密钥被用于签名, 并与hash算法一起被用于证书验证消息; 这个公钥必须使用一个曲线和server支持的点的格式.
rsa_fixed_dh Diffie-Hellman公钥; 必须使用与server的密钥相同的参数
dss_fixed_dh
- 如果列出在证书请求中的certificate_authorities非空, 证书链中的一个证书应该被一个列出的CA签发.
- 证书必须被一个可接受的hash/签名算法对签名, 正如7.4.4节描述的那样. 需要注意的是这放宽了在以前的TLS版本中对证书签名算法的限制.
需要注意的是, 与server证书一样, 有一些证书使用了当前不能用于TLS的算法/算法组合.
当这个消息即将被发送时:
这个消息一直由client发送. 如果被发送的话, 它必须紧随着client证书消息. 否则, 它必须是在client收到ServerHelloDone后发送的第一个消息.
这个消息的含义:
在这个消息中设置了预主密钥, 这个或者通过RSA加密后直接传输, 或者通过传输Diffie-Hellman参数来允许双方协商出一致的预主密钥.
当client使用一个瞬时ephemeral指数时, 这个消息就会包含client的Diffie-Hellman公开数. 如果client正在发送一个包含一个静态DH指数(例如, 它正在进行fixed_dh client认证)的证书时, 这个消息必须被发送但必须为空.
这个消息的结构:
这个消息的选项依赖于选择了哪种密钥交互方法. 见7.4.3节关于KeyExchangeAlgorithm的定义.
struct {
select (KeyExchangeAlgorithm) {
case rsa:
EncryptedPreMasterSecret;
case dhe_dss:
case dhe_rsa:
case dh_dss:
case dh_rsa:
case dh_anon:
ClientDiffieHellmanPublic;
} exchange_keys;
} ClientKeyExchange;
7.4.7.1. RSA加密的预主密钥消息
如果RSA被用于密钥协商和认证, client会生成一个48字节的预主密钥,使用server证书中的公钥加密,然后以一个加密的预主密钥消息发送. 这个结构体是ClientKeyExchange消息的一个变量, 它本身并非一个消息.
这个消息的结构是:
struct {
ProtocolVersion client_version;
opaque random[46];
} PreMasterSecret;
client_version
client所支持的最新版本. 这用于探测版本号以抑制攻击.
random
46字节以安全方式产生的随机字节.
struct {
public-key-encrypted PreMasterSecret pre_master_secret;
} EncryptedPreMasterSecret;
pre_master_secret
这个随机数由client产生,且用于生成族密钥, 如8.1节所述.
注意:PreMasterSecret中的版本号是由client在ClientHello.client_version中提供的, 并不是为这个连接协商的版本号. 这特性被用来防止回滚攻击. 不幸的是, 一些旧的实现使用了协商的版本, 因此检查版本号会导致与这些不正确的clieng实现之间的互操作失败.
Client实现必须一直在PreMasterSecret中发送正确的版本号. 如果ClientHello.client_version是TLS 1.1或更高, server实现必须按照以下的描述检查版本号. 如果版本号是TLS 1.0或更早, server实现应该检查版本号, 但可以有一个可配置的选项来禁止这个检查. 需要注意的是如果检查失败, PreMasterSecret应该按照以下的描述将PreMasterSecret随机化.
注: 由Bleichenbacher [BLEI]和Klima et al.[KPR03]发现的攻击能被用于攻击TLS server, 这表明一个特定的消息在解密时,是否已经被格式化为PKCS#1, 包含一个有效的PreMasterSecret结构,或有正确的版本号.
正如Klima [KPR03]所描述的, 这些弱点能够被避免, 通过不区别对待格式不正确的消息块和/或不匹配的版本号和格式正确的RSA块. 换句话说:
1. 生成一个46字节随机字符串R;
2. 解密这消息来恢复明文M;
3. 如果PKCS#1填充不正确,或消息M的长度不是精确的48字节:
pre_master_secret = ClientHello.client_version || R
否则,如果ClientHello.client_version <= TLS 1.0, 且版本号检查被显示禁用:
pre_master_secret = M
否则:
pre_master_secret = ClientHello.client_version || M[2..47]
需要注意的是使用ClientHello.client_version显式构造pre_master_secret会产生一个无效的master_secret, 如果client在原始的pre_master_secret中发生了错误的版本的话。
另外一个可供选择的方法是讲版本号不匹配作为一个PKCS-1格式错误来处理, 并将预主米有完全随机化:
1. 生成一个46字节随机字符串R;
2. 解密这消息来恢复明文M;
3. 如果PKCS#1填充不正确,或消息M的长度不是精确的48字节:
pre_master_secret = R
否则,如果ClientHello.client_version <= TLS 1.0, 且版本号检查被显示禁用:
pre_master_secret = M
否则,如果M的前两个字节不等于ClientHello.client_version:
premaster secret = R
否则:
pre_master_secret = M
虽然没有已知的针对这个结构体的攻击, Klima et al. [KPR03]描述了一些理论上的攻击, 因此推荐第一种结构描述。
在任何情况下,一个TLS Server一定不能产生一个警报,如果处理一个RSA加密的预密钥消息失败,或版本号不是期望的。作为替代,它必须以一个随机生成的预主密钥即系握手流程。出于定位问题的意图将失败的真正原因记录在日志中可能是有帮助的。但必须注意避免泄露信息给攻击者(例如,计时,日志文件或其它渠道)
在[PKCS1]中定义的RSAES-OAEP加密方案对于Bleichenbacher攻击是更安全的。然而,为了最大程度上兼容早期的TLS版本,本规范使用RSAES-PKCS1-v1_5方案。如果上述建议被采纳的话,不会有多少已知的Bleichenbacher能够奏效。
实现注记:公钥加密数据被表现为一个非透明向量<0..2^16-1> (见4.7节)。因此,一个ClientKeyExchange消息中的RSA加密的预主密钥以两个长度字节为先导。 这些字节对于RSA是冗余的因为EncryptedPreMasterSecret是ClientKeyExchange中仅有的数据,它的长度会明确地确定。SSLv3规范对公钥加密数据的编码没有明确指定,因此很多SSLv3实现没有包含长度字节 -- 它们将RSA加密数据直接编码进ClientKeyExchange消息中。
本规范要求EncryptedPreMasterSecret和长度字节一起正确地编码。结果PDU会与很多SSLv3实现不兼容。实现者从SSLv3升级时必须修改他们的实现以生成和接受正确的编码。希望兼容SSLv3和TLS的实现者必须使他们的实现的行为依赖于版本号。
实现注记:现在得知对TLS进行基于计时的攻击是可能的,至少当client和server在相同局域网中时是可行的。相应地,使用静态RSA密钥的实现必须使用RSA混乱或其它抗计时攻击技术,如[TIMING]所述。
7.4.7.2. Client Diffie-Hellman Public Value
这个消息的含义是:
这个结构体传递了client的Diffie-Hellman公开值(Yc)如果这个值没有被包含在clietn的证书中。Yc所用的编码由PublicValueEncoding所列举。这个结构是client密钥交换消息的一个变量,它本身并非一个消息。
这个消息的结构是:
enum { implicit, explicit } PublicValueEncoding;
implicit
如果client发送了一个证书其中包含了一个合适的Diffie-Hellman密钥(用于fixed_dh类型的client认证),则Yc是隐式的且不需要再次发送。这种情况下,client密钥交换消息会被发送,单必须是空。
explicit
Yc需要被发送。
struct {
select (PublicValueEncoding) {
case implicit: struct { };
case explicit: opaque dh_Yc<1..2^16-1>;
} dh_public;
} ClientDiffieHellmanPublic;
dh_Yc
client的Diffie-Hellman公开值(Yc)
当这个消息将被发送时:
这个消息用于对一个client的证书进行显式验证。这个消息只能在一个client证书具有签名能力时才能发送(例如,所有除了包含固定Diffie-Hellman参数的证书)。当发送时,它必须紧随着client密钥交换消息。
这消息的结构是:
struct {
digitally-signed struct {
opaque handshake_messages[handshake_messages_length];
}
} CertificateVerify;
这里handshake_messages是指发送或接收到的所有握手消息,从client hello开始到但不包括本消息,包含握手消息的类型和长度域。这是到目前为止所有握手结构(如在7.4节中定义的那样)的关联。需要注意的是这要求两端要么缓存消息,要么计算用所有可用的hash算法计算运行时hash值直到计算CertificateVerify的hash值为止。Server可以通过在CertificateRequest消息中提高一个受限的摘要算法及来最小化这种计算代价。
在签名中使用的hash和签名算法必须是CertificateRequest消息中supported_signature_algorithms域所列出的算法中的一种。此外,hash和签名算法必须与client的终端实体证书相兼容。RSA密钥可以与任何允许的hash算法一起使用,但需要服从证书中的限制,如果有的话。
由于DSA签名不包含任何安全表明hash算法的方法,如果任意密钥使用多个hash的话会产生一个hash替代风险。目前DSA[DSS]可以与SHA-1一起使用。将来版本的DSS[DSS-3]被希望允许与DSA一起使用其它的摘要算法,
正如说明摘要算法应该使用的密钥大小的指导一样。此外,将来版本的[PKIX]可以指定机制以允许证书表明哪些摘要算法能与DSA一起使用。
当这个消息即将被发送时:
一个结束消息一直会在一个change cipher spec消息后立即发送,以证明密钥交换和认证过程是成功的。一个change cipher spec消息必须在其它握手消息和结束消息之间被接收。
这个消息的含义:
结束消息是第一个被刚刚协商的算法,密钥和机密保护的消息。结束消息的接收者必须验证内容是正确的。一旦一方已经发送了结束消息且接收并验证了对端发送的结束消息,它必须在连接上开始发送和接收应用数据。
这个消息的结构:
struct {
opaque verify_data[verify_data_length];
} Finished;
verify_data
PRF(master_secret, finished_label, Hash(handshake_messages))
[0..verify_data_length-1];
finished_label
对于由client发送的结束消息,字符串是"client finished". 对于由server发送的结束消息,字符串是"server finished".
Hash指出了握手消息的一个hash。对于在第5节定义的PRF,hash必须用作PRF的基础。任何定义了一个不同PRF的密码族必须定义Hash用于结束消息的计算。
在TLS之前的版本中,verify_data一直是12字节长。在当前的TLS版本中,她取决于密码族。任何没有显式指定verify_data_length的密码族都有一个verify_data_length等于12.这包括了所有现有的密码族。需要注意的是这种表示的编码与之前的版本相同。将来密码族可能会指定其它长度但这个长度必须至少是12字节。
handshake_messages
所有在本次握手过程(不包括任何HelloRequest消息)到但不包括本消息的消息中的数据。这是只能在握手层中看见的数据且不包含记录层头。这是到目前为止所有在7.4节中定义的握手结构体的关联。
如果一个结束消息在握手的合适环节上没有一个ChangeCipherSpec在其之前则是致命错误。
handshake_messages的值包括了从ClientHello开始一直到(但不包括)结束消息的所有握手消息。这与7.4.8节中的handshake_messages不同,因为它包含CertificateVerify消息(如果发送了)。同样,client发送的结束消息的handshake_messages与server发送的结束消息不同,因为第二个被发送的要包含前一个。
注意:ChangeCipherSpec消息,警报,和任何其它记录类型不是握手消息,不会被包含在hash计算中。同样,HelloRequest消息也被握手hash忽略。
为了开始连接保护,TLS记录协议要求指定一个算法族,一个主密钥和client及server端随机数。认证,加密和消息认证码算法由cipher_suite确定,cipher_suite是由server选定并在ServerHello消息中表明出来。压缩算法在hello消息这协商出来,随机数也在hello消息中交换。所有这些都用于计算主密钥。
对于所有的密钥交换算法,相同的算法都会被用来将pre_master_secret转化为master_secret。一旦master_secret计算完毕,pre_master_secret就应当从内存中删除。
master_secret = PRF(pre_master_secret, "master secret",
ClientHello.random + ServerHello.random)
[0..47];
主密钥的长度一直是48字节。预密钥的长度根据密钥交换算法而变。
当RSA被用于身份认证和密钥交换时,client会产生一个48字节的pre_master_secret,用server的公钥加密,然后发送给server。Server用它自己的私钥解密pre_master_secret。然后双方按照前述方法将pre_master_secret转换为master_secret。
一个传统的Diffie-Hellman计算需要被执行。协商出来的密钥(Z)会被用做pre_master_secret, 并按照前述方法将其转换为master_secret。在被用做pre_master_secret之前,Z开头所有的0位都会被压缩。
注:Diffie-Hellman参数由server指定,可能是临时的或包含在server的证书中。
在没有指定一个应用配置标准时,一个TLS兼容的应用必须实现密码套件TLS_RSA_WITH_AES_128_CBC_SHA (定义见附录A.5).
应用数据消息由记录层承载和分片、压缩、并基于当前连接状态进行加密。这些消息被记录层当做透明数据处理。
安全问题的讨论贯穿本备忘录的全文,尤其是附录D, E和F。
本文档使用了几个在[TLS1.1]中原创的Registry。 IANA更新了这些参考稳定。这些Registry和它们的分配策略(照搬自[TLS1.1])列在下面。
- TLS ClientCertificateType标识符Registry: 在0-63(十进制)范围内(包含0和63)的将来的数值由标准行为[RFC2434]来指定。在64-223(十进制)范围内(包含64和223)的数值由规范需求[RFC2434]来指定。在224-255(十进制)范围内(包含224和255)的数值保留以备私用[RFC2434]。
- TLS密码套件Registry: 在0-191(十进制)范围内(包含0和191)的将来的数值连同第一个字节由标准行为[RFC2434]来指定。在192-254(十进制)范围内的数值连同第一个字节由规范需求[RFC2434]来指定。数值255连同第一个字节保留以备私用[RFC2434]。
- 本文定义了几个新的基于HMAC-SHA256的密码套件,它们的数据(在附录A.5中)已经从TLS密码套件registry中分配。
- TLS ContentType Registry: 将来的数值由标准行为[RFC2434]来分配。
- TLS警报Registry: 将来的数值由标准行为[RFC2434]来分配。
- TLS HandshakeType Registry: 将来的数值由标准行为[RFC2434]来分配。
本文档也使用了几个在[RFC4366]中原创的Registry。 IANA更新了这些参考稳定。这些Registry和它们的分配策略(照搬自[RFC4366])列在下面。
- TLS ExtensionType Registry: 将来的值由IETF一致分配[RFC2434]. IANA已经更新了这个registry以包含signature_algorithms扩展和它对应的值(见 7.4.1.4节).
此外,本文还定义了两种新的registry并由IANA维护:
- TLS SignatureAlgorithm Registry: 这个registry已经初始化为在7.4.1.4.1节中描述的值. 在0-63(十进制)范围内(包含0和63)的将来的数值由标准行为[RFC2434]来指定。在64-223(十进制)范围内(包含64和223)的数值由规范需求[RFC2434]来指定。在224-255(十进制)范围内(包含224和255)的数值保留以备私用[RFC2434]。
- TLS HashAlgorithm Registry: 这个registry已经初始化为在7.4.1.4.1节中描述的值. 在0-63(十进制)范围内(包含0和63)的将来的数值由标准行为[RFC2434]来指定。在64-223(十进制)范围内(包含64和223)的数值由规范需求[RFC2434]来指定。在224-255(十进制)范围内(包含224和255)的数值保留以备私用[RFC2434]。
本文也使用了TLS压缩方法标识符Registry, 在[RFC3749]中定义. IANA已经分配了值0给"null"压缩方法。
本节描述了协议类型和常量。
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;
struct {
enum { change_cipher_spec(1), (255) } type;
} ChangeCipherSpec;
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;
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;
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>;
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] */
} 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] */
} 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;
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;
struct {
opaque verify_data[verify_data_length];
} Finished;
下面的值定义了ClientHello和ServerHello消息所使用的密码套件码。
一个密码套件定义了在TLS 1.2版本中支持的一个密码规范。
TLS_NULL_WITH_NULL_NULL 是指定的且是一个TLS连接在通道中初次握手过程中的初始状态,但不能被协商,它也不能提供比无安全的连接更多的保护。
CipherSuite TLS_NULL_WITH_NULL_NULL = { 0x00,0x00 };
下面的CipherSuite的定义要求server提供一个RSA证书用于密钥交换。Server可以在证书请求消息中请求任何可签名的证书。
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 };
下面的密码套件定义是用于server认证的(也可以是client认证的)Diffie-Hellman。DH代表着一类密码套件,其server证书包含了由证书权威(CA)签名的Diffie-Hellman参数。DHE代表着瞬时 Diffie-Hellman, 这里Diffie-Hellman参数由一个可签名的证书签名来签名,这个证书由CA签发。Server使用的签名算法由CipherSuite名称中的DHE组件后面的名称来指定。Server能够从client请求任何可签名的证书用于验证client,它也可以请求一个Diffie-Hellman证书。任何由client提供的Diffie-Hellman证书必须使用由server描述的参数(组或生成者)。
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 };
下面的密码套件用于完全匿名的Diffie-Hellman通信,其中的任何一方都没有验证。需要注意的是这种模式无法防御中间人攻击。因此使用这种模式是受限使用的:这些密码套件不能被TLS 1.2的实现所使用,除非应用层指定要求使用匿名密钥交换。(匿名密钥交换有时也是可接受的,例如:在没有验证建立的情况下支持投机的加密, 或TLS作为更复杂的安全协议的一部分来使用,有其它手段来确保验证)
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 };
需要注意的是使用非匿名密钥交换且没有实际的密钥交换验证在实质上等同于匿名密钥交换,之前的告警同样适用。当非匿名密钥交换总体上包含了比匿名密钥交换更高的计算和商业成本时, 且应用层允许匿名密钥交换,出于互操作的目的不禁用非匿名密钥交换。
新的密码套件值已经由IANA指定并在12节中描述。
注: 密码套件值 { 0x00, 0x1C }和{ 0x00, 0x1D } 被保留以避免与SSL 3中Fortezza-based密码套件冲突。
这些安全参数由TLS握手协议决定且作为参数提供给TLS记录层以初始化一个连接的状态。SecurityParameters包括:
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;
/* 在CompressionMethod, PRFAlgorithm, BulkCipherAlgorithm, 和
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;
RFC 4492 [TLSECC]将椭圆曲线密码套件添加到TLS中。本文改变了在那个文档中使用的一下结构体。本节详细说明要求RFC 4492和TLS 1.2的实现者做出的改变。TLS 1.2的实现者但不实现RFC 4492的不需要阅读本节。
本文添加了一个"signature_algorithm"域到数字签名的元素中以便标示出生成一个签名所用的签名和摘要算法。这个改变应用于数字签名,也适用于ECDSA签名,因此允许ECDSA签名与除SHA1外的摘要算法一起使用,提供这种用法可以兼容证书和[PKIX]的将来的修改所施加的任何限制.
正如7.4.2节和7.4.6节描述的那样, 对签发证书所用的签名算法的限制不再与密码套件(被server所使用的)或ClientCertificateType(被client所使用的)所限定。因此,对RFC 4492的第2和第3节中所指定的对签发证书的签名算法的限制也同样解除。在本文中,对终端实体证书中密钥的限制仍然不变。
Advanced Encryption Standard (AES) —— 高级加密标准
AES [AES]是一个广泛使用的对称加密算法。AES是一个块加密算法,密钥是128、192、256 bit,块大小是16字节。TLS当前支持128和256 bit的密钥长度。
application protocol —— 应用层协议
一个应用层协议是一个直接位于传输层(例如, TCP/IP)之上的协议。例子包括HTTP, TELNET, FTP, 和SMTP.
asymmetric cipher —— 非对称密码
见公钥密码学。
authenticated encryption with additional data (AEAD) —— 带额外数据的认证加密
一种同时提供机密性和消息完整性的对称加密算法。
authentication —— 认证
认证是一种一个实体确认另一个实体的身份的能力。
block cipher —— 分组密码
一个分组密码是一种算法,以bit组(称为块)的形式操作明文。64 bit曾经是,128 bit现在是一个通常的块大小。
bulk cipher —— 块密码
一个用于加密大量数据的对称加密算法。
cipher block chaining (CBC) —— 密码分组链
CBC是一个模式, 在这个模式中每个用一个密文块加密的明文块都首先与前一个密文块做异或运算(或者,对于第一个块,与初始向量)。对于解密,每个块都是先解密,然后再与之前的密文块(或向量)做异或运算。
certificate —— 证书
做为X.509协议(也被称为ISO认证框架)的一部分, 证书由一个可信的证书权威指定并在一方的身份或一些其它属性和它的公钥之间提供一个强绑定。
client —— 客户端
与server发起一个TLS连接的应用实体。这也许可能(也许不可能)意味着client发起了基础的传输连接。在操作上client与server的主要差别是server通常是被验证的,而client只是选择性验证。
client write key —— 客户端写密钥
用于加密由client所写数据的密钥。
client write MAC key —— 客户端写消息认证码密钥
用于认证由client所写数据的密钥。
connection —— 连接
一个连接是一个提供一直合适类型服务的传输层(在OSI分层模型中定义)。对于TLS,这样的连接是对等关系。这样的连接是临时的。每个连接都与一个会话相关联。
Data Encryption Standard —— 数据加密标准
DES [DES]仍然是一个被广泛使用的对称加密算法,虽然它限制被认为相当弱。DES是一个块加密算,密钥是56位,块是8字节。在TLS中,出于密钥生成的考虑,DES被作为8字节(64位)密钥长度来对待,但它仍然只提供56位保护。(每个密钥字节的最低位用于设置这个密钥字节的奇偶校验)DES也可以以一种模式[3DES]操作,在这种模式下对每块数据用3个独立密钥进行三次加密;这会使用168位(对于TLS密钥生成方法是24字节)密钥并提供等同于112位的安全。
Digital Signature Standard (DSS) —— 数字签名标准
一个数字签名的标准,包括数字签名算法,被美国国家标准与技术研究所(NIST)认可,定义于NIST FIPS PUB 186-2, "数字签名标准",由美国商务部于2000年1一月发布[DSS]。一个有意义的更新[DSS-3]已经被草案化并于2006年3月公布。
digital signatures —— 数字签名
数字签名使用公钥密码学和单向hash函数产生一个能够被认证的数据的签名,并且这个签名难于被伪造或否认。
handshake —— 握手
一个client和server之间的初始协商,建立它们之间事物的参数。
Initialization Vector (IV) —— 初始向量
当一个块加密算法使用CBC模式时,在加密之前初始向量会与第一个明文块进行异或运算。
Message Authentication Code (MAC) —— 消息认证码
一个消息认证码是一个从一个消息或一些机密数据计算得到的单向hash。它在不知道机密数据的情况下很难伪造。它用于检测消息是否被修改。
master secret —— 主密钥
用于产生加密密钥、MAC密钥和向量的安全机密数据。
MD5
MD5 [MD5]是一个散列函数,能将任意长的数据流转化为一个固定大小(16字节)的hash值。由于密码分析的显著进步,在本文发布的时候,MD5不再被认为是一个“安全的”散列函数。
public key cryptography —— 公钥密码学
使用两个密钥进行加解密的一类密码学技术。公钥加密的消息只能由相关的私钥解密。相反,由私钥签名的消息只能由公钥验证。
one-way hash function —— 单向hash函数
一个将任意数量的数据转换为一个固定长度hash的变换。在计算上很难反变换或找到碰撞。MD5和SHA就是单向hash函数的例子。
RC4
一个由Ron Rivest发明的流加密算法。一个兼容的密码算法的描述见[SCH]。
RSA
一个被非常广泛使用的公钥算法,能用于加密或数字签名[RSA]。
server —— 服务器
Server是一个应用实体,能对client的连接请求做出响应。参见"client"。
session —— 会话
一个TLS会话是一个在一个client和一个server之间的联系。会话由握手协议创建。会话定义了一套能在多个连接中共享的密码学安全参数。会话被用于避免每个连接为协商新的安全参数所产生的代价。
session identifier —— 会话标识符
一个会话标识符是一个由server产生的值,能标识一个特定的会话。
server write key —— server写密钥
用于加密由server所写数据的密钥。
server write MAC key —— server写消息认证码密钥
用于认证由server所写数据的密钥。
SHA
安全hash算法(SHA)[SHS]在FIPS PUB 180-2中定义。它产生一个20字节的输出。需要注意的是所有对SHA(无数字后缀)的引用实际上使用的是修改后的SHA-1算法。
SHA-256
256位的安全hash算法(SHA)由FIPS PUB 180-2定义。它产生一个32字节的输出。
SSL
网景的安全套接层协议[SSL3]。TLS基于SSL的3.0版本。
stream cipher —— 流密码
一个加密算法,它可以将一个密钥转换为一个密码学上的强密钥流,然后用这个密钥流与明文异或。
symmetric cipher —— 对称密码
见块密码。
Transport Layer Security (TLS) —— 传输层安全
这是个协议,也是因特网工程任务族中的传输层安全工作租。见本文末尾的“工作组信息”(见99页)。
密码套件 密钥交换 加解密 消息认证码
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
密码算法 类型 密钥长度 IV大小 块大小
----------- ------ -------- ---- -----
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 算法 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
类型
表明是否是一个流加密算法或是一个在CBC模式下运行的块加密算法。
密钥长度
用于产生写密钥的密钥块的字节数。
IV大小
用于产生初始化向量的数据的数量。流加密算法是0;块加密算法等于块大小(等于SecurityParameters.record_iv_length)。
块大小
一个加密算法一次处理的数据的数量;一个块加密算法在CBC模式下只能加密自身块大小偶数倍的数据。
TLS协议不能阻止很多常见的安全错误。这节会提供几个协助开发者的建议。
TLS需要一个密码学上安全的伪随机数生成器(PRNG)。在设计和种子化PRNG时必须小心。PRNG基于安全的hash操作,最显著的是SHA-1,是可接受的,但不能提供比随机数生成器状态的大小更多的安全。
为了估计所产生的种子材料的量,在每个种子的字节中添加了不可预测信息的位数。例如,从一台兼容18.2HZ定时器的PC中获取的按键计时数据提供了1或2个安全位,即使计数器数值的总大小是16位或更多。种子化一个128位的PRNG因此应该要求大约100个这样的定时器数值。
[RANDOM] 对于随机数值的生成提供了指导
实现负责验证证书的完整性,且通常应该支持证书吊销。证书应该一直被验证以确保它被一个可信的证书权威(CA)适当的签发。可信CA的选择和添加应该十分小心。用户应该能够观察到有关证书和根CA的信息。
TLS支持一个密钥大小和安全等级的范围,包括不提供安全或提供最少安全的等级。一个适当的实现可能不会支持很多密码套件。例如,匿名Diffie-Hellman强烈不推荐因为它不能阻止中间人攻击。应用应该强制最小和最大密钥大小。例如,包含了512位RSA密钥或签名的证书链不适用于高安全的应用。
实现的经验表面早期TLS特定部分的规范并不容易理解,并成为了安全和互操作问题的一个来源。其中很多领域会已经在本文中澄清,但本附录包含了一个大多数重要事情的短的列表,希望实现者特别关注。
TLS协议问题:
- 你是否能正确处理分片为多个TLS记录的握手消息(见6.2.1节)? 包括边缘实现(如:一个ClientHello被分为若干个小的分片)? 你会将握手消息分片超过最大分片大小限制吗?特别是证书和证书请求握手消息能足够大到要求分片。
- 你忽略了在ServerHello(见附录E.1)之前的所有消息中的TLS记录层版本号了吗?
- 你正确处理ClientHello中的TLS扩展了吗(包括完全忽略扩展域)?
- 由client或server发起重协商你都支持吗?当重协商是一个可选特性时,强烈建议支持它。
- 当server请求一个client的证书但没有合适的证书可用,你会正确地发送一个空的证书消息而不是忽略整个消息吗(见7.4.6)?
密码学细节:
- 对于RSA加密的预主密钥,你正确地发送和验证了版本号了吗?当遇到一个错误时,你是继续握手以避免Bleichenbacher攻击吗(见7.4.7.1)?
- 你采取了什么对抗措施来阻止对RSA解密和签名操作(见7.4.7.1)的计时攻击?
- 对于RSA加密的预主密钥,你正确地发送和验证了版本号了吗?当遇到一个错误时,你是继续握手以避免Bleichenbacher攻击吗(见7.4.7.1)?
- 你采取了什么对抗措施来阻止对RSA解密和签名操作(见7.4.7.1)的计时攻击?
- 当校验RSA签名时,空和缺少参数你都接受吗?(见4.7节)?你是否验证RSA的填充在hash值之后不包含任何额外数据?[FI06]
- 使用Diffie-Hellman密钥交换时,你会正确地剥离协商的密钥(见8.1.2节)第一个0字节吗?
- 你的TLS客户端是否检查server发送的Diffie-Hellman参数是可接受的吗?
- 你怎样为CBC模式的密码算法(见6.2.3.2)产生一个不可预测的向量呢?
- 你接受长CBC模式的填充Do you accept long CBC mode padding (上限255字节;见6.2.3.2节)吗?
- 你怎样防御CBC模式计时攻击(见6.2.3.2节)?
- 你是否使用了一个强壮的,更重要的,进行了适当种子化的随机数生成器(见Appendix D.1)来生成预主密钥(用于RSA密钥交换),Diffie-Hellman私有数据,DSA "k"参数,和其它的重要安全参数?
由于存在着很多版本的TLS(1.0, 1.1, 1.2, 和任何将来的版本)和SSL (2.0和3.0), 意味着需要协商所使用的具体协议版本号。TLS协议提供了一个内置机制用于版本协商,这样就不会为其它协议组件引入版本选择的复杂性。
TLS版本1.0, 1.1, 1.2, 和SSL 3.0都非常相似,而且都使用兼容的ClientHello消息; 因此, 全部支持这些版本相对容易。相似地,server能轻易的使client试图使用TLS将来的版本,只要ClientHello格式保持兼容,且client支持server提供的最高版本协议。
一个希望与这样的旧版本server进行协商的TLS 1.2 client应该发送一个正常的TLS 1.2 ClientHello,ClientHello.client_version中包含{ 3, 3 } (TLS 1.2)。如果server不支持这个版本,它会回应一个包含旧版本号的ServerHello。如果client同意使用这个版本,协商将会以适用于所协商协议的方式继续进行。
如果server选择的版本client并不支持(或不能接受),client必须发送一个“protocol_version”警报消息并关闭连接。
如果一个TLS server接收的ClientHello包含的版本号大于server所支持的最高版本,它必须按照server所支持的最高版本来回应。
一个TLS server也能接收一个包含的版本号小于最高支持版本的ClientHello。如果server希望与旧的client协商,它将继续以适用于server所支持的最高版本的方式继续,这个最高版本不大于ClientHello.client_version。例如,如果server支持TLS 1.0, 1.1, 和1.2,client_version是TLS 1.0,server将以一个TLS 1.0 ServerHello继续。如果server支持(或愿意使用)只比client_version大的版本,它必须发送一个“protocol_version”警报消息并关闭连接。
无论何时一个client已经得知一个server所这次和的最高协议版本(例如,当恢复一个会话时),它都应当在原始协议中初始化这个连接。
注: 一些server的实现已经被得知不正确地实现了版本协商。例如,有一些古怪的TLS 1.0 server在client提供的版本号比TLS 1.0新时会简单地关闭连接。同样,一些server会拒绝连接,如果ClientHello中包含任何TLS扩展的话。与这些古怪的server进行互操作是一个复杂的话题,超出了本文的范围,且可能会要求client尝试进行多次连接。
更早版本的TLS规范并没有是否明确记录层版本号(TLSPlaintext.version)在发送ClientHello时(例如, 在它知道应当采用什么协议版本之前)应该包含。因此,符合这个规范的TLS server必须接受任意值{03,XX}做为ClientHello的记录层版本号。
希望与旧版本server进行协商的TLS client应当发送任意值{03,XX}做为记录层版本号。典型的值可以是{03,00},是client支持的最低版本号,也是ClientHello.client_version的值。没有单个值能保证与所有旧版本server进行互操作,但这是一个复杂的话题,超出了本文的范围。
希望支持SSL 2.0的TLS 1.2 client必须发送在[SSL2]中定义的版本2的CLIENT-HELLO消息。这个消息必须包含与通用ClientHello消息中所用的相同的版本号,且必须在CIPHER-SPECS-DATA域中将所支持的TLS密码套件安装如下描述进行编码。
警告:发送2.0版本CLIENT-HELLO消息的能力会被尽快淘汰,因为更新的ClientHello格式提供了更好的机制用于向更新版本演进和协商扩展。TLS 1.2 client不应当支持SSL 2.0。然而,即使不支持SSL 2.0的TLS server也可以接收2.0版本的CLIENT-HELLO消息。这个消息呈现在下面,为TLS server实现者提供了足够的细节; 真实的定义仍然假定是[SSL2]。
为了进行协商,2.0 CLIENT-HELLO被理解为一个ClientHello携带一个空的压缩方法且没有扩展。需要注意的是这个消息必须直接在网线上发送,并不会包裹在一个TLS记录中。为了计算Finished和CertificateVerify, msg_length域不会被认为是握手消息的一部分。
uint8 V2CipherSpec[3];
struct {
uint16 msg_length;
uint8 msg_type;
Version version;
uint16 cipher_spec_length;
uint16 session_id_length;
uint16 challenge_length;
V2CipherSpec cipher_specs[V2ClientHello.cipher_spec_length];
opaque session_id[V2ClientHello.session_id_length];
opaque challenge[V2ClientHello.challenge_length;
} V2ClientHello;
msg_length
最高位必须是1;其余的位包含后续数据的字节长度。
msg_type
这个域结合版本号域, 标识了一个版本2的ClientHello消息。这个值必须是1.
version
等于ClientHello.client_version.
cipher_spec_length
这个域是cipher_specs域的总长度。它不能是0,且必须是V2CipherSpec长度的整数倍(3)。
session_id_length
这个域必须有一个0值用于一个client宣称支持TLS 1.2。
challenge_length
Client发送给server用于验证自己的挑战消息的字节长度。从历史上看,可能的值在16和32字节之间(包括16和32)。当使用后向兼容SSLv2的握手时client应该使用一个32字节的挑战。
cipher_specs
这是client愿意和能够使用的所有CipherSpecs的列表。 除了在[SSL2]中定义的2.0密码规范之外,还包含了通常在ClientHello.cipher_suites中发送的TLS密码套件,每个套件都以一个0字节作为前缀。例如,TLS密码族{0x00,0x0A}应该作为 {0x00,0x00,0x0A}.
session_id
这个域必须为空。
challenge
与ClientHello.random相对应。如果挑战长度小于32,TLS server应该以0字节填充到首部(注:无拖尾)以保证长度是32字节。
注记:恢复一个TLS会话的请求必须使用一个TLS client hello。
当TLS client回滚到版本2兼容模式时,它必须使用特殊的PKCS#1块格式。这样做会使得TLS server拒绝与具备TLS能力的client进行版本2的会话。
当一个客户端协商SSL 2.0但也支持TLS时,它必须将PKCS填充(不包括填充的结束符null)的右手(最低位字节)的、用于RSA加密CLIENT-MASTER-KEY域的ENCRYPTED-KEY-DATA的8个随机字节设置为0x03(其它的填充字节是随机的)。
当一个由TLS能力的server协商SSL 2.0时,它应该在解密ENCRYPTED-KEY-DATA后检查这8个填充字节是否是0x03。如果不是,server应该为ENCRYPTED-KEY-DATA产生一个随机值,然后继续握手(这会最终失败因为密钥不匹配)。需要注意的是将错误情况报告给client可能会是server易遭受[BLEI]中所描述的攻击。
TLS协议被设计用来在一个不安全的通信通道上为一个client和一个server之间建立一个安全连接。本文设置了几个传统的假设,包括攻击者拥有重要的计算资源,不能从协议之外的源获取机密信息。攻击者被假定有能力捕捉,修改,删除,重放,和其它方式篡改消息并在通信通道上发送。本附录概述了TLS怎样被设计来抵御各种攻击。
握手协议负责选择一个密码规范(译注:应该理解为密码参数)和产生一个主密钥,它们一起构成了与一个安全会话相关联的主密码学参数。握手协议也能选择拥有由可信证书权威签名的证书的认证方。
TLS支持三种认证模式:双向认证,对server认证不认证client,和全部不认证。无论何时server被认证,通道就能够安全地应对中间人攻击,但完全匿名的会话本质上还是容易遭受这类攻击。匿名server不能认证client。如果server被认证了,它的证书信息必须提供一个导向一个可接受的证书权威机构的有效证书链。相似地,已认证的client必须提供一个可接受的证书给server。每一方都需要负责认证对方证书是有效的且没有过期或被吊销。
密钥交换过程的总体目标是产生一个通信各方共知的预主密钥且不能为攻击者所知。预主密钥将被用于产生主密钥(见8.1节)。主密钥用于产生Finished消息,加密密钥,MAC密钥(见7.4.9和6.3节)。通过发送一个正确的Finished消息,各方就能由此证明他们知道正确的预主密钥。
F.1.1.1. 匿名密钥交换
完全匿名的会话可以使用Diffie-Hellman密钥交换来建立。Server端公钥参数包含在server密钥交换消息中,client的包含在client密钥交换消息中。不知道私钥值的窃听者不能找到Diffie-Hellman的结果(例如,预主密钥)。
警告:完全匿名连接只能提供对被动窃听的保护。除非有一个独立的防篡改通道能验证Finished消息没有被一个攻击者替换,server认证在一个存在活跃的中间人攻击的环境中是需要的。
F.1.1.2. RSA密钥交换和认证
使用RSA,密钥交换和sever认证可以合并。公钥包含在server的证书中。需要注意的是作为妥协产物的server端静态RSA证书会导致所有在静态密钥保护下的会话丧失机密性。想要完美前向安全的TLS用户应使用DHE密码套件。通过频繁更改一方的私钥(和证书)可以限制私钥泄露产生的破坏。
在认证了server的证书之后,client用server的公钥加密一个预主密钥。通过成功解码预主密钥和产生了一个正确的Finished消息,server表明它知道与server证书匹配的私钥。
当RSA被用于密钥交换时,client在证书认证消息(见7.4.8节)中被认证。Client签名一个从前面的所有握手消息中提取的值。这些握手消息包括server证书,它将签名与server绑定;包括ServerHello.random,它将签名与当前握手过程绑定。
F.1.1.3. Diffie-Hellman密钥交换和认证
当使用Diffie-Hellman密钥交换时,server可以提供一个包含固定Diffie-Hellman参数的证书,或使用server密钥交换消息发送一个由DSA或RSA证书签名的临时Diffie-Hellman参数集合。临时参数在签名前会与hello.random值一起做hash以确保攻击者不会重放旧参数。在这两种情况下,client能够验证证书或签名以确保参数属于server。
如果client有一个包含固定Diffie-Hellman参数的证书,它的证书会包含完成密钥交换所需的信息。需要注意的是在这种情况下client和server在每次通信时将产生相同的Diffie-Hellman结果(例如,pre_master_secret)。为了避免pre_master_secret在内存存在的时间过长,它应当尽快被转换为master_secret。Client Diffie-Hellman参数必须与server提供的相兼容以使密钥交换机制能够正常工作。
如果client有一个标准的DSA或RSA证书,或clietn未经认证,它在client密钥交换消息中发送了一个临时参数集给server,然后可以选择性使用一个证书验证消息认证自己。
如何相同的DH密钥对被用于多次握手,或者由于client或server有一个包含一个固定DH密钥对的证书,或者由于server重用DH密钥,都必须多加小心以阻止小子群攻击(small subgroup attacks)。实现上需要遵循[SUBGROUP]中的指导。
小子群攻击多可以通过使用一个DHE密码套件并生成一个新的DH私钥(X)给每个握手来避免。如果徐州了一个合适的基(比如:2),g^X mod p可以被快速计算;因此,性能开销被最小化了。此外,每个会话使用一个新的密钥提供了完美前向安全。实现上需要在使用DHE密码套件时为每个会话产生一个新的X。
由于TLS允许server提供任意的DH组,client应该验证DH组的大小是满足本地策略定义的。Client也应该验证DH公开指数的大小是适当的。[KEYSIZ]对于各种组大小的优点提供了有用的指导。Server可以选择通过提供一个知名组来协助client,正如在[IKEALG]或[MODP]中定义的一样。这些能够通过简单比较来验证。
由于TLS包含了对SSL版本2.0的重大改进,攻击者可以尝试将TLS client和server回退到版本2.0。这种攻击当(且仅当)TLS双方使用了SSL 2.0握手时才会发生。
虽然解决方案使用了非随机的PKCS #1块类型2消息填充并不优雅,它却为版本3.0的server提供了一个检测这种攻击的合理的安全方法。这个解决方案对于能够对密钥进行穷举攻击并在应用指定的等待门限过期之前用一个新的ENCRYPTED-KEY-DATA消息包含相同的密钥(但有正常的填充)进行替换的攻击者来说是不安全的。改变PKCS填充中最低有效的8字节并不影响协议中使用的签名hash长度和RSA密钥长度的安全性,因为这在本质上等同于将输入块大小增加了8字节。
一个攻击者可能会试图影响握手交互以是的握手双方选择与正常选择不同的加密算法。
对于这种攻击,一个攻击者必须动态地改变一个或多个握手消息。如果这样做了,client和server将会对握手消息计算出不同的hash值。结果是,通信双方将不会接受对端的Finished消息,这样攻击就会被发现。
当一个连接通过恢复一个会话来建立时,新的ClientHello.random和ServerHello.random值会与会话的主密钥一起做hash。在假定主密钥不能被破解且用于产生加密密钥和MAC密钥的安全hash操作的安全的条件下,连接应该是安全的并且有效地独立于之前的连接。攻击者在无法破解安全hash操作的条件下不能使用已知的加密密钥或MAC密钥破解主密钥。
会话不能被恢复除非client和server都同意。如果任何一方怀疑会话可能被入侵,或证书可能过期或被吊销,它应当强制进行完全的握手。建议会话ID的生命期上限为24小时,因为一个得到了一个主密钥的攻击者可能会冒充有弱点的通信方直到相应的会话ID过期。可能在相对不安全的环境中运行的应用不应当将会话ID写入到持久的存储中。
主密钥会与ClientHello.random和ServerHello.random一起做hash以便为每个连接产生独特的数据加密密钥和MAC密钥。
输出数据在传输之前使用一个MAC值进行保护。为了阻止重放或修改攻击,MAC值由MAC密钥,序列号,消息长度,消息内容和2个固定字符串计算得来。消息类型域是必要的以确保消息按预期被一个TLS记录层的client使用,不会被重定向为其它的消息。序列号确保了试图删除或重排序消息将会被探测到。因为序列号是64位,它不能溢出。来自一方的消息不能被插入到其它的输出中,因为他们使用独立的MAC密钥。相似地,server写和client写密钥也是独立的,这样流密钥只能被使用一次。
如果一个攻击者破解了一个加密密钥,所有由它加密的信息都能被读取。相似地,削弱一个MAC密钥会是的修改消息的攻击变得可能。因为MAC也是被加密的,消息修改攻击通常需要破解加密算法而不只是MAC。
注:MAC密钥可以比加密密钥长,这样即使加密密钥被破解,消息仍然能够防止篡改。
[CBCATT]描述了一个对TLS的选择明文攻击,这个攻击依赖于得知一个记录的向量(IV)。TLS以前的版本[TLS1.0]使用了之前记录中的CBC剩余数据作为IV而因此这种攻击能够发生。本版本使用了一个显式IV以抵御这种攻击。
TLS使用对称加密和在协商的密码套件中定义的认证函数来确保所传输的应用数据的安全。 目标是在网络中的活跃攻击者的而已行为下保护已传输数据的完整性和机密性。由此产生的结果是为了取得这个目标,加密和认证函数应用于数据的顺序扮演了一个重要的角色[ENCAUTH]。
最健壮的是被称为加密然后认证的方法,首先对数据进行加密然后对密文计算MAC。这种方法确保了使用任何加密和MAC函数对都能够获得完整性和机密性,如果前者对选择明文攻击是安全的且MAC对于选择消息攻击是安全的。TLS使用了另一种方法,称为认证然后加密,即先对明文计算MAC然后对明文和MAC的级联数据进行加密。这种方法对于确定的加密函数和MAC函数的组合是安全的, 但不能保证在通常的情况下是安全的。特别是,已经能够证明存在能够与任何安全MAC函数结合的完美的安全加密算(甚至在信息论意义上也是安全的),但却不能提供针对一个活跃攻击的保密性目标。因此,TLS采纳了新的密码套件和操作模式,它们需要在“认证然后加密”的模式下被分析以验证它们达成了规定的完整性和机密性的目标。
当前,“认证然后加密”的方法的安全性在一些重要的案例上已经得到了证明。其中一个案例是流密码,它的原理是用一个伪随机数生成器产生一个与消息的长度加上MAC标记的长度的不可预测的填充,然后再与明文和MAC标签进行异或;另一个案例是使用CBC模式的一个安全块密码。在这个案例中,如果能够做到一次将一个CBC加密传递给明文和MAC的级联,每对新的明文和MAC使用独立的、不可预测的IV,就是安全的。在TLS 1.1之前的版本,CBC模式的使用是很合理的,除了以之前密文的最后一块的形式使用了一个可预测IV。这使得LTS对选择明文攻击是开放的。本版本的协议对这些攻击免疫。关于加密模式安全性证明的精确细节, 见[ENCAUTH]。
TLS易受大量的拒绝服务(DoS)攻击的影响。尤其是一个攻击者发起了大量的TCP连接能导致一个服务器消耗大量CPU用于RSA解密操作的情况。然而,由于TLS通常在TCP上运行,如果TCP协议栈使用了合适的TCP SYN随机化[SEQNUM]则攻击者很难隐藏其源头。
由于TLS运行在TCP之上,它也容易遭受对个体连接的大量DoS攻击。特别是,攻击者能伪造RST,因此能结束连接,或伪造部分TLS记录,因此会导致连接停滞。这些攻击通常不能由使用TCP的协议来防范,关心这类攻击的实现者和用户可以使用IPsec AH [AH]或ESP [ESP]。
由于TLS能够提供一个安全的连接,client和server的系统,密钥和应用必须是安全的。此外,实现上必须没有安全错误。系统只能与最弱的密钥交换方法和所支持的认证算法一样强壮,且只能使用值得信任的密码函数。使用短的公钥和匿名server应及其小心。在决定使用那个证书和证书权威时实现者和用户必须谨慎;一个不诚实的证书权威能进行极大的破坏。
[AES] National Institute of Standards and Technology,
"Specification for the Advanced Encryption Standard (AES)"
FIPS 197. November 26, 2001.
[3DES] National Institute of Standards and Technology,
"Recommendation for the Triple Data Encryption Algorithm
(TDEA) Block Cipher", NIST Special Publication 800-67, May
2004.
[DSS] NIST FIPS PUB 186-2, "Digital Signature Standard",
National Institute of Standards and Technology, U.S.
Department of Commerce, 2000.
[HMAC] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed-
Hashing for Message Authentication", RFC 2104, February
1997.
[MD5] Rivest, R., "The MD5 Message-Digest Algorithm", RFC 1321,
April 1992.
[PKCS1] Jonsson, J. and B. Kaliski, "Public-Key Cryptography
Standards (PKCS) #1: RSA Cryptography Specifications
Version 2.1", RFC 3447, February 2003.
[PKIX] Housley, R., Polk, W., Ford, W., and D. Solo, "Internet
X.509 Public Key Infrastructure Certificate and
Certificate Revocation List (CRL) Profile", RFC 3280,
April 2002.
[SCH] B. Schneier. "Applied Cryptography: Protocols, Algorithms,
and Source Code in C, 2nd ed.", Published by John Wiley &
Sons, Inc. 1996.
[SHS] NIST FIPS PUB 180-2, "Secure Hash Standard", National
Institute of Standards and Technology, U.S. Department of
Commerce, August 2002.
[REQ] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119, March 1997.
[RFC2434] Narten, T. and H. Alvestrand, "Guidelines for Writing an
IANA Considerations Section in RFCs", BCP 26, RFC 2434,
October 1998.
[X680] ITU-T Recommendation X.680 (2002) | ISO/IEC 8824-1:2002,
Information technology - Abstract Syntax Notation One
(ASN.1): Specification of basic notation.
[X690] ITU-T Recommendation X.690 (2002) | ISO/IEC 8825-1:2002,
Information technology - ASN.1 encoding Rules:
Specification of Basic Encoding Rules (BER), Canonical
Encoding Rules (CER) and Distinguished Encoding Rules
(DER).
[AEAD] McGrew, D., "An Interface and Algorithms for Authenticated
Encryption", RFC 5116, January 2008.
[AH] Kent, S., "IP Authentication Header", RFC 4302, December
2005.
[BLEI] Bleichenbacher D., "Chosen Ciphertext Attacks against
Protocols Based on RSA Encryption Standard PKCS #1" in
Advances in Cryptology -- CRYPTO'98, LNCS vol. 1462,
pages: 1-12, 1998.
[CBCATT] Moeller, B., "Security of CBC Ciphersuites in SSL/TLS:
Problems and Countermeasures",
http://www.openssl.org/~bodo/tls-cbc.txt.
[CBCTIME] Canvel, B., Hiltgen, A., Vaudenay, S., and M. Vuagnoux,
"Password Interception in a SSL/TLS Channel", Advances in
Cryptology -- CRYPTO 2003, LNCS vol. 2729, 2003.
[CCM] "NIST Special Publication 800-38C: The CCM Mode for
Authentication and Confidentiality",
http://csrc.nist.gov/publications/nistpubs/800-38C/
SP800-38C.pdf
[DES] National Institute of Standards and Technology, "Data
Encryption Standard (DES)", FIPS PUB 46-3, October 1999.
[DSS-3] NIST FIPS PUB 186-3 Draft, "Digital Signature Standard",
National Institute of Standards and Technology, U.S.
Department of Commerce, 2006.
[ECDSA] American National Standards Institute, "Public Key
Cryptography for the Financial Services Industry: The
Elliptic Curve Digital Signature Algorithm (ECDSA)", ANS
X9.62-2005, November 2005.
[ENCAUTH] Krawczyk, H., "The Order of Encryption and Authentication
for Protecting Communications (Or: How Secure is SSL?)",
Crypto 2001.
[ESP] Kent, S., "IP Encapsulating Security Payload (ESP)", RFC
4303, December 2005.
[FI06] Hal Finney, "Bleichenbacher's RSA signature forgery based
on implementation error", [email protected] mailing
list, 27 August 2006, http://www.imc.org/ietf-openpgp/
mail-archive/msg14307.html.
[GCM] Dworkin, M., NIST Special Publication 800-38D,
"Recommendation for Block Cipher Modes of Operation:
Galois/Counter Mode (GCM) and GMAC", November 2007.
[IKEALG] Schiller, J., "Cryptographic Algorithms for Use in the
Internet Key Exchange Version 2 (IKEv2)", RFC 4307,
December 2005.
[KEYSIZ] Orman, H. and P. Hoffman, "Determining Strengths For
Public Keys Used For Exchanging Symmetric Keys", BCP 86,
RFC 3766, April 2004.
[KPR03] Klima, V., Pokorny, O., Rosa, T., "Attacking RSA-based
Sessions in SSL/TLS", http://eprint.iacr.org/2003/052/,
March 2003.
[MODP] Kivinen, T. and M. Kojo, "More Modular Exponential (MODP)
Diffie-Hellman groups for Internet Key Exchange (IKE)",
RFC 3526, May 2003.
[PKCS6] RSA Laboratories, "PKCS #6: RSA Extended Certificate
Syntax Standard", version 1.5, November 1993.
[PKCS7] RSA Laboratories, "PKCS #7: RSA Cryptographic Message
Syntax Standard", version 1.5, November 1993.
[RANDOM] Eastlake, D., 3rd, Schiller, J., and S. Crocker,
"Randomness Requirements for Security", BCP 106, RFC 4086,
June 2005.
[RFC3749] Hollenbeck, S., "Transport Layer Security Protocol
Compression Methods", RFC 3749, May 2004.
[RFC4366] Blake-Wilson, S., Nystrom, M., Hopwood, D., Mikkelsen, J.,
and T. Wright, "Transport Layer Security (TLS)
Extensions", RFC 4366, April 2006.
[RSA] R. Rivest, A. Shamir, and L. M. Adleman, "A Method for
Obtaining Digital Signatures and Public-Key
Cryptosystems", Communications of the ACM, v. 21, n. 2,
Feb 1978, pp. 120-126.
[SEQNUM] Bellovin, S., "Defending Against Sequence Number Attacks",
RFC 1948, May 1996.
[SSL2] Hickman, Kipp, "The SSL Protocol", Netscape Communications
Corp., Feb 9, 1995.
[SSL3] A. Freier, P. Karlton, and P. Kocher, "The SSL 3.0
Protocol", Netscape Communications Corp., Nov 18, 1996.
[SUBGROUP] Zuccherato, R., "Methods for Avoiding the "Small-Subgroup"
Attacks on the Diffie-Hellman Key Agreement Method for
S/MIME", RFC 2785, March 2000.
[TCP] Postel, J., "Transmission Control Protocol", STD 7, RFC
793, September 1981.
[TIMING] Boneh, D., Brumley, D., "Remote timing attacks are
practical", USENIX Security Symposium 2003.
[TLSAES] Chown, P., "Advanced Encryption Standard (AES)
Ciphersuites for Transport Layer Security (TLS)", RFC
3268, June 2002.
[TLSECC] Blake-Wilson, S., Bolyard, N., Gupta, V., Hawk, C., and B.
Moeller, "Elliptic Curve Cryptography (ECC) Cipher Suites
for Transport Layer Security (TLS)", RFC 4492, May 2006.
[TLSEXT] Eastlake, D., 3rd, "Transport Layer Security (TLS)
Extensions: Extension Definitions", Work in Progress,
February 2008.
[TLSPGP] Mavrogiannopoulos, N., "Using OpenPGP Keys for Transport
Layer Security (TLS) Authentication", RFC 5081, November
2007.
[TLSPSK] Eronen, P., Ed., and H. Tschofenig, Ed., "Pre-Shared Key
Ciphersuites for Transport Layer Security (TLS)", RFC
4279, December 2005.
[TLS1.0] Dierks, T. and C. Allen, "The TLS Protocol Version 1.0",
RFC 2246, January 1999.
[TLS1.1] Dierks, T. and E. Rescorla, "The Transport Layer Security
(TLS) Protocol Version 1.1", RFC 4346, April 2006.
[X501] ITU-T Recommendation X.501: Information Technology - Open
Systems Interconnection - The Directory: Models, 1993.
[XDR] Eisler, M., Ed., "XDR: External Data Representation
Standard", STD 67, RFC 4506, May 2006.
工作组信息
略。