SSL协议 Version 3.0 3/4/96

来源:互联网

翻译—隋立颖

一 本文件的地位

本文件是一个Internet草案。Internet草案是Internet工程特遣组(ITEF)及其领域和工作组的工作文件。请注意,其他团体也可以发布Internet草案。

Internet草案自公布之日起至多六个月内有效。它可以随时被修改、被替换,或被其他文件所覆盖。把Internet草案作为文献来引用,除说明是“正在进展中的工作”以外,是不合适的。

要了解任何一个Internet的当前地位,请查阅如下网址:ds.internic.net(美国东海岸),nic.nordu.net(欧洲),ftp.isi.edu(美国东海岸)以及munnari.oz.au(环太平洋地区)。其中Internet Drafts Shadow Directories目录下的1id-abstract.txt列表可提供这方面的信息。

二 摘要

本文制定了安全套接层协议第3.0版(SSL3.0)规范。SSL是一个提供Internet上的通信隐私性的安全协议。该协议允许客户端/服务器应用之间进行防窃听、消息篡改及消息伪造的安全的通讯。

三 简介

制定SSL协议的初衷是为通讯双方提供安全可靠的通讯服务,协议包含两个层次:其较低的SSL记录层协议位于某一可靠的传输协议(例如TCP[TCP]协议)之上;SSL记录层协议用来对其上层的协议进行封装。握手协议就在这些被封装的上层协议之中,它允许客户端和服务器彼此认证对方;并且在应用协议发出或收到第一个数据之前协商加密算法和加密密钥。这样作的原因是保证了应用协议的独立性,使低级协议对高级协议是透明的。

SSL协议提供的连接安全性具有以下三条属性

l 连接是安全的。在初始化握手协议协商加密密钥之后传输的消息均为加密的消息。加密的算法为单钥加密算法(例如DES[DES], RC4[RC4]等)。

l 对方的身份是可以通过非对称加密算法-即公钥加密算法(例如RSA[RSA], DSS[DSS]等)来验证。

l 连接是可靠的。所传输的消息均包含一利用签名私钥加密的消息文摘(MAC),以保证消息的完整性。安全杂凑(hash)函数(例如SHA, MD5等)被用来产生消息文摘(MAC)。

四 目标

按它们的优先级,SSL协议3.0的目标是

l 在通讯双方之间利用加密的SSL消息建立安全的连接。

l 互操作性。通讯双方的程序是独立的,即一方可以在不知道对方程序编码的情况下利用SSL 3.0成功的交换加密参数。

注意:并不是所有的SSL的实例(甚至在同一应用程序内)都可以成功的连接。例如,如果服务器支持一特定的硬件令牌(token),而客户端不能访问此令牌,则连接不会成功。

l 可扩展性。SSL寻求提供一种框架结构,在此框架结构中,在不对协议进行大的修改的情况下,新的公钥算法和单钥算法可以在必要时被加入。这样做还可以实现两个子目标

1. 避免产生新协议的需要,因而进一步避免了产生新的不足的可能性。

2. 避免了实现一完整的安全协议的需要。

相对的有效性。加密操作,尤其是公钥加密,对CPU来说是一种很耗时的事,因此SSL协议引入一可选的对话缓存(CACHE)来减少从头开始的连接的数目。同时,它还注意减少网络的活动。

五 此文档的目的

SSL协议版本3.0详细说明书的主要读者是要实现此协议的人和进行加密分析的人。此详细说明书主要是为这两类人而写的,所以它时刻注意反映这两类人的需要。因此在本详细说明书中(而不是写在附录中)以文本方式包含了许多与算法相关的数据结构和规则,使它们易于被访问。虽然本详细说明书包含了维护物理安全性所必须的策略,本详细说明书并不想提供关于服务和界面的定义。

六 描述语言(Presentation language)

本文档主要是描述外部表示(external representation)的数据的格式,所以用到了下列简单、基础而有点随意的定义的表示语法,这些语法在结构上来自不同的出处。虽然这些语法在结构上有点象程序设计语言C、在语法和目标上象XDR[XDR],但过分的强调这种类似是有害的。本描述语言的目的仅仅是描述SSL。

6.1  基本块长(Basic block size)

所有的数据项的表示均是显式说明的,基本数据块的长度为一字节(也就是说8比特)多字节的数据项是由从上至下、从左至右的多个字节连接组成。从字节流的角度来看,一多字节的数据项(在本例中是一数字)是通过下述公式形成的:

value = (byte[0] << 8*(n-1)) | (byte[1] << 8*(n-2)) | ... | byte[n-1];

这种字节的顺序关系是网络中常用的顺序关系即大endian格式。

6.2  杂项

注释以“/*”开始,以“*/”结束。

可选的部分是由将其包含进斜体的括号“[ ]”中而指定的。

单字节的包含不可解释的数据的实体的类型为opaque。

6.3  向量(Vectors)

向量(一维数组)是一同类型的数据元素的流,向量的规模可以在编写文档时说明,也可以留至运行时才指明;不管在哪一种情况下,向量的规模(即大小)是由向量中字节的个数而不是向量中元素的个数决定的。

说明一新类型T’是一固定长度的类型T的向量的语法为:

T T'[n];

在这里,T’在数据流中占n比特,其中n是T的所占字节数的倍数。向量中包含数据元素的个数没有包含在数据流之中。在下例中,Datum被定义为三个连续的协议无法解释的字节的向量;而Data被定义为三个连续的Datum,一共包含9个字节

opaque Datum[3]; /* 三个协议无法解释的字节 */

Datum Data[9];   /* 三个连续的包含三个字节的向量 */

可变长向量可以通过指明合法长度的范围,即形如<最小长度..最大长度>的形式来定义,在编码时,在字节流中实际长度应在向量的内容之前,此实际长度是以数字的形式存储的,此数字应能表示此向量的最大长度(ceiling length)。空向量指的是一实际长度为零的向量。

T T'<floor..ceiling>;

在下例中,mandatory是一必须包含300到400字节的opaque类型数据的向量,它不能是空向量,其实际长度域占有两个字节,即uint16,足以表示400(见6.4节),而longer最多能表示800个字节,即400个uint16的数据元素,且<请合法使用软件>可以是空向量。它的编码包含一两字节的实际长度域。

opaque mandatory<300..400>; /* 长度域为两个字节,不能为空向量 */

uint16 longer<0..800>; /* 零至400个16比特的无符号整数 */

6.4  数字(Numbers)

基本的数字的数据类型是无符号字节(uint8)。其他所有的大的数字均是由6.1节中描述的固定长度的字节流连接而成,且它们均是无符号的。下列数字类型是预定义的:

uint8 uint16[2];

uint8 uint24[3];

uint8 uint32[4];

uint8 uint64[8];

6.5  枚举(Enumerate)

另一类稀疏的数据类型是枚举(enum),枚举类型的数据的取值范围只能是在其定义是声明的值。每一次定义均定义了一不同的类型。只有相同类型的枚举数据才可以相互赋值和比较,枚举类型的每个枚举元素均必须象下例中所示的那样,被赋一个值。由于枚举类型这的元素是没有顺序的,所以它们可以取任意顺序的不同的值。

enum { e1(v1), e2(v2), ... , en(vn), [(n)] }     Te;

枚举类型的值在字节流中占据的空间的大小是其定义的取值范围中最大的可能值所占的空间大小。下列定义会使类型Color占有一个字节。

enum { red(3), blue(5), white(7) } Color;

你还可以通过指定一个无标签的值来强制枚举类型所占的字节数,这样就不需定义一冗余元素了。在下例中,类型Taste在字节流中占两个字节但只能在1,2或4中取值。

enum { sweet(1), sour(2), bitter(4), (32000) } Taste;

枚举类型的元素的名字只在定义的类型中是有效的。在第一个例子中,对类型Color中的第二个元素的完全限定引用为Color.blue。这样的限定当赋值的目标是被说明的时候时是可以忽略的。

Color color = Color.blue; /* 重复说明,但是合法的 */

Color color = blue; /* 正确,类型是隐含的 */

对于元素的值不会转化为外部表示的枚举类型,元素的取值信息是可以省略的。

enum { low, medium, high } Amount;

6.6  结构(Constructed type)

结构类型可以由基本的数据类型方便的建成,每一次说明均声明了一新的、唯一的类型,定义的语法类似于C语言中结构的定义。

struct {

T1  f1;

T2  f2 ;

...

Tn  fn;

} [T];

在一结构中的各域可以象枚举中引用元素时的语法一样用类型名加域名引用,例如T.f2引用的是上例中的第二个域,结构的定义可以嵌套。

