SSL握手协议位于SSL记录协议之上。它允许客户端和服务器互相认证,协商cipher suites和压缩方法。
上图中的方括号里的消息是可选的,不总是被发送。ChangeCipherSpec实际上不是SSL握手协议的消息。SSL握手协议包含四组消息-叫flights-在客户端和服务器之间交换。一个flight的所有消息可以在一个TCP段内传输。也可能包含一个叫做HelloRequest的第五个flight(type值是0x00),它可能从服务器发送给客户端,初始化一个SSL握手。很少使用这个消息。消息必须以这样的顺序发送,否则返回致命错误。
这样,握手完成了。客户端和服务器开始使用SSL application data protocol交换应用层数据。
大多数SSL session从握手开始,交换一次应用数据,等一会就结束了。如果需要交换更多数据(同一个客户端和服务器之间),有两种可能行:或者是一个全的握手协商一个新的session,或者是一个简单的握手继续一个旧的(先前建立的)session。
SSL协议允许客户端在任何时间简单地发送一个ClientHello消息,请求session重新协商。如果服务器想重新协商,就发送HelloRequest消息。
比如,一个web服务器的大部分文档都可以匿名请求,但是有一部分资源需要基于证书的客户端认证,服务器就可以重新协商一个连接。
如果顺序号太大,也可以重新协商。
客户端发送一个带有session ID的ClientHello消息,服务器就在它的session缓存里查找,如果找到了,服务器就重新建立连接,返回包含该session ID的ServerHello。如果找不到,就需要完整的握手。
SSL握手的每一个消息,使用一个字节的type属性开始,3个字节的length属性说明消息的长度。多个握手消息可以放在一个SSL记录里。SSL记录的前五个字节是头。头的第一个字节是22,表示握手协议,下来的两个字节是版本,最后两个字节是整个SSL记录的剩余部分的长度。
SSL握手的时候,客户端发给服务器的第一个消息。一般,它是SSL握手的开始。它开始于通用的5字节的SSL记录头,type属性是1,3字节的length属性。
它的消息体是:
为了向前兼容,compression_methods后面可以跟随其他属性。这些数据必须包含在握手hash里,否则被忽略。
收到ClientHello消息,服务器开始处理和验证,返回ServerHello。
ServerHello的结构和ClientHello类似,服务器只返回一个cipher suite和一个压缩方法。服务器只能从客户端的选项中选一个,放到session里使用。
大多数key交换方法都不是匿名的,这意味着服务器必须使用公钥证书向客户端认证自己(除了DH anon)。因此,服务器向客户端发送完ServerHello消息,马上发送Certificate消息(在同一个flight内)。同样,在SSL握手的时候,当服务器发送CertificateRequest消息,客户端就要回复Certificate消息。不论如何,Certificate消息传送公钥证书,或者是公钥证书链(certificate_list)。公钥证书链以发送者的证书开始,然后是一系列的CA证书,向上一直到根CA证书。证书类型必须适合使用的key交换算法。一般是X.509证书。
Certificate消息以SSL记录头开始,然后是type属性,3字节的length属性和实际的证书链。
每个证书也以3字节的length开始。Certificate消息可能很长。
如果使用RSA key交换,客户端可以从服务器证书里检索公钥,以及使用这个公钥加密pre master secret。类似地,如果使用fixed Diffie-Hellman key交换,客户端可以从服务器证书里检索Diffie-Hellman参数。采用这些参数执行Diffie-Hellman key交换,把结果当作pre master secret。
这两种情况下,服务器的Certificate消息足够(不需要附加信息)客户端安全地把re master secret发给服务器。特别是,不需要ServerKeyExchange消息。
其他时候,客户端需要附加信息,必须由服务器发送ServerKeyExchange。
key交换算法不同,ServerKeyExchange消息格式也不一样。
前两种情况,ServerKeyExchange可以包含签名部分。如果服务器认证不是SSL session的一部分,就不需要签名部分,ServerKeyExchange消息以Diffie-Hellman参数或者RSA参数结束。如果服务器不是匿名的,已经发送了Certificate消息,签名的参数格式依赖服务器证书里的签名算法(RSA或者DSA)
hash函数的输入是ClientHello.random、ServerHello.random和上面提到的服务器key参数。
非匿名的服务器可以要求客户端认证。这个消息不只是要求客户端发送证书,还包括客户端证书类型的信息。
CertificateRequest消息包含可接受的证书类型列表,还包含服务器可接受的CA列表。
该消息可能很长。
客户端发送的消息。提供了客户端侧加密材料,让服务器在后来的安全通信中使用。消息体和使用的key交换算法相关。
如果服务器收到ClientKeyExchange消息,它使用私钥解密pre_master_secret,或者使用自己的Diffie-Hellman参数计算pre_master_secret。
如果客户端提供的Certificate消息里有带签名的证书,它必须证明自己拥有相应的私钥(证书不能证明客户端的身份,因为证书可以被窃听和重播)。客户端发送CertificateVerify消息,该消息包含的数字签名由客户端的私钥生成。
hash值是这样计算的:
h(k || opad || h(handshake messages || k || ipad))
h是指MD5或者SHA-1,k是master secret。
Finished消息总是在ChangeCipherSpec消息之后马上发送。该消息用来验证key交换和认证过程胜利结束。它是使用新的协商算法和key保护的第一个消息。不需要确认,之后可以立刻发送加密数据。
Finished消息的消息体是加密保护的,由type、length、16字节的MD5 哈是值、20字节的SHA-1 hash值,16或者20字节的MAC。MD5和SHA-1 hash值依赖key和实际的MAC。这里的MAC和SSL记录里计算的MAC不同,附加在消息里。
MD5和hash值这样计算:
h(k || opad || h(handshake messages || sender || k || ipad))
其中,sender是发送消息的实体。如果是客户端发送的,值是0x434C4E54,否则是0x53525652。