SSL 握手.docx

上传人:b****6 文档编号:5918446 上传时间:2023-01-02 格式:DOCX 页数:12 大小:73.68KB
下载 相关 举报
SSL 握手.docx_第1页
第1页 / 共12页
SSL 握手.docx_第2页
第2页 / 共12页
SSL 握手.docx_第3页
第3页 / 共12页
SSL 握手.docx_第4页
第4页 / 共12页
SSL 握手.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

SSL 握手.docx

《SSL 握手.docx》由会员分享,可在线阅读,更多相关《SSL 握手.docx(12页珍藏版)》请在冰豆网上搜索。

SSL 握手.docx

SSL握手

第二章.对SSL握手协议的研究(part-1)

SL握手过程即完成身份认证和建立加密通道的过程,分为四种。

――FullHandshake:

全流程握手,C/S双方从无到有建立SSL连接;

――ResumsessionHandshake:

C/S双方曾经建立过连接,但中途断了,SSL会话信息还有保留,只需要执行部分握手流程就可建立SSL连接;

――ServerRe-negotiationHandshake:

已经建立了SSL连接,但server端为了某些原因(比如安全性)要求重新对密钥进行协商,也只需要执行部分握手流程;

――ClientRe-negotiationHandshake:

已经建立SSL连接,但client端为了某些原因要求重新协商,只需执行部分握手流程。

FullHandshake

这里主要介绍全流程握手,握手步骤如下图所示,其他的握手过程都属于全流程的子集。

SSL握手协议总共有10中消息类型,类型名和枚举值如下:

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)

struct{

HandshakeTypemsg_type;/*握手消息类型,如上所述的枚举值*/

uint24length;/*握手消息体的长度用24位表示,即最大长度不超过16M*/

HandShakeMsgpayload/*握手消息的有效荷载,上面列的10类型之一*/

}Handshake;

Struct

{

ProtocolVersionclient_version;

Randomrandom;

SessionIDsession_id;

CipherSuitecipher_suites<0..216-1>;

CompressionMethodcompression_methods<0..28-1>;

}ClientHello;

struct{

ProtocolVersionserver_version;

Randomrandom;

SessionIDsession_id;

CipherSuitecipher_suite;

CompressionMethodcompression_method;

}ServerHello;

选择SSL通信,当底层连接建立好后会触发或调用SSL的初始化和握手,握手由Clinet端发出ClientHello消息开始

客户端首先发ClientHello消息到服务器端,服务器端收到hello消息后再发Serverhello消息回应客户端。

ClientHello和ServerHello消息结构如下:

 

 

C代码

1.struct {  

2.ProtocolVersion client_version;  

3.Random random;  

4.SessionID session_id;  

5.CipherSuite cipher_suites<0..216-1>;  

6.CompressionMethod compression_methods<0..28-1>;  

7.} ClientHello;  

8.  

9.struct {  

10.ProtocolVersion server_version;  

11.Random random;  

12.SessionID session_id;  

13.CipherSuite cipher_suite;  

14.CompressionMethod compression_method;  

15.} ServerHello;   

struct{

ProtocolVersionclient_version;

Randomrandom;

SessionIDsession_id;

CipherSuitecipher_suites<0..216-1>;

CompressionMethodcompression_methods<0..28-1>;

}ClientHello;

struct{

ProtocolVersionserver_version;

Randomrandom;

SessionIDsession_id;

CipherSuitecipher_suite;

CompressionMethodcompression_method;

}ServerHello; 

选择SSL通信,当底层连接建立好后会触发或调用SSL的初始化和握手,握手由Clinet端发出ClientHello消息开始。

ClientHello消息

ProtocolVersion:

消息中协议版本是两个byte长度分别表示主次版本,如若在JAVA中初始化SSLContext时候选择了SSLv3则主版本号是3,次版本号为0,若选择了TLSv1(TLSv1相当SSLv3的升级版)则主版本号是3,次版本号是1。

Random:

随机数结构,由两部分组成,

 

C代码

1.struct {  

2.   uint32 gmt_unix_time;  

3.   opaque random_bytes[28];  

4.} Random;   

struct{

uint32gmt_unix_time;

opaquerandom_bytes[28];

}Random; 

一个4字节的系统当前时间,一个28位长的随机数,在后面计算所有消息的摘要或计算主密钥时候会用到。

(疑惑1:

在java的JSSE实现版本中貌似只有一个4字节的系统当前时间,没有28位的随机数,奇怪了,怎么和非JSSE版本实现正确握手的啊…)

SessionID:

SSL会话ID标识一次会话用,可以重用。

会话ID都是由服务器分配因此在全流程握手中clienthello消息中的sessionid是空,用字节0表示。

CiphersuitList:

密钥套件列表,列表中包含了Client端支持的所有密钥套件。