6.6.1  变体结构(Variant)

定义结构时可能根据环境的不同而有不同的变体,选择器必须为枚举类型的数据,以定义结构中可能的变体,且必须用case语句将select中声明的每个枚举元素不会起来。变体结构的结构体可以有一供其引用的标签。在运行时如何决定变体的机制并没有在描述语言中规定。

struct {

T1  f1;

T2  f2 ;

...

Tn  fn;

select (E) {

case e1 :  Te1 ;

case e2 :  Te2 ;

....

case en :  Ten ;

} [fv];

} [Tv];

例如:

enum { apple, orange } VariantTag;

struct {

uint16 number;

opaque string<0..10>; /* 可变的长度 */

} V1;

struct {

uint32 number;

opaque string[10];   /* 固定的长度 */

} V2;

struct {

select (VariantTag) {              /* 变体的选择器是隐含的 */

case apple: V1;             /*VariantBody的定义,标签= apple */

case orange: V2;            /*VariantBody的定义,标签= orange */

} variant_body;                       /* 可选的变体标签 */

} VariantRecord;

变体结构可以通过在类型前指定选择器的值来限定(narrowed),例如:

orange VariantRecord

是类型VariantRecord的一限定,它包含类型为V2的variant_body。

6.7  加密属性(Cryptographic attribute)

数字签名、流加密、块加密和公钥加密这四项加密操作的加密属性分别为digitally-signed,stream-ciphered,block-ciphered和public-key-encrypted。对一个域进行何种加密操作是由在此域的类型说明前的合适的加密属性(关键字)决定的,加密的密钥是由当前对话状态字隐含给出的(见7.1节)。

在数字签名中,输入为一单向哈希函数(one-way hash function)。当用RSA算法进行签名时,用签名私钥对一36字节的结构进行签名,此36字节的结构是由两个哈希函数生成的,一个为SHA,另一个是MD5。当用DSS算法进行签名时,可以直接对由SHA哈希函数生成的20字节的结构进行签名。

在流加密中,明文的长度与由密钥产生的密文的长度是相同的。此密钥是由伪随机数发生器生成的安全的密钥,是根据时间的变化而变化的。

在块加密中,密文的每一块均被加密成一块密文。由于明文的长度并不是固定的,所以有可能在对明文(发出的数据)分块时产生一不满的块(块的长度通常为64比特),此时就需要将此不满的块的剩余部分填充数据,一般来说是填零。

在公钥加密中,单向限门函数被用来加密要发出的数据,用一给定公钥加密的数据仅能由相应的私钥解出,同样用一给定私钥加密的数据仅能由相应的公钥解出。

在下例中:

stream-ciphered struct {

uint8 field1;

uint8 field2;

digitally-signed opaque hash[20];

} UserType;

哈希函数的结果做为签名算法的输入,整个结构UserType用流加密进行加密。

6.8  常量(Constant)

有类型的常量可以通过在常量名前加上所希望的类型,并在常量名后给其赋上所期待的值来定义。未限定的类型(opaque,可变长向量,变体结构及带有opaque的结构)不能被赋值。多元素的结构和向量的所有域均应被赋值。

例如:

struct {

uint8 f1;

uint8 f2;

} Example1;

Example1 ex1 = {1, 4}; /* 令 f1 = 1, f2 = 4 */

七、 SSL 协议

SSL是一层次化协议。在每一层,消息均可以包含描述长度、消息的描述及消息的内容的域。SSL在传输消息时,首先将消息分为其可处理的数据块,可以进行压缩,将其封装为一带消息验证(MAC)的包,随之进行加密并传输所得到的加密后的消息。在收到消息时,首先解密,然后验证、解压缩并重新组合得到原有的消息,将此消息发向较高的层次。

7.1 对话及连接状态

SSL的对话是有状态的,是由SSL的握手协议来同步客户端和服务器的状态的,因而允许它们一致的操作而不管它们的协议状态是否是并行的。从逻辑上讲,状态被提到了两次,一次是当前的操作状态,而另一次是在握手协议中的未决状态。而且,还维持单独的读状态和写状态。当客户端或服务器收到change cipher spec消息时,它将未决读状态复制到当前读状态。当客户端或服务器发出change cipher spec消息时,它将未决写状态复制到当前写状态。当握手协商完毕时,客户端或服务器将彼此交换change cipher spec消息(见7.3节),这样它们就可以用新同意的cipher spec来进行通信了。

SSL对话可以包含若干次安全连接,而且每一方均可以同时有多个对话。

对话状态包含下列元素:

对话标识 一由服务器为标识当前活跃的对话或重新开始的对话而随机

选取的字节流。

对等证书 对等方的X509.v3[X509]证书,状态的此元素可以为空。

压缩方法 在加密之前压缩数据所采用的算法。

加密说明 指出所采用的数据加密算法(如没有采用加密算法,采用DES

等)和作消息文摘的算法(如MD5,SHA等),它还定义象

hash_size一类的加密的属性(见附录A.7)

主共享的秘密 客户端和服务器所共享的48比特的共享的秘密。

是否可以重开始 一标识此对话是否可以用来初始化新的连接的标志。

连接状态包含下列元素:

客户端和服务器的随机数 由客户端和服务器为建立一次连接而随机选取的一字

节流。

服务器的MAC写共享秘密 服务器在对数据进行消息验证(MAC)操作时所使用的

共享的秘密。

客户端的MAC写共享秘密 客户端在对数据进行消息验证(MAC)操作时所使用的

共享的秘密。

服务器的写密钥 服务器在对数据进行加密时所使用的加密密钥,此密

钥也是客户端进行解密时的解密密钥。

客户端的写密钥 客户端在对数据进行加密时所使用的加密密钥,此密

钥也是服务器进行解密时的解密密钥。

初始化向量 当用CBC方式进行块加密时,对于每一密钥系统都将维

护一初始化向量(IV)。此域的值是由SSL握手协议进行

初始化的,为以后使用的方便此后的每一记录的最终的

密文块被保存在记录的后面。

序列号 参与连接的每一方都为其发出的和收到的消息维护一独

立的序列号。当一方发出或收到Change Cipher spec

消息时,其序列号置为零。序列号的类型为Unit64,所

以序列号不能超过264 -1。

7.2 记录层

SSL记录层由更高的层次那里接收未加解释的任意长度的非空块。

7.2.1 打包

记录层将信息块分裂为小于或等于214字节的SSLPlainText记录。客户端消息的界限并不反映至记录层中(也就是说,具有同样ContentType的多个客户消息可能会合并为一SSLPlaintext记录)。

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[SSLPlaintext.length];

} SSLPlaintext;

其中

type        指出采用打包的更高层次的协议。

version     协议的版本号。此文档所描述的是SSL版本3.0 (见附录A.1.1).

length      SSLPlaintext的字节长度,此长度不应超过214

fragment    应用数据。此数据对由Type域中所指出的更高层次的协议是透明的,并被

视为一独立的块 。

注意: 不同的SSL记录层的CententType数据可以交叉存取,应用数据一般要比其他的

ContentType数据要求更低级的传输过程。

7.2.2 记录的压缩和解压缩

所有的记录均应用在当前的对话状态中定义的压缩算法进行压缩。一般地,此算法为当前活跃的压缩算法,但在初始化时它被定义成CompressionMethod.null。压缩算法将 SSLPlaintext结构转换为SSLCompressed 结构,当CipherSpec变换后,压缩函数将删除其状态信息。

注意: CipherSpec是在7.1节中所描述的对话状态的一部分, 对CipherSpec 中各域的引

用在本文档中是用表示语法来表达的。关于CipherSpec的更加详细的描述见附录

A.7.

压缩必须是无损压缩且对原文的长度的增加不超过1024比特。如果解压缩函数遇到一待解的超过214比特的SSLCompressed.fragment,它将产生一终止的decompression_failure 报警(见7.4.2节)。

struct {

ContentType type; /* same as SSLPlaintext.type */

ProtocolVersion version; /* same as SSLPlaintext.version */

uint16 length;

opaque fragment[SSLCompressed.length];

} SSLCompressed;

length        SSLCompressed.fragment的长度(单位:字节)。长度不应超过214+1024。

fragment      SSLPlaintext.fragment的压缩格式。

注意: 操作CompressionMethod.null是一标识性操作,没有任何一个域的值被改变

(见附录 A.4.1) 。

实现时请注意:解压缩函数的责任是保证消息不会造成内部的缓冲区溢出。

7.2.3 记录的有效负荷保护和加密说明(CipherSpec)

所有的记录均用在当前的加密说明(CipherSpec)中定义的加密算法和消息验证(MAC)算法所保护, 一般地,在SSL内部有一活跃的CipherSpec,但在初始化时,它的值为SSL_NULL_WITH_NULL_NULL, 从值并不提供任何安全性。只有当握手结束后,参与双方共享一用于加密记录和计算消息验证码(MACs)的公共秘密。进行加密和消息验证(MAC)操作的技术有CipherSpec定义,并受CipherSpec.cipher_type的限制。加密和消息验证(MAC)函数将一SSLCompressed结构转换为一 SSLCiphertext结构,解密函数作相反的过程。传输时将包含一序列号,这样当包丢失、被改变或包被重复收到时可以及时的发现。

struct {

ContentType type;

ProtocolVersion version;

uint16 length;

select (CipherSpec.cipher_type) {

case stream: GenericStreamCipher;

case block: GenericBlockCipher;

} fragment;

} SSLCiphertext;

type              类型域被指定为SSLCompressed.type。

version           版本域被指定为SSLCompressed.version。

length            指明随后的SSLCiphertext.fragment的长度(单位:字节)。长度不应

超过214+2048。

fragment          包含消息验证码(MAC)的SSLCompressed.fragment加密后的形式。

7.2.3.1 Null 或标准的流加密

流加密(包含BulkCipherAlgorithm.null –见附录A.7)将SSLCompressed.fragment结构转换为SSLCiphertext.fragment 流结构,或者反之,将SSLCiphertext.fragment结构转换为SSLCompressed.fragment 流结构。

stream-ciphered struct {

opaque content[SSLCompressed.length];

opaque MAC[CipherSpec.hash_size];

} GenericStreamCipher;

产生的消息验证码(MAC)形式为:

hash(MAC_write_secret + pad_2 +

hash (MAC_write_secret + pad_1 + seq_num + length + content));

其中“+”表示将前后连接起来。

pad_1      字符0x36 在MD5算法中重复48次或在SHA算法中重复40次。

pad_2      字符0x5c,与pad_1一样的重复。

seq_num    从消息的序列号。

hash       由cipher组合所决定的杂凑算法。

请注意,消息验证码(MAC)在加密之前就计算出来了。流加密加密的是包含消息验证码(MAC)在内的整个块。对于不用同步向量的流加密方法(如RC4),在记录后边的流加密方法的状态被简单的用在随后的包中,若CipherSuite是SSL_NULL_WITH_NULL_NULL,且加密包含指定的操作(也就是说,数据还未被加密且消息验证码(MAC)的长度为零,标志着不使用消息验证码(MAC)), 则SSLCiphertext的长度是 SSLCompressed的长度与CipherSpec.hash_size的和。

7.2.3.2密码分组链接(CBC)块加密

对于块加密(象RC2或DES),加密和消息验证(MAC)函数将SSLCompressed.fragment结构转换成SSLCiphertext.fragment块结构。

block-ciphered struct {

opaque content[SSLCompressed.length];

opaque MAC[CipherSpec.hash_size];

uint8 padding[GenericBlockCipher.padding_length];

uint8 padding_length;

} GenericBlockCipher;

消息验证(MAC)与7.2.3.1节中描述的一样。

padding         为了使明文的长度成为加密块长度的倍数而添加的随机字节。

padding_length  填料的长度必须比加密块的长度小且可以为零,填料的长度应使结构

GenericBlockCipher的总长度是加密块长度的倍数

被加密的数据长度(SSLCiphertext.length)应比SSLCompressed的长度, CipherSpec.hash_size和填料的长度的总和多一。

注意: 对于CBC块加密来说,其第一个记录的CBC块链的初始化向量(IV)是由握手协议提供的,随后记录的IV是前一记录的最后一密文块。

7.3 更改加密说明cipher spec的协议

更改加密说明(cipher spec)的协议在加密策略中被用来通知参与各方这一改变。协议只包含一个在当前(不是未决的)CipherSpec下加密并压缩过的消息。此消息包含一个字节,其值为1。

struct {

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

} ChangeCipherSpec;

更改cipher spec的消息可以由客户端或服务器发出来通知对方随后的记录将由刚协商好的CipherSpec和密钥来保护。收方收到此消息后,将读未决状态复制到当前读状态中。客户端在密钥交换握手和证书验证消息(如果有的话)之后发出更改cipher spec的消息,服务器则在成功的处理了客户端发来的密钥交换消息之后发出一更改cipher spec的消息。一意外的更改cipher spec消息应产生一unexpected_message报警(见 7.4.2节)。当重新开始一原有的对话时,更改cipher spec消息应在问候消息(hello messages)之后发出。

7.4 报警协议

由SSL记录层所支持的一种媒体类型为报警类型,报警消息带有此消息的严重程度的编码和对此报警的描述。最严重一级的报警消息将立即终止连接,在这种情况下,本次对话的其他连接还可以继续进行,但对话标识符必须无效以防止此失败的对话重新建立新的连接。象其他的消息一样,报警消息是利用由当前连接状态所指出的算法加密和压缩的。

enum { warning(1), fatal(2), (255) } AlertLevel;

enum {

close_notify(0),

unexpected_message(10),

bad_record_mac(20),

decompression_failure(30),

handshake_failure(40), no_certificate(41), bad_certificate(42),

unsupported_certificate(43), certificate_revoked(44),

certificate_expired(45), certificate_unknown(46),

illegal_parameter (47)

(255)

} AlertDescription;

struct {

AlertLevel level;

AlertDescription description;

} Alert;

7.4.1 关闭报警

客户端和服务器为避免截断攻击必须共享连接已关闭这一信息,它们中的任一方均可以初始化关闭信息的交换。

close_notify       此消息通知收方发出者不会在此连接内再发任何消息,当一次对话中

的所有连接都没有恰当的close_notify 消息而终止时,此次对话是

不能重新开始的。close_notify 消息的严重程度是警告级的

7.4.2 错误报警

在SSL握手协议中的错误处理是很简单的,当发现一个错误后,发现方将向对方发一消息。当传输或收到最严重一级的的报警消息时,连接双方均立即终止此连接。服务器和客户端均应忘记前一次对话的标识符、密钥及有关失败的连接的共享信息。SSL中定义了下列错误报警:

unexpected_message      收到一意外的消息,此报警通常是致命性错误的报警且不应在正

常的连接中被观察到。

bad_record_mac          当收到一带有不正确的MAC的记录时,将返回此报警。此报警通

常是致命性错误的报警。

decompression_failure   解密函数收到不合法的输入(如数据太长等),此报警通是致命

性错误的报警。

handshake_failure       收到一handshake_failure报警消息表明发出者不能接受现有的

选项所提供的安全参数的集合,此报警通常是致命性错误的报

警。

no_certificate          当被要求给出证书而没有合法的证书时,将发出一

no_certificate报警消息。

bad_certificate         当一证书被讹用、或者证书中不会的签名不能被正确的认证等

时,发出此报警。

unsupported_certificate 一有不被支持的类型的证书(如包含了用户自定义的扩展)。certificate_revoked     一被其发出者取消的证书。

certificate_expired     一过期了的或不合法证书。

certificate_unknown     由一些不明的发出者发出的证书所引起的证书的不可接受性。

illegal_parameter       在握手中的一个域的值溢出或与其他域的值不一致,此报警是致

命性错误的报警。

7.5 握手协议总揽

wps_clip_image-19046[3][1]

对话状态的加密的参数是由SSL握手协议产生的,握手协议是在SSL记录层的顶部操作的。当一SSL客户和服务器首次开始通讯时,它们就协议版本、加密算法的选择、是否验证对方及公钥加密技术的应用进行协商以产生共享的秘密,这一处理是由握手协议完成的,可以总结如下:

其中*表示不是必须发出的可选的或依赖于环境的消息

客户端首先发出客户问候消息(client hello message),服务器收到之后或者发出服务器问候消息(server hellomessage),或者发生一终止性的错误然后此次连接将无法建立客户和服务器问候消息(client hello message)被用来在客户端和服务器之间建立安全的性能的协商,客户和服务器的问候消息(client hello message)将产生了下列属性:协议版本号、对话标识符、加密套接字及压缩方法,而且产生了两个随机数ClientHello.random和 ServerHello.random并且相互交换。

在问候消息之后,若要求验证身份,服务器将发出其证书,另外,如果需要的话(例如,如果他们的服务器没有证书,或者其证书仅用来进行签名),将发出一个server key exchange消息。如果这个服务器已经被认证,而且被所选的密码组( cipher SUITE)所允许的话,它将向客户端请求证书。