一个密钥套件定义了一个密钥规格,其中描述如下内容:

密钥交换算法,是否出口,对称加密算法,支持的最高对称密钥位数,MAC算法(或摘要算法)。

一个ciphersuit用2个字节表示,下面列举JSSE支持的几个套件:

SSL_RSA_WITH_RC4_128_MD5=0x0004/*非对称加密算法或密钥交换算法为RSA,采用高强度128位对称加密算法RC4,摘要或MAC算法为MD5,不支持出口*/

SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA=0x0014/*非对称加密算法或密钥交换算法支持RSA和DH,采用40位对称加密算法DES,摘要或MAC算法为SHA,可以出口*/

关于出口,美帝国主义要求要用他们的加密算法比如JDK中的JSSE,对称加密密钥长度不能超过40,非对称密钥长度不超过512,在国内只能使用这种低强度的玩意,赤裸裸的技术封锁啊。

这个加密套件列表长度不超过128K。

CompressionMethods:

客户端支持的压缩算法列表,填0表示空,JSSE会有默认算法。

ClientHello承载着这些信息被发送到Server端。

∙ServerHello阶段

ServerHello消息

ProtocolVersion:

服务端服务器拿出消息中的版本号,再看看自己支持的版本列表,选个两者都支持的最高版本号定为这次协商出来的SSL协议使用的版本。

比如C端发过来ssl3.0,而S端发现自己只支持ssl2.0,server就会选择SSL2.0作为这次协商版本,反之若Server支持SSL2.0,SSL3.0,TLS1.0,选两者都支持的最高版本SSL3.0。

Random:

产生的方式和ClientHello中相同。

SessionID:

服务端检测到传过来的sessionID是空或者检索session列表没有发现传过来的sessionid就会新建一个,JSSE中SessionID是取系统时间的前32位(系统时间是long类型64位,只取其中32位)。

Ciphersuit:

server端收到密钥套件列表后,将密钥套件一个个拿出来,经过几道检查,选择第一个通过检查的套件。

JSSE实现的SSL协议中的检查项目有如下几项:

检查服务端是否也支持这个套件,检查这个套件是不是被禁用(的确是支持某个套件,但出于某些原因被禁用了),检查套件是否符合出口限制,比如在国内加密强度128或256位的对称密钥在检查时候就会被认为不合法pass掉;若server端启用了双向认证,某些不支持双向认证的套件就会被pass掉。

层层选拔,第一个通过的幸运儿光荣的成为密钥协商的成果,被放入ServerHello消息中。

这样ServerHello消息组装好了,发出去告诉客户端协商的SSL版本和加密套件,并创建了一个会话,hello阶段结束。

服务发送证书(ServerCertificates可选)消息

在服务器发送完hello消息后接下来可以发送3个可选消息,服务器证书消息,服务器证书交换消息,客户端证书请求消息。

服务器证书消息在全流程握手中一般是必须发的(疑惑2:

不知道除了session重用外在什么情况下服务器端不需要发送证书过去?

),但是在会话重用的消息中就不需要再发。

服务器证书中包含公钥,发给客户端用来验证签名或在密钥交换时候给消息加密。

证书消息是紧跟着ServerHello消息发送,证书消息中就是一个证书列表,证书应该转换成ASN.1DER格式,不支持PKCS7格式。

证书链中的证书挨个取下来放入列表中,按照次序服务器自己的证书放列表最前头,根CA证书放列表最后,证书链长度不超过16M。

服务端密钥交换消息(ServerKeyExchange可选)

说到这个不得不介绍下SSL密钥交换方式。

安全加密通信是发送方将信息加密,接收方将信息解密,加解密用的密钥分两类:

对称和非对称加密(具体概念google之)。

非对称加密的最大优点是可以将一部分密钥公开,叫公钥(publickey),这样通信双方交换密钥很简单,A和B通信,A有一个密钥对Pri-A和Pub-A,B也有一个密钥对Pri-B和Pub-B,AB互相交换自己公钥,A用Pub-B加密要发给B的消息,经过加密的消息就算被第三方窃取由于只有B有和Pub-B对应的私钥Pri-B,因此只有B才能解开消息,其他没有对应私钥的无法获得有效信息,B如果要向A发送消息同理也用Pub-A加密消息。

 

照这个办法这个安全通信的加密问题是否圆满解决了呢,世上痛苦的事十之八九,答案当然是否定的,非对称加密有致命的缺点就是加解密效率太低,不能应用在实际通信中应用,而对称加密的特性正好和非对称加密相反,加解密都用同一个密钥,效率高,但通过网络交换密钥又比较困难,万一被哪个不安分的偷了去那这个加密信道对他来说形同虚设了。