现在这个服务器将发出服务器问候结束消息(server hello done message),表明握手过程中的问候消息阶段已经结束。这个服务器接着将等候客户端的回答。

如果服务器发出一个certificate request 消息,客户端必须发出证书消息(certificate messang),或者一个no certificate报警。现在,客户端密钥交换消息(client key exchangemessage)准备发送,而且消息的内容将取决于在客户端问候消息(client hello message)和服务器问候消息(server hello message)之间所选择的公钥算法。如果客户端已经发出了一个具备签名能力的证书,一个数字签名后的证书验证消息(certificate verify message)将被发送以确认此证书的合法性。

就这一点而言,一个改变加密说明(change cipher spec)消息是被客户端发送的,而且客户端将未决的Cipher Spec复制到当前 Cipher Spec。然后,客户端立即用协商的新的算法、密钥、和共享信息发出结束消息。服务器将发出其自己的改变cipher spec 消息(change cipher spec message)作为回应,将未决(pending) cipher spec复制到当前Cipher Spec,并用新的cipher spec发出其结束消息。此时,握手过程结束,客户端和服务器可以开始交换应用层数据了(见上图)。

注意: 为了避免通道延迟,changeCipher spec是一独立的SSL协议的媒体类型,而并非

一SSL握手消息。

wps_clip_image-25447[3][1]

当客户端和服务器决定重新开始一以前的对话或复制一已存在的对话(并不协商新的安全参数)时,其消息流如下:

客户端首先利用需要重新开始的对话的对话标识符发出客户端问候消息(ClientHello message),服务器检查对话缓存来寻找匹配的对话,若找到匹配的对话,服务器将在指定的对话状态下重新建立连接,它将发出带有对话标识符的服务器问候消息(ServerHello)。此时,客户端和服务器均必须发出改变cipher spec 消息(change cipher spec message)和结束消息(finished message),只要此重新建立连接一完毕,客户端和服务器将交换应用层数据(见上图)。若没找到找到的对话标识符,服务器将产生新的对话标识符,然后SSL客户和服务器将进行正常的握手过程。

每一消息的内容和重要性将在以下各节中详细的描述。

7.6 握手协议

SSL握手协议是SSL记录协议(record Protocol)的一个高级的客户。此协议用来协商一对话的安全参数,握手消息被传给SSL记录层,在那里这些消息被封装在一个或多个SSLPlaintext结构之中,这些SSLPlaintext结构由当前活跃的对话状态中所指定的参数进行处理和传输。

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;

握手协议的消息必须按指定的顺序发出(此顺序如上所示),若不按此顺序发出,则会重新终止性错误。

7.6.1 问候消息(Hello message)

问候消息用来在客户端和服务器之间交换彼此安全系统的性能。当一个对话刚开始时, CipherSpec中的加密、杂凑(hash)和压缩算法均初始化为Null,当前的CipherSpec被用来协商这些参数。

7.6.1.1 Hello request

要求问候消息(hello request message)可以由服务器在任何时间发出,但若客户端正在进行握手过程的话,此消息将会被客户端所忽略。它只是一个简单的通知,通知客户端可以在方便的时候发出一客户问候消息(client hello message )来开始一协商的过程。

注意:由于握手消息是比应用层数据的传输优先级高,所以协商的时间不应超过最大长度的

应用层数据消息的传输时间的一到两倍。

在发出要求问候消息之后,在握手协商未完成之前服务器不应再重复发出此消息。一处于协商状态中的客户可以简单的忽略其收到随后要求问候消息。

要求问候消息的结构如下:

struct { } HelloRequest;

7.6.1.2 客户问候消息(Client hello)

当一客户第一次与服务器进行连接时,它必须发客户问候消息(Client hello)作为它的第一个消息。客户也可以将此消息作为对服务器发来的要求问候消息的回应,还可以作为它自己想重新协商一已存在的对话的安全参数的标志发出此消息。

客户问候消息(Client hello)包含一以后会在协议中用到的随机结构

struct {

uint32 gmt_unix_time;

opaque random_bytes[28];

} Random;

gmt_unix_time   以标准的UNIX的32位形式表示的发出者的内部时钟的当前时间和日期。

SSL协议中并不要求时钟必须是准确的,更高级的协议或应用层协议可能

会对时钟有更高的要求。

random_bytes    一由安全随机数发生器产生的28字节的随机数。

客户问候消息(Client hello)包含一可变长度的对话标识符,如果此域的值非空,则此值标识了在同一客户和同一服务器之间的一次对话,此次对话的安全参数是客户端想重新使用的。 这个对话的标识符也许是来自一个早期的连接,此次连接,或另一个当前活动的连接。如果客户端仅仅要更新这个随机结构及一次连接的导出值的话,第二种选择是有用的,而第三种选择使得不必完全重复握手协议而同时建立几种同步且独立的安全连接成为可能。对话标识符的实际内容由服务器定义。

opaque SessionID<0..32>;

注意:服务器不能在对话标识符中放置机密信息,不能使假的对话标识符中的内容导致任何泄密。

这个包含在由客户端传向服务器的客户问候消息(client hello)中的加密套接字(CipherSuite)列表包含了加密算法的组合,此列表是客户端所支持的的加密算法的列表,且此列表的顺序是由客户端按其自身的偏爱而选定的(列表的第一项是其最喜爱的)。每一CipherSuite同时定义了一个密钥交换算法和一个CipherSpec。服务器将或者选择一个CipherSuite,或者,如果未给出可接受的选择, 将返回一个handshake failure报警并关闭连接。

uint8 CipherSuite[2];    /* 加密组选择器 */

此客户问候消息(client hello)包含了一个由客户端支持的压缩算法的列表,且根据客户端的偏好排序。如果服务器不支持客户端所定义的任何一种压缩算法,则此次对话失败。

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

问题:需要支持哪一种要压缩算法正处于调查之中。

客户问候消息(client hello)的结构如下:

struct {

ProtocolVersion client_version;

Random random;

SessionID session_id;

CipherSuite cipher_suites<2..2 16 -1>;

CompressionMethod compression_methods<1..2 8 -1>;

} ClientHello;

client_version       客户端希望在此次对话中使用的SSL协议的版本。这应该是被客户

端所支持的最新的版本(最高值)。对于本文所描述的SSL协议,版

本号应该是3.0。(关于背景兼容信息请见附录E)。

random               一个客户端生成的随机结构。

session_id           客户端在此次连接中想使用的对话标识符(ID)。如果没有可用的

session—ID或者客户端想要生成新的加密参数,这个值应该为空。

cipher_suites        这是一个由客户端支持的,由客户端按其自身的偏爱而选定的加密套

接字的列表(列表的第一项是其最喜爱的),如果session_id 域非

空(暗示重新开始一已有的对话),则此向量必须至少包含来自已有

对话的cipher_suite。加密套接字的值的定义见附录A.6。

compression_methods  这是一个由客户端支持的压缩算法的列表,他根据客户端自身的偏

爱而选定的(列表的第一项是其最喜爱的),如果session_id 域非

空(暗示重新开始一已有的对话),则此向量必须至少包含一个来自

已有对话的compression_methods的参数。所有实现均必须支持

CompressionMethod.null。

继发送client hello消息之后,客户端等候一个服务器问候消息(server hello message)。除了hello消息外,由服务器返回的任何其他握手消息,均被视为致命错误(fatal error)。

实现时注意:只有当结束消息(finishedmessage)发送之后,应用程序的数据才能被发送。在一个合法的结束(finished)消息被收到之前,就传输应用程序的数据被认为是不安全的。如果此次连接有一个当前的,非空的加密,则此绝对的限制将被放宽。

7.6.1.3  服务器问候消息(Server hello)

服务器处理客户端问候消息(client hello)并且对客户端问候消息作出握手失败(handshake_failure)警告或者发出服务器问候消息(server hello)作为响应。

struct {

ProtocolVersion server_version;

Random random;

SessionID session_id;

CipherSuite cipher_suite;

CompressionMethod compression_method;

} ServerHello;

server_version           这个域将包含客户端在客户端问候消息(client hello)中建

议使用的最低版本和被服务器所支持的最高版本。对于使用本

详细说明书的版本来说,版本号应为3.0(关于背景兼容性的详

细信息请见附录E)

random                   这个结构由服务器产生,且必须与客户问候消息中的

ClientHello.random不同,并且独立于ClientHello.random。

session_id               这是对应于此次连接的对话标识。若客户问候消息中的

ClientHello.session_id非空,服务器将在对话缓存器中寻找

匹配的对话。如果匹配被找到并且服务器希望利用给定的对话

对话状态码建立一个新的连接,服务器将用与客户问候消息中

给定的相同的值响应,这确认了一个重新开始的对话,且对话

双方将在收到对方的直到finished消息之前将继续执行下去。

否则,这个域将包含一个与客户问候消息中的session_id不同

的值以标志一个新的对话。服务器可以返回一个空对话标识

(session_id)用来表示这个对话将不会被缓存,而且因此不能

被重新开始。

Cipher_suite             由服务器从客户端问候消息中的ClientHello.cipher_suites

列表中选择一个加密套接字而得到的。对于重新开始的对话,

此域的值是从重新开始的对话状态字中得到的。

compression_method      由服务器从客户端问候消息中的compress_method列表中选择的

一压缩算法。对于重新开始的对话此域的值是从重新开始的对话

状态字中得到的。

7.6.2  服务器的证书(Server certificate)

如果服务器要求被认证(通常都是这么要求的),则服务器在发出服务器问候消息(server hello message)之后接着发出服务器证书。证书的类型必须是由被选择的加密套接字中密钥交换算法所支持的,通常是X.509.版本3的证书(或是一在Fortezza [FOR]下的修改过的X.509证书。客户端的证书的类型与服务器的证书类型相同。

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

struct {

ASN.1Cert certificate_list<1..224-1>;

} Certificate;

certificate_list       证书链中包含一序列X.509.版本3的证书,这些证书的顺序是首先是要验证的一方的证书,然后是发出前一个证书的一方的证书…最后是根的证书。

注意:由于没有使用PKCS #6 [PKCS6]扩充证书,所以PKCS #7 [PKCS7]也不能做为证书向量的格式来使用。且PKCS #7定义了一个集合而不是一个序列,从而使解析证书列表的任务更为复杂。

7.6.3  服务器密钥交换消息(Server key exchange message)

若服务器没有证书,或只有供其签名用的公钥证书(例如DSS[DSS]证书,只供签名的RSA [RSA]证书),或使用了Fortezza/DMS密钥交换,则服务器发出服务器密钥交换消息(server key exchange message)。当服务器的证书中包含Diffie-Hellman [DH1](即公钥加密)参数时,则不发出此消息。

注意:根据当前的美国出口法律,在软件中用于密钥交换的超过512比特的RSA模块是不允许出口的。对于此消息,长的RSA密钥可以做为只用作签名的证书来签用作密钥交换的临时的短的RSA密钥。

enum { rsa, diffie_hellman, fortezza_dms } KeyExchangeAlgorithm;

struct {

opaque rsa_modulus<1..216-1>;

opaque rsa_exponent<1..216-1>;

} ServerRSAParams;

其中:

rsa_modulus          服务器的临时的RSA密钥的模数。

rsa_exponent         服务器的临时的RSA密钥的公开指数。

struct {

opaque dh_p<1..216-1>;

opaque dh_g<1..216-1>;

opaque dh_Y s <1..216-1>;

} ServerDHParams;      /* 短期的DH参数 */

其中:

dh_p             Diffie-Hellman操作中用到的质模数。

dh_g             Diffie-Hellman操作中用到的发生器。

dh_Y             服务器的Diffie-Hellman公开值(gX mod p)。

struct {

opaque r_s [128];

} ServerFortezzaParams;

其中:

r_s           服务器为密钥交换算法而生成的随机数。

digitally-signed struct {

select(SignatureAlgorithm) {

case anonymous: struct { };

case rsa:       opaque md5_hash[16];

opaque sha_hash[20];

case dsa:        opaque sha_hash[20];

};

} Signature;

struct {

select (KeyExchangeAlgorithm) {

case diffie_hellman:  ServerDHParams  params;

Signature signed_params;

case rsa:             ServerRSAParams params;

Signature signed_params;

case fortezza_dms:    ServerFortezzaParams params;

};

} ServerKeyExchange;

其中:

params  服务器的密钥交换参数。

signed_params  相应的参数值的哈希值,及对此哈希值的签名。

md5_hash            MD5(ClientHello.random + Serverhello.random +ServerParams);

sha_hash            SHA(ClientHello.random + ServerHello.random +ServerParams);

enum { anonymous, rsa, dsa } SignatureAlgorithm;

7.6.4  证书请求(Certificate request)

如果选择的加密套接字允许的话,一非匿名的服务器可以向客户端要求客户端的证书。

enum {

rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),

rsa_ephemeral_dh(5), dss_ephemeral_dh(6), fortezza_dms(20), (255)

} ClientCertificateType;

opaque DistinguishedName<3..216-1>;

struct {

ClientCertificateType certificate_types<1..28-1>;

DistinguishedName certificate_authorities<3..216-1>;

} CertificateRequest;

certificate_types         此域的值是一被请求的证书的类型的列表,此列表是按服务器

的偏好的先后顺序排序的。

certificate_authorities   是可接受的证书授权当局的唯一的辨别名的列表。

注意:唯一的辨别名是由[X509]中导出的。

注意:对于一匿名的服务器来说,请求客户端发出客户端的证书是一致命的握手失败报警。

7.6.5  服务器问候结束(Server hello done)

由服务器发出的服务器问候结束消息(server hello done message)是表明服务器问候和其相关的消息的结束。当服务器发出此消息后,它将等待客户端的回应。

struct { } ServerHelloDone;

当客户端收到服务器问候结束消息(server hello done message)后,若它要求的话,它将验证服务器的证书是否合法,并且检查在服务器问候消息中的参数是否是可接受的。

7.6.6  客户端的证书(Client certificate)

客户端证书是客户端收到服务器问候结束消息(server hello done message)后所能发出的第一个消息。只有当服务器要求客户端的证书时客户端才发出此消息。如果客户端没有合适的证书,则它发出没有证书报警(no certificate alert),此报警仅仅是一个警告,若服务器要求客户端认证,则服务器回应一握手失败的致命性的报警。

客户端的证书的格式的定义件7.6.2节。

注意:客户端的Diffie-Hellman证书必须与服务器指定的Diffie-Hellman参数相匹配。

7.6.7  客户端密钥交换消息(Client key exchange message)

是否选择发出此消息依赖于在密钥交换算法定义中选择的公钥算法(见7.6.3节)

struct {

select (KeyExchangeAlgorithm) {

case rsa: EncryptedPreMasterSecret;

case diffie_hellman: ClientDiffieHellmanPublic;

case fortezza_dms: FortezzaKeys;

} exchange_keys;

} ClientKeyExchange;

选择合适的记录结构的信息在未决的对话状态字中(见7.1节)

7.6.7.1 RSA加密的预主秘密消息 (RSAencrypted premaster secret message)

若在密钥一致性及认证时使用的是RSA算法,则客户端产生一48比特的预主秘密(pre-master secret),并用服务器证书中的公钥或一由服务器密钥交换消息中得到的临时RSA密钥对其进行加密,然后将加密的结果用一加密的预主秘密(encrypted premaster secret)消息发给服务器。

struct {

ProtocolVersion client_version;

opaque random[46];

} PreMasterSecret;

client_version         客户端所支持的最新的SSL版本,此域用来发现版本重算攻击

(version roll-back attacks)。

random   46比特安全产生的随机数。

struct {

public-key-encrypted PreMasterSecret  pre_master_secret;

} EncryptedPreMasterSecret;

pre_master_secret        由客户端产生的此随机数是用来产生8.1节中说明的主秘密

(master secret)的。

7.6.7.2  Fortezza密钥交换消息(Fortezza key exchange message )

在Fortezza DMS下,客户端利用Fortezza密钥交换算法(KEA)得到一标记加密密钥(Token Encryption Key (TEK))。 客户端利用服务器证书中的服务器公钥和其自身的标记参数来计算KEA,并且利用其自身的参数发出供服务器产生TEK的公开参数。客户端产生对话密钥,用TEK封装后发给服务器;客户端还产生对话密钥的初始向量(IV)和TEK,并将它们发给服务器;它还初始一48比特的随机的预主秘密,用TEK加密后发给服务器。

struct {

opaque y_c<0..128>;

opaque r_c[128];

opaque y_signature[20];

opaque wrapped_client_write_key[12];

opaque wrapped_server_write_key[12];

opaque client_write_IV[24];

opaque server_write_IV[24];

opaque master_secret_IV[24];

block-ciphered opaque encrypted_pre_master_secret[48];

} FortezzaKeys;

y_signature              y_signature是KEA公钥的签名,是用客户端的DSS私钥签的