这时一个聪明的办法就是在SSL握手期间用非对称密钥加密对称密钥发送到对方,接收方有私钥将消息解密,获得对称密钥,等握手结束后再使用已经交换好的对称密钥来家解密,这样即解决了非对称加密低效的问题又解决了对称密钥难交换的问题,问题终于圆满解决了。

聪明的人不止一个,又有种优秀的密钥交换算法被整出来了,DH(DiffieHellman)算法,一种专门用来交换密钥的算法,简单的讲就是在通信前,A和B双方约定2个大整数p和g,其中1

而第三方只获得p,g,Ya,Yb是无法算出Xa,Xb和密码的,经过计算这样A,B各自算出了共享的对称密钥再接下来的通信中对数据加解密。

再啰嗦一点有关对称密钥生成方面的细节,对称密钥并不是直接生成的,是客户端产生一个预主密码(premaster),然后用密钥交换算法交给服务器端,两端根据这个预主密码计算出主密码,再用主密码生成对称密钥。

过程比较曲折。

对于RSA的密钥交换方式,预主密码是一个48位的随机数,而DH算法的预主密码相当于上边用Xa,Yb或Xb,Ya产生的那个共享密码。

接下来的主密码生成方式和交换算法无关了,RSA和DH的都一样了,相当于密钥交换实际上是在交换那个Pre-master。

(疑惑3:

为什么要搞出个预主密码和主密码来啊,客户端直接生成一个可用的对称密钥发给服务器端不就得了吗,折腾的,这点搞不懂,大牛指点一下啊)

稍微介绍了下背景知识,回归正题,既然密钥交换算法有很多种那SSL握手期间用哪种呢,这个就是之前由选择的ciphersuit决定的,比如选择的是SSL_RSA_WITH_RC4_128_MD5=0x0004,那就是RSA的密钥交换算法即用非对称加密对称将密钥传送到对方,若选择的是SSL_DHE_RSA_EXPORT….那就使用DH交换算法。

对不同的交换算法发过去的消息结构也会不一样,下面主要介绍RSA和DH两种方式的密钥交换。

RSA方式的密钥交换:

在这种方式下,实际上服务器端的这个消息不是必须发送的,可选的。

这个消息中包含了一个RSA公钥,公钥用两个参数表示,称为模数和指数。

公钥已经在发送服务证书时候包含在证书里头交给客户端了,为啥还要发公钥过去啊?

这个有如下几个原因:

  Reason1.之前证书中的包含的是DSS(DSA)公钥,此类公钥只能拿来签名,不能拿来加密,因此需要server端生成一对非对称密钥用来临时加密用,并将公钥塞在ServerKeyExchange消息中发送过去。

  Reason2.山姆大叔搞技术封锁,设置加密算法的出口限制,他规定RSA加密算法密钥长度不能超过512,如果超过512只能拿来签名,不能拿来加密。

因此那些证书里头的RSA公钥长度超过512的不得不重新生成一对512长度的临时加密用的密钥,并将RSA公钥塞进消息体发出去。

 Reason3.。

我只知道上面两个原因,不排除还有其他的

这样说起来的话如果哥身处美国,不受出口限制,或哥证书中的公钥长度本来就没超过512就完全没必要发这个消息啦~因此这个消息是可选的,不是必须发的。

DH方式密钥交换

用DH算法产生整数p,g,和server端的Ys,将这三个参数塞进消息体。

为了防止消息被恶意篡改,ServerKeyexchange消息中还要包含一个对密钥参数的签名。

请求客户端证书消息(可选)

如果是SSL的双向认证的话,服务器端还会发出clientcertrequest消息,要求客户端发他自己的证书过来验证。

使用JAVAJSSE,如果有如下设置说明启用了双向认证:

Java代码

1.SSLServerSocket.setNeedClientAuth(true)   

SSLServerSocket.setNeedClientAuth(true)

此消息包含两部分内容:

一个是server端支持的证书类型(RSA,DSA,ECDSA等。

),另一部分是server端所信任的所有证书发行机构的DN(DistinguishedName)列表,客户端会用这些信息来筛选证书,以后会讲到。

 

服务端ServerHelloDone消息

这个消息没有什么内容,表示服务器刚才说了那么多,要说的都说完了,等着客户端回应了。

第三章.对SSL握手协议的研究(part-2)

∙Client处理和回应ServerHello阶段

客户端收到服务器发过来的那些消息,要做的是验证服务器证书,发送自己的证书(如果双向认证),发送计算出的预主密码,发送证书验证消息。

收到ServerHello

在serverhello阶段连续的发了多个消息,最先发出的是serverhello,client收到后将SSL会话ID,服务器端的一个随机数,协商出的SSL协议版本号以及密钥套件放到会话缓存中。

收到ServerCertificates

接下来收到的应该是server端的证书消息了,取出所有的消息,最头上的是server证书,最末端的CA根证书。

在SUNJDKJSSE中实现的SSL是这样处理的:

从SSLContex中取出trustmanager,如果用过javaSSL编程的同学应该知道,可以自己实现X509TrustManager和X509KeyManager两个接口来定制对证书的验证,sunjdk中就是调用这个trustmanager的验证方法来验证证书(在解决浏览器检测到证书是自签名的时候跳出警告框的问题就是可以自己实现这个trustmanager来让浏览器不跳出警告框)。

 

JDK当然也有默认的证书验证实现,就是验证签名有效性,验证证书是否过期等。

证书签名的有效性验证是在取证书链中某证书中的公钥验证前一个证书的签名,这样第一个证书就是用第二个证书的公钥来验证,那有人可能会问最后的根CA的证书谁来验证?

根CA的证书是自签名的,就是自己给自己签名,没人管的了,JDK中某个文件中有存储一堆可信任的证书发行机构的证书列表,如果你的根CA在那个列表中并对比后确实是那个根CA的证书那就OK,验证通过,这个可信任的机构也可以自己实现那个trustmanager来添加设置。

 

收到ServerKeyExchange消息

RSA方式密钥交换消息则把消息中的加密用公钥放入会话缓存中,作为客户端这边握手阶段的写密钥而不是用服务器证书中的公钥。

DH方式的消息就把消息中的p,g,Ys三个参数记录下来,有这些client端就可以计算出pre-master了,只要回头再把自己这边的Yc参数发过去,server端就也能计算出相同的pre-maseter了。

 

收到serverCerttificatsRequest消息

将消息中的证书类型列表和可信任证书发行机构列表保存下来,可在后面发送客户端证书时候拿来筛选证书用。

 

收到ServerHelloDone消息

收到这个消息后client端开始向server发消息了

发送ClientCertificates消息

如果是server端要求客户端认证就会发这个消息,否则不发。

客户端可能会有多个证书,在JSSE里头多个客户端证书存储在keystore里头,选哪个发过去呢?

这时候要用到server端之前发的certrequest消息中的支持的证书类型列表和信任的根CA列表,满足这个两个条件的第一个证书链就会被选中作为客户端证书。

发送ClientKeyExchange消息

若是RSA方式密钥交换,则产生一个48位随机数作为pre-master并用服务器公钥加密后发出去

若是DH方式的密钥交换,则根据sever的g,p,Ys,产生Xa和Yc,Xa和Ys能计算出pre-master,把产生的Yc放入消息中发给server,这样server用它的Xb和Yc也能计算出pre-master了。

计算出预主密码后就顺便把主密码(mastersecret)给算出来了。

算出主密码就把对称密钥产生出来了。

发送Certificateverify消息

这个消息是可选的,只有在客户端发送了自己证书到服务器端,这个消息才需要发送。

发这个消息的目的是让服务器验证发消息的客户端和客户端证书的真实所有者。

这个消息中要包含一个签名,签名里头内容就是从clienthello开始到目前为止所有握手消息(不包括本消息)的摘要,主密码,若是RSA方式则要把这些内容分别用MD5和SHA1计算一遍,两种摘要算法算得的摘要拼接起来用客户端证书中公钥对应的私钥加密就获得了签名。

到时候服务器端会用收到的证书中的公钥来验证签名。

发送changecipherspec消息

发送这个消息,然后把session的写密钥设置成计算得到得对称密钥,从此消息之后再发送消息就会用这个写密钥来加密消息。

发送clientFinished消息

Client端的Finished消息一般都是紧随changecipherspec消息发送出去,标志着本方的SSL协商成功结束。

消息中包含两个个摘要,是分别用MD5和SHA算法计算当前收到所有握手消息和主密码的摘要,不包括本消息和changecipherspec(因为不属于握手消息)。

该消息是SSL握手中的最后要互传的消息,包含一个所有握手消息的摘要值,这是为了防止中间人将强度较大的CipherSuite在clienthello消息中删除,使得server不得不选择强度较小的CipherSuite,然而这非client所愿。

问题是这个摘要不可以被中间人更改吗?

想象一下这时共享对称密钥已经协商好了,ChangeCipherSpec已经经过,所以这些消息本身是加过密的。

∙       Server处理和回应cleintFinished阶段

Server收到client的证书链后验证证书,并验证certificatieverify中的签名,验证通过了也就确认了client的身份,如假包换。

收到clientkeyexchange消息从中拿出Pre-master计算出master,生成对称密钥。

收到client的changecertspec后将会话的读密钥设置为刚产生的对称密钥。

处理完这些server会发送自己的changecipherspec消息并把会话的写密钥设置为生成的对称密钥,最后发送serverFinished消息,client端收到server端的changecipherspec消息将会话的读密钥设置为生成的对称密钥。

到此握手过程圆满结束,接下来的应用消息将使用设置好的读写密钥对数据加解密。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1