名。

y_c                        在KEA计算中用到的客户端的Yc值(公钥)。若客户端已发

出其证书且KEA公钥是合适的,则由于证书中已有此值,此

域可以为空。当客户端发出一密钥合适的公钥的证书的时候,

就要有y_c,且y_signature是用客户端DSS私钥签署的KEA

公钥的签名。它必须在64至128比特之间。

r_c                        客户端为计算KEA的Rc值。

wrapped_client_write_key    这是客户端的写密钥,是由TEK封装的。

wrapped_server_write_key   这是服务器的写密钥,是由TEK封装的。

client_write_IV              这是客户端的写密钥的初始化向量。

server_write_IV             这是服务器的写密钥的初始化向量。

master_secret_IV           这是用来加密预主秘密的TEK的初始化向量。

pre_master_secret          这是一个由客户端产生的随机数,用来产生8.1节中所描述

的主秘密。在上述结构中,它是用TEK加密过的。

7.6.7.3  客户端Diffie-Hellman公开值(Client Diffie-Hellman public value)

若客户端的Diffie-Hellman公开值(Yc)未包含在客户端的证书中的话,此结构将传送它。Yc 使用的编码方式是由枚举类型PublicValueEncoding. 所决定的。

enum { implicit, explicit }    PublicValueEncoding;

其中:

implicit          若客户端的证书中已包含此公开值,则它是隐含的,所以Yc不需再传了。

Explicit          Yc需要被传送。

struct {

select (PublicValueEncoding) {

case implicit: struct { };

case explicit: opaque dh_Yc<1..216 -1>;

} dh_public;

} ClientDiffieHellmanPublic;

其中:

dh_Yc                客户端的Diffie-Hellman公开值(Yc)。

7.6.8  证书验证(Certificate verify)

此消息被用来对客户端的证书提供明显的验证。此消息仅在有签名能力的客户端证书(即没有包含固定的Diffie-Hellman参数的所有证书)发出之后才被发出。

struct {

Signature  signature;

} CertificateVerify;

CertificateVerify.signature.md5_hash

MD5(master_secret + pad2 +

MD5(handshake_messages + master_secret + pad1));

Certificate.signature.sha_hash

SHA(master_secret + pad2 +

SHA(handshake_messages + master_secret + pad1));

在这里,握手消息(handshake_messages)指的是从客户端问候(client hello)消息开始的,不包含此消息在内的到目前为止的所有握手消息。

7.6.9  结束消息(Finished)

在发出旨在验证密钥交换和认证过程是否成功的更改加密说明消息(change cipher specs message)之后,发出此结束消息,此结束消息是第一个由刚刚协商的算法、密钥及秘密保护的消息,且此消息不要求回应。通信各方在发出结束消息之后便可发出加密过的应用数据。此结束消息的接收者必须验证消息内容是正确的。

enum { client(0x434C4E54), server(0x53525652) } Sender;

struct {

opaque md5_hash[16];

opaque sha_hash[20];

} Finished;

其中:

md5_hash              MD5(master_secret + pad2 + MD5(handshake_messages +

Sender + master_secret + pad1));

sha_hash              SHA(master_secret + pad2 + SHA(handshake_messages +

Sender + master_secret + pad1));

由服务器发出的结束消息中包含的哈希值与Sender.server中的值是一致的,而由客户端发出的结束消息中的哈希值与Sender.client中的值相一致。握手消息(handshake_messages)中的值包含由客户端问候消息开始的,不包含结束消息在内的所有握手消息。由于它包含证书验证消息(若它发出的话),所以这与7.6.8节中定义的握手消息(handshake_messages)不同。

注意:更改加密说明消息(Change cipher spec messages)不是握手消息,所以它并没有包含在哈希值的计算中。.

7.7   应用数据传输协议(Application data protocol)

应用数据消息是由记录层传输的,它根据当前的连接状态被打包、压缩和加密。这些消息被记录层认为是透明的数据。

八 加密计算(Cryptographic computations)

密钥交换。认证、加密和消息文摘(MAC)的算法是由服务器选择的加密套接字(cipher_suite)并且在服务器问候中被反映出来。

8.1 非对称的加密算法

非对称的加密算法用于在握手协议中认证各方并且产生共享的密钥和秘密。

对于Diffie-Hellman, RSA和Fortezza来说,相同的算法被用来将预主秘密(pre_master_secret)转化为主秘密(master_secret)。当主秘密(master_secret)被计算出来之后,预主秘密(pre_master_secret)应从内存中删除。

master_secret =

MD5(pre_master_secret + SHA(`A' + pre_master_secret +

ClientHello.random + ServerHello.random)) +

MD5(pre_master_secret + SHA(`BB' + pre_master_secret +

ClientHello.random + ServerHello.random)) +

MD5(pre_master_secret + SHA(`CCC' + pre_master_secret +

ClientHello.random + ServerHello.random));

8.1.1 RSA

当服务器认证和密钥交换是使用的是RSA算法时,客户端将产生一48比特的预主秘密,并用服务器的公钥加密后发给服务器。服务器利用它的私钥解出此预主秘密,然后通信双方均按如上方法将预主秘密转化为主秘密。

RSA数字签名是利用PKCS #1 [PKCS1]的block type 1进行的,RSA公钥加密是利用 PKCS #1的block type 2进行的。

8.1.2 Diffie-Hellman

这里进行的是一传统的Diffie-Hellman加密计算。协商的密钥(Z)用作预主秘密(pre_master_secret),并按上述方法被转化成主秘密(master_secret)。

注意:Diffie-Hellman参数由服务器指定,且可以是临时的,也可以被包含在服务器的证书中。

8.1.3 Fortezza

客户端产生一随机的48比特的预主秘密(pre_master_secret)被TEK和其初始化向量IV加密后发出,服务器解出此预主秘密(pre_master_secret)并用上述方法将其转化为主秘密( master_secret)。Bulk加密密钥和初始化向量是由客户端标识(token)产生的并在密钥交换消息中被交换。 主秘密只在产生消息文摘(MAC)时才用到。

8.2 对称加密算法和加密说明(CipherSpec)

加密的验证SSL记录完整性的技术是由当前活跃的加密说明(CipherSpec)所指定的。一个典型的例子是用DES进行数据加密、用MD5产生认证码。在SSL握手协议开始时,加密算法和消息验证(MAC)算法被设为SSL_NULL_WITH_NULL_NULL,表明消息未被加密且不需认证。握手协议被用来协商一个更为安全的加密说明(CipherSpec)并产生加密的密钥。

8.2.1  主秘密(master secret )

在安全加密和完整性验证可以被用在记录之前,客户端和服务器需要产生一只有它们采知道的共享秘密。此值是一48比特的称为主秘密的字节流,主秘密被用来产生加密和消息验证码(MAC)的密钥和秘密。有一些象Fortezza那样的算法,有其自身的产生加密密钥的过程,所以主秘密仅被用在产生消息验证码(MAC)。

8.2.2 将主秘密转化为密钥和消息验证码(MAC)的秘密

主秘密被哈希函数杂凑成一系列安全的字节流,它按当前的加密说明(CipherSpec )中的要求被赋给消息验证码(MAC)秘密、密钥和初始化向量(见附录A.7)。

加密说明(CipherSpecs)由一客户端写MAC秘密(client write MAC secret),一服务器写MAC秘密(server write MAC secret),一客户端写密钥(client write key),一服务器写密钥(a server write key),一客户端写初始化向量(client write IV)和一服务器写初始化向量(a server write IV)组成,它们是按此顺序由主秘密产生的。象Fortezza算法中那样利用密钥交换消息交换的数据加密密钥,而在加密说明(CipherSpecs)中未用到的值置为空。下列是定义密钥过程的输入:

opaque MasterSecret[48]

ClientHello.random

ServerHello.random

当产生密钥和消息验证码秘密时,主秘密被用作熵,而随机数提供了未加密随机原材料(salt material)。初始化向量提供了适合输出的密码(exportable ciphers)。

为产生密钥,计算:

key_block =

MD5(master_secret + SHA(`A' + master_secret +

ServerHello.random + ClientHello.random)) +

MD5(master_secret + SHA(`BB' + master_secret +

ServerHello.random + ClientHello.random)) +

MD5(master_secret + SHA(`CCC' + master_secret +

ServerHello.random + ClientHello.random)) + [...];

直至产生了足够的输出,这样密钥块(key_block)被如下分配:

client_write_MAC_secret[CipherSpec.hash_size]

server_write_MAC_secret[CipherSpec.hash_size]

client_write_key[CipherSpec.key_material]

server_write_key[CipherSpec.key_material]

client_write_IV[CipherSpec.IV_size]            /* 非输出性密码 */

server_write_IV[CipherSpec.IV_size]           /*  非输出性密码 */

其他密钥块均被抛弃。

可输出的加密算法(即CipherSpec.is_exportable的值为真)要求如下附加的过程来得到最终的写密钥:

final_client_write_key = MD5(client_write_key +

ClientHello.random + ServerHello.random);

final_server_write_key = MD5(server_write_key +

ServerHello.random + ClientHello.random);

可输出的加密算法由消息中的随机数得到它们的初始化向量:

client_write_IV = MD5(ClientHello.random + ServerHello.random);

server_write_IV = MD5(ServerHello.random + ClientHello.random);

通过删除最不重要的字节,MD5的输出被修剪至合适的大小。

8.2.2.1输出的密钥(Export key)的产生的例子

SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5要求每一(共两个)加密密钥有5个随机的字节 、每一消息验证码(MAC)密钥(共两个)有16个随机的字节,所以共需要42个随机的字节。每一次调用MD5都将产生16字节的输出,所以共需调用3次MD5,MD5的输出连接起来形成一48字节的密钥块,其中第一次调用MD5产生第0至15字节,第二次调用提供第16至31字节等, 密钥块被分割了且由于用到了可输出的加密算法,写密钥是salted 。

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 = MD5 (client_write_key +

ClientHello.random + ServerHello.random) 0..15 ;

final_server_write_key = MD5 (server_write_key +

ServerHello.random + ClientHello.random) 0..15 ;

client_write_IV = MD5(ClientHello.random + ServerHello.random) 0..7 ;

server_write_IV = MD5(ServerHello.random + ClientHello.random) 0..7 ;

附录 A

A. 协议常量值(Protocol constant value)

本节主要描述协议的类型和常量

A.1  保留的端口值

目前SSL是利用TCP/IP做为其基本网络技术实现的。IANA为SSL连接保留下列Internet协议(IP)端口:

443  为带有SSL的超文本传输协议(HTTP)而保留的端口。

465 为带有SSL的简易邮件传输协议(ssmtp)而保留(未决的)的端口。

563 为带有SSL的网络新闻组传输协议(snntp)而保留(未决的)的端口。

A.1.1记录层(Record layer)

struct {

uint8 major, minor;

} ProtocolVersion;

ProtocolVersion version = { 3,0 };    /* 定义SSL版本号为 3.0 */

enum {

change_cipher_spec(20), alert(21), handshake(22),

application_data(23), (255)

} ContentType;

struct {

ContentType type;

ProtocolVersion version;

uint16 length;

opaque fragment[SSLPlaintext.length];

} SSLPlaintext;

struct {

ContentType type;          /* 与SSLPlaintext.type相同 */

ProtocolVersion version;   /* 与SSLPlaintext.version相同*/

uint16 length;

opaque  fragment[SSLCompressed.length];

} SSLCompressed;

struct {

ContentType type;

ProtocolVersion version;

uint16 length;

select (CipherSpec.cipher_type) {

case stream: GenericStreamCipher;

case block: GenericBlockCipher;

} fragment;

} SSLCiphertext;

stream-ciphered struct {

opaque content[SSLCompressed.length];

opaque MAC[CipherSpec.hash_size];

} GenericStreamCipher;

block-ciphered struct {

opaque content[SSLCompressed.length];

opaque MAC[CipherSpec.hash_size];

uint8 padding[GenericBlockCipher.padding_length];

uint8 padding_length;

} GenericBlockCipher;

A.2  更改加密说明消息(Change cipher specs message)

struct {

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

} ChangeCipherSpec;

A.3  报警消息(Alert message)

enum { warning(1), fatal(2), (255) }   AlertLevel;

enum {

close_notify(0),

unexpected_message(10),

bad_record_mac(20),

decompression_failure(30),

handshake_failure(40), no_certificate(41), bad_certificate(42),

unsupported_certificate(43), certificate_revoked(44),

certificate_expired(45), certificate_unknown(46),

illegal_parameter (47),

(255)

}   AlertDescription;

struct {

AlertLevel level;

AlertDescription description;

}  Alert;

A.4  握手协议(Handshake protocol)

enum {

hello_request(0), client_hello(1), server_hello(2),

certificate(11), server_key_exchange (12), certificate_request(13),

server_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_done: ServerHelloDone;

case certificate_verify: CertificateVerify;

case client_key_exchange: ClientKeyExchange;

case finished: Finished;

} body;

} Handshake;

A.4.1 Hello messages

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<0..216-1>;

CompressionMethod compression_methods<0..28 -1>;

} ClientHello;

struct {

ProtocolVersion server_version;

Random random;

SessionID session_id;

CipherSuite cipher_suite;

CompressionMethod compression_method;

} ServerHello;

A.4.2  服务器认证和密钥交换消息(Server authentication and key exchange message)

opaque ASN.1Cert<224-1>;

struct {

ASN.1Cert  certificate_list<1..224-1>;

} Certificate;

enum { rsa, diffie_hellman, fortezza_dms } KeyExchangeAlgorithm;

struct {

opaque RSA_modulus<1..216-1>;

opaque RSA_exponent<1..216-1>;

}  ServerRSAParams;

struct {

opaque DH_p<1..216-1>;

opaque DH_g<1..216-1>;

opaque DH_Ys<1..216-1>;

} ServerDHParams;

struct {

opaque r_s [128]

} ServerFortezzaParams

struct {

select (KeyExchangeAlgorithm) {

case diffie_hellman:

ServerDHParams params;

Signature signed_params;

case rsa:

ServerRSAParams params;

Signature signed_params;

case fortezza_dms:

ServerFortezzaParams params;

};

} ServerKeyExchange;

enum { anonymous, rsa, dsa }  SignatureAlgorithm;

digitally-signed struct {

select(SignatureAlgorithm) {

case anonymous: struct { };

case rsa:

opaque md5_hash[16];

opaque sha_hash[20];

case dsa:

opaque sha_hash[20];

};

} Signature;

enum {

RSA_sign(1), DSS_sign(2), RSA_fixed_DH(3), DSS_fixed_DH(4),

RSA_ephemeral_DH(5), DSS_ephemeral_DH(6), Fortezza_dms(20), (255)

} CertificateType;

opaque DistinguishedName<3..216-1>;

struct {

CertificateType certificate_types<1..28-1>;

DistinguishedName certificate_authorities<3..216-1>;

} CertificateRequest;

struct { } ServerHelloDone;

A.5  客户端认证和密钥交换消息(Client authentication and key exchange message)

struct {

select (KeyExchangeAlgorithm) {

case rsa: EncryptedPreMasterSecret;

case diffie_hellman: DiffieHellmanClientPublicValue;

case fortezza_dms: FortezzaKeys;

} exchange_keys;

} ClientKeyExchange;

struct {

ProtocolVersion client_version;

opaque random[46];

} PreMasterSecret;

struct {

public-key-encrypted PreMasterSecret pre_master_secret;

} EncryptedPreMasterSecret;

struct {

opaque y_c<0..128>;

opaque r_c[128];

opaque y_signature[20];

opaque wrapped_client_write_key[12];

opaque wrapped_server_write_key[12];

opaque client_write_iv[24];

opaque server_write_iv[24];

opaque master_secret_iv[24];

opaque encrypted_preMasterSecret[48];

} FortezzaKeys;

enum { implicit, explicit } PublicValueEncoding;

struct {

select (PublicValueEncoding) {

case implicit: struct {};

case explicit: opaque DH_Yc<1..216-1>;

} dh_public;

} ClientDiffieHellmanPublic;

struct {

Signature signature;

} CertificateVerify;

A.5.1  握手结束消息(Handshake finalization message)

struct {

opaque md5_hash[16];

opaque sha_hash[20];

} Finished;

A.6  加密套接字(CipherSuite)

在加密套接字(CipherSuite)中定义的下列值在客户端问候消息和服务器问候消息中被用到。

一加密套接字(CipherSuite)定义了一由SSL版本3.0所支持的加密说明。

CipherSuite SSL_NULL_WITH_NULL_NULL        = { 0x00,0x00 };

下列加密套接字(CipherSuite)的定义要求服务器提供一可以用来作密钥交换的RSA证书。服务器可以在证书请求消息中请求一能进行RSA或DSS签名的证书。

CipherSuite SSL_RSA_WITH_NULL_MD5 = { 0x00,0x01 };

CipherSuite SSL_RSA_WITH_NULL_SHA = { 0x00,0x02 };

CipherSuite SSL_RSA_EXPORT_WITH_RC4_40_MD5 = { 0x00,0x03 };

CipherSuite SSL_RSA_WITH_RC4_128_MD5 = { 0x00,0x04 };

CipherSuite SSL_RSA_WITH_RC4_128_SHA = { 0x00,0x05 };

CipherSuite SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = { 0x00,0x06 };

CipherSuite SSL_RSA_WITH_IDEA_CBC_SHA = { 0x00,0x07 };

CipherSuite SSL_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x08 };

CipherSuite SSL_RSA_WITH_DES_CBC_SHA = { 0x00,0x09 };

CipherSuite SSL_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00,0x0A };

下列加密套接字(CipherSuite)的定义被用来进行服务器的Diffie-Hellman认证(当然也可以做客户端认证)。其中DH指定了一由证书授权当局(CA)签署的包含Diffie-Hellman参数的服务器证书的加密套接字(cipher suite);DHE指定一临时的Diffie-Hellman参数,这些参数是由证书授权当局(CA)签署的DSS或RSA证书中的公钥签署过的。在DH或DHE之后的是所使用的签名算法。在所有的情况下,客户端的证书类型都要和服务器的证书类型相一致,且必须利用由服务器所选择的Diffie-Hellman参数。

CipherSuite SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x0B };

CipherSuite SSL_DH_DSS_WITH_DES_CBC_SHA = { 0x00,0x0C };

CipherSuite SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA = { 0x00,0x0D };

CipherSuite SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x0E };

CipherSuite SSL_DH_RSA_WITH_DES_CBC_SHA = { 0x00,0x0F };

CipherSuite SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00,0x10 };

CipherSuite SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x11 };

CipherSuite SSL_DHE_DSS_WITH_DES_CBC_SHA = { 0x00,0x12 };

CipherSuite SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA = { 0x00,0x13 };

CipherSuite SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x14 };

CipherSuite SSL_DHE_RSA_WITH_DES_CBC_SHA = { 0x00,0x15 };

CipherSuite SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00,0x16 };

下列加密套接字被用来在通信双方均没有认证的情况下进行完全匿名的Diffie-Hellman通讯,值得注意的是,这种方式是很容易受到中间人的攻击,所以这里不推荐使用这种方式。

CipherSuite SSL_DH_anon_EXPORT_WITH_RC4_40_MD5 = { 0x00,0x17 };

CipherSuite SSL_DH_anon_WITH_RC4_128_MD5 = { 0x00,0x18 };

CipherSuite SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x19 };

CipherSuite SSL_DH_anon_WITH_DES_CBC_SHA = { 0x00,0x1A };

CipherSuite SSL_DH_anon_WITH_3DES_EDE_CBC_SHA = { 0x00,0x1B };

最后是Fortezza标识(token)的加密套接字。

CipherSuite SSL_FORTEZZA_DMS_WITH_NULL_SHA = { 0X00,0X1C };

CipherSuite SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA = { 0x00,0x1D };

注意:以0xFF作为第一个字节的所有加密套接字都被认为是私有的,所以可以被用来定义局部/临时的算法。这种类型的互操作性是一个局部的问题。

注意:只有当独立的通信双方均递交了公证信后,附加的加密套接字才会被考虑实现。在公开的控制SSL的标准被承认之前,Netscape Communications公司会充当中间的注册机构 。

A.7  加密说明(CipherSpec)

一加密套接字标识了一加密说明(CipherSpec)。这些结构是SSL对话状态字的一部分。加密说明(CipherSpec)包括:

enum { stream, block } CipherType;

enum { true, false } IsExportable;

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

enum { null, md5, sha } MACAlgorithm;

struct {

BulkCipherAlgorithm bulk_cipher_algorithm;

MACAlgorithm mac_algorithm;

CipherType cipher_type;

IsExportable is_exportable

uint8 hash_size;

uint8 key_material;

uint8 IV_size;

} CipherSpec;

Appendix B

B. 词汇表

应用协议(application protocol)

应用协议是通常位于传输协议(例如TCP/IP)之上的协议,常见的应用协议有:HTTP, TELNET,FTP和SMTP。

非对称加密(asymmetric cipher)

见公钥加密(public key cryptography)。

认证(authentication)

认证是参与连接的一方决定另一方是否是它所要连接的对象的能力。

块加密(block cipher)

块加密是一将明文首先分为大小相等的块,然后再对这些块进行加密的算法。典型的块的大小为64比特。

bulk cipher

用来对大量的数据进行加密的对称的加密算法。

Cipher Block Chaining Mode (CBC)

CBC是一种加密方式,其中,每一明文块在加密之前首先与上一个明文块的加密所得的密文相异或(当所加密的密文块是第一个密文块时,它与初始化向量相异或),然后再进行加密。

证书(certificate)

证书作为X.509协议(a.k.a. ISO Authentication framework)的一部分,是由可信的证书授权当局发放的。是用来对一方进行身份验证和提供被授予方的公钥的

客户端(client)

与服务器端初始化一连接的应用实体。

客户端写密钥(client write key)

用来对客户端所写的数据进行加密的密钥。

客户端写MAC秘密(client write MAC secret)

用来对客户端所写的数据进行完整性验证的秘密数据。

连接(connection)

一连接是一提供合适类型服务的传送器(根据OSI七层协议的定义)。对于SSL 来说,这种连接是一点对点的关系,连接是临时的且每一连接均与一对话相对应。

数据加密标准(Data Encryption Standard (DES))

DES是一被广泛使用的对称加密算法,它属于块加密。

数字签名标准(Digital Signature Standard (DSS))

DSS是数字签名的标准,其中包含由NIST FIPS PUB 186所定义的、由National Institute of Standards and Technology所支持的、由美国商业部在1994年五月出版的数字签名算法。

数字签名(digital signatures)

数字签名利用公钥加密算法和单向哈希函数产生一可以被验证的数据的签名。且此签名a signature of the data that很难被伪造和复制。

Fortezza

一同时提供加密和数字签名的PCMCIA卡。

握手(handshake)

在客户端和服务器之间初始化协商它们之间的连接参数的过程。

初始化向量(Initialization Vector (IV))

当使用CBC方式的块加密时,初始化向量与明文的第一块进行异或后再进行加密。

IDEA

由Xuejia Lai和JamesMassey所设计的64比特的对称加密算法。

消息验证码(Message Authentication Code (MAC))

消息验证码是一消息及相关的秘密数据的单向哈希值 。它的目的是防止消息在传输过程中被篡改。

主秘密(master secret)

被用来产生加密用的密钥、MAC秘密和初始化向量的安全的秘密数据。

MD5

MD5 [7] 是一将任意长的数据转化为固定长度的数据流的安全哈希算法。

公钥加密算法(public key cryptography)

一由两个密钥组成的加密技术,由公钥加密的消息仅能由相应的私钥解出;同样的,由私钥签署的消息仅能由相应的公钥进行验证。

单向哈希函数(one-way hash function)

是一将一任意长度的数据转化为一固定长度的哈希值的单向转换过程。它的逆过程和找到冲突的可能性必须是计算上不可行的。MD5和SHA是单向哈希函数。

RC2, RC4

由RSA Data Security, Inc所专有的bulk加密算法(由于他们没有出版他们的工作成果,所以有参考很少,只能见[RSADSI])。RC2是一块加密算法而RC4是一流加密算法。

RSA

可以用来加密也可以用来签名的被广泛使用的公钥加密算法.

salt

用来使可输出的加密密钥对预计算攻击(precomputation attack)有一定的防卫能力的非加密的随机数。

服务器(server)

服务器是对客户端的连接请求进行应答的应用实体。服务器被动的等待客户端的请求。

session

一SSL对话是客户端和服务器之间进行的一次联系。对话是由握手协议产生的。对话定义了一系列可以由多个连接所共享的加密参数。利用对话可以避免为每一连接协商新的加密参数。

session identifier

是由服务器产生的用来标识一特定的对话的一标识值。

服务器写密钥(server write key)

是用来对服务器所写的数据进行加密的密钥。

服务器写MAC秘密(server write MAC secret)

是用来验证服务器所写的数据的完整性的密码数据。

SHA  安全哈希算法(Secure Hash Algorithm)是在IPS PUB 180-1 中定义的,它将产生一20比特的输出。

流加密stream cipher

是一加密算法,此算法先将密钥转化为一与加密相关的密钥流, 然后将此密钥流于明文进行异或(Ored)。

对称加密(symmetric cipher)

见bulk cipher。

发表评论