《图解HTTP》——HTTPS协议 (部分涉及《密码编码学与网络安全 原理与实践 第6版》第17章内容)

HTTP的优点在于便捷,但是其缺点也十分明显。

其不足主要如下:

  通信使用明文(不加密),内容容易被窃听

  不验证通信方的身份,因此有可能遭到伪装

  无法证明报文的完整性,所以有可能已遭篡改

缺点一、 通信使用明文可能会被窃听

  由于在互联网中,在任何角落都有可能存在通信被窃听的风险。窃听相同段上的通信只需要收集在互联网上流动的数据包(帧)就可以了。一般我们可以通过一些抓包(Packet Capture)或嗅探器(Sniffer)工具就可以实现(例如:Wireshark)

  当然窃听对于明文和密文是一视同仁的,也就是说加密本身并不能防止数据帧被他人获取。

缺点二、不验证通信方的身份就可能遭到伪装

  因为HTTP协议中的请求和响应不会对通信方进行确认。也就是说无法确认通信双方是否是真正想要进行通信的对象。

  因为无需确认HTTP请求方的身份,所以无论是谁发来的请求都会返回响应。

  因此会来带一系列的隐患,比如不确定双方身份,隐秘的重要信息泄露,遭受DoS攻击等等。

缺点三、无法证明报文的完整性,可能被篡改

  接受的内容没有验证其真实性,因此有可能中途被更改,不如HTTP首部注入攻击,就可能改变主体的内容。

因为HTTP存在这一系列的问题,所以我们提出了一种更加安全的协议模式:HTTPS

HTTPS是什么?

  HTTP + 加密 + 认证 + 完整性保护 = HTTPS

  HTTPS实际上就是披着SSL外衣的HTTP,HTTPS并非一种应用层的新协议,只是HTTP通信接口部分用SSL(Secure Socket Layer)和 TLS(Transport Layer Security)协议代替。

也就是说其实HTTPS的加密、认证以及完整性保护都是由SSL提供的。

首先我们来解决HTTP存在的第一个缺点:明文

  我们知道加密技术一般分为两种:

    1. 对称加密技术

    2.非对称加密技术

  我们先来聊聊对称加密技术,也就是公开密钥加密(Common key crypto system),比较常见的对称加密算法有: DES算法,3DES算法,TDEA算法,Blowfish算法、RC5算法,IDEA算法。

  对称加密算法可以简单理解为加密和解密用的是同一把钥匙。如DES算法,其具体实现可以参考这篇博客:https://www.cnblogs.com/myworld7/p/10596869.html

  但是显然单纯的使用对称加密算法并不能解决问题,因为客户端和服务器都需要相同的密钥才能实现加密和解密,那么势必需要在网络上传递共享密钥,那么攻击者同样能获得这个公开密钥,进而解密信息。

  于是我们需要考虑一种更为安全的方式去进行加密,考虑使用非对称加密技术(Asymmetric cryptographic system), 比较常见的非对称加密算法有:RSA算法、Elgamal算法、背包算法、Rabin算法、D-H算法、ECC(椭圆曲线加密)算法。

  非对称加密算法可以理解为一方生成了两把不同的钥匙,一把是公钥会被交给用户,来对需要加密的数据进行加密,另一把是私钥,用来对被加密的信息进行解密。由于私钥不需要上传至网络,所以攻击者无法通过窃听的手段获取到私钥。保证了加密信息的安全性。

  但是为什么SSL并没有直接采用非对称算法,而是将对称和非对称算法进行混合使用呢?这是因为非对称加密技术处理的速度比对称加密技术要慢上许多。于是,我们考虑通过非对称加密技术来传递对称加密技术生成的共享密钥,通过这种方式传递的共享密钥无法被攻击者获取,进而保证了共享密钥的安全性。在此之后的通信就可以使用共享密钥来完成加密。

  我们通过混合加密机制保证了明文的安全性,解决了HTTP存在的第一个问题,然后我们将继续解决其第二个不足,也就是认证。

  虽然通过混合加密机制保证了信息的安全,但是我们无法保证信息的来源是否是我们真正想要通信的对象,因此我们需要制定一个合理的方案来解决这一问题,于是我们设立了数字证书认证机构(CA Certificate Authority)和其相关颁发的公开密钥证书。

  数字证书机构处于客户端与服务器双方都可信赖的第三方机构的立场上。认证流程如下:

  1.服务器运营者向数字证书认真机构提出公开密钥的认证申请

  2.在判定申请者身份后,对申请的公开密钥做数字签名,然后分配这个已签名的公开密钥

  3.将公开密钥放入公开证书后绑定在一起,发送给客户端

  客户端在接收到数字证书之后,可以通过数字认证机构的公开密钥,对数字签名进行验证,一旦验证通过,客户端就可以确认:认证服务器的公开密钥是真实有效的数字认证机构;服务器的公开密钥是值得信任的。

  为了保证认证机构的公开密钥能安全地交给客户端,所以一般采取在浏览器开发商发布版本时,就事先在内部植入认证机构的公开密钥。

  客户端同样可以通过这种方式来获得客户端证书,现实上来讲一般为网上银行等。

  需要注意的是,认证机构的信誉是第一位的,必须保证认证机构的安全可靠性。

最后一点是对数据完整性的验证,一般是通过散列算法来完成的,一般常见的散列算法有MD5、SHA-1。

下面我们看看SSL是如何建立链接的。

  SSL的工作原理主要分为三部分:

  1. 握手协议(Handshake protocol)

  2. 纪律协议(Record protocol)

  3. 警报协议(Alert protocol)

  首先是握手协议:

  是客户机和服务器用SSL通信使用的第一个子协议,也是最为复杂的一个协议。主要是让服务器和客户机相互验证,协商加密算法、MAC算法以及保密密钥,用来保护SSL记录发送的数。握手协议是在应用程序的数据传输之前使用的。

  每个握手协议包含三个字段

  1. Type : 表示10种消息类型之一(1字节)

  2. Length: 表示消息长度字节数(3字节)

  3. Content: 表示消息相关的参数(大于等于0字节)

  

 

  而握手协议又可以细分为 4 个阶段:

  1. 第一阶段:建立安全能力

    该阶段主要分为两步,第一步是客户端向服务器端发出client hello消息并等待服务器响应,随后服务器会向客户端返回server hello消息,对client hello 中的内容进行确认。

    

     此阶段用于建立初始的逻辑链接,并建立与之相关联的安全功能,客户端发起这个交换,发送具有如下参数的client_hello message。

    版本:客户端所支持的最高SSL版本

    随机数:由客户端生成的随机数结构,用32位时间戳和一个安全随机数生成器生成的28字节随机数组成。这些值作为随机数,在密钥交换时防止重放攻击。

    会话标志:一个变长的会话标志。非0值意味着客户端想更新现有连接的参数,或为此会话创建一个新的连接;0值意味着客户端想在新会话上创建一个新连接。

    密码组:按优先级的降序排列的、客户端支持的密码算法列表。表的每个元素定义了一个密钥交换算法和一个密码说明。每个套件都是以“SSL”开头,紧跟着的是密钥交换算法。用“With”将密钥交换算法、加密算法、散列算法分开,例如:SSL_DHE_RSA_WITH_DES_CBC_SHA,表示将DHE_RSA(带有RSA签名的暂时Diffie-HellMan)定义为密钥交换算法;将DES_CBC定义为加密算法;将SHA定义为散列算法。

    压缩方法:一个客户端支持的压缩方法列表

    在此之后,服务器会根据客户端发来的client_hello message来进行回应。客户端将会收到参数相同的server_hello message。

    版本:包含客户端支持的最低版本和服务器支持的最高版本号。

    随机数:同客户端随机数生成方式一致,但数域相互独立。

    会话标志:如果为客户端为非0,则需要与客户端保持一致,否则,服务器会话标志域包含新对话的值。

    密码则:密码组域包含着服务器从客户端所给密码组中选出的密码组。

    压缩方法:包含着服务器从客户端所给密码组中选出的密码组。

    在这阶段过后,客户端和服务器会知道如下内容:

    1. SSL版本

    2. 密钥交换、信息验证和加密算法

    3. 压缩方法

    4. 有关密钥的两个随机数

  2. 第二阶段:服务器认证和密钥交换

    

   如果需要进行验证,则服务器开始发送它的证书:消息包含一个或一组X.509证书。除匿名Diffie-Hellman方法外,其他密钥交换方法均需要证书消息certificate message。

  接着,如果需要,可以发送服务器密钥交换消息(server_key_exchange message)。在以下两种情况下不需要此消息:

  1. 服务器发送了带有固定Diffie-Hellman参数的证书

  2. 使用RSA密钥交换。

  以下情况需要发送server_key_exchange消息

    匿名Diffie-Hellman:消息内容包含两个全局Diffie-Hellman值和服务器Diffle-Hellman公钥

    瞬时Diffie-Hellman:消息内容包含三个Diffie-Hellman参数,包括匿名Diffie-Hellman中的两个参数和它们的参数签名。

    RSA密钥交换,服务器在使用RSA时仅用了RSA签名密钥:因此客户端不能简单地通过服务器公钥加密其密钥后传送,而服务器必须创建一个临时RSA公钥/私钥对,并使用服务器密钥交换消息发送公钥。消息内容包含两个临时的RSA公钥参数(指数和模)和参数签名。

    Fortezza

    签名的细节得以保证。通常情况下,通过对消息使用散列函数并使用发送者私钥加密获得签名。在此,Hash函数定义如下: 

hash(ClientHello.random || ServerHello.random || ServerParams)

    Hash不仅包含Diffie-Hellman或RSA的参数,还包含两个初始hello消息中的随机数,可以防止重放攻击和伪装。对DSS签名使用SHA-1算法;对RSA签名,将要计算MD5和SHA-1,再将两个Hash结果串接(36字节)后,用服务器私钥加密。

  在这之后服务器端可以选择是否要求客户端同样提供验证信息(证书)。如果需要则会提出certificate_request message。其中包含两个参数:证书类型和签证机构。证书类型表明了公钥算法和它的用途:

    RSA:仅用于签名

    DSS:仅用于签名

    固定Diffie-Hellman的RSA:此时,发送RSA签名证书,其签名仅用于认证。

    固定Diffie-Hellman的DSS:仅用于认证

    瞬时Diffie-Hellman的RSA

    瞬时Diffie-Hellman的DSS

    Fortezza

    签证机构(第二个参数)是一个可接受的签证机构名字表。

  在第二阶段最后一步,服务器会发送一个server_done message,用来表明服务器的hello和相关消息结束,并等待客户端应答,此消息不带参数。

  3. 第三阶段:客户端认证和密钥交换

    

   客户端在接受完服务器发送来的消息之后,客户端会首先验证服务器是否提供了合法的证书,并检查server_hello中的参数是否可以接受。如果所有条件均满足,则客户端会向服务器发回一个或者多个消息。

    假设服务器请求了证书,那么客户端一开始需要发送以条证书消息 certificate message。如果不能提供合适的证书,则会发送一条“无证书警告”。

    接下来客户端会发送一条必要的密钥交换信息 client_key_exchange message,消息内容依赖于密钥交换的类型。如下:

    RSA:客户端生成48字节的次密钥,并使用服务器证书中的公钥或服务器密钥交换消息Client_key_exchange message 中的临时RSA密钥加密。它被用于生成之后要用的主密钥自己算。

    瞬时或匿名Diffie-Hellman:发送的客户端Diffie-Hellman公钥参数

    固定Diffie-Hellman:由于证书消息中包括Diffie-Hellman公钥参数,因此,此消息为空

    Fortezza参数

    在此阶段,客户端可以发送一个证书验证消息 certificate_verify message 来提供对客户端证书的精确认证。此消息只在客户端证书具有签名能力的时候发送(除了固定Diffie-Hellman参数外的证书)。此消息基于前述消息的Hash编码签名,其定义如下:

    CertificateVerify.signature.md5_hash = MD5(master_secret || pad_2 || MD5(handshake_message || master_secret || pad_1)

    CertificateVerify.signature.sha_hash = SHA(mastet_secret || pad_2 || SHA(handshake_message || master_secret || pad_1)

    其中 pad_1 和 pad_2 是前面MAC定义的值,握手消息是指的所有发送的握手协议消息或接收到的从 client_hello 消息开始不包括此消息的所有消息。主密钥的计算方法将在以后介绍,如果用户私钥是DSS,则被用于加密SHA-1散列;如果用户私钥是RSA的密钥,则被用于加密MD5和SHA-1散列连接。目的都是为了使用私钥验证客户证书的客户所有权,即使有人误用了客户证书,他也无法发送消息。

  

  最后一个阶段是完成阶段:完成安全连接设置。

  客户端发送改变密码规范消息change_chipher_spec message 并向当前 CipherSpec中复制,同时挂起CipherSpec。于是,客户端立即使用新的算法、密钥和密码发送新的完成消息 finished message。完成消息对密钥交换和认证过程的正确性进行验证。

  一是可以证明握手数据没有被篡改过,二是能证明自己确定是密钥的拥有者。

  完成消息中包含:

     CertificateVerify.signature.md5_hash = MD5(master_secret || pad_2 || MD5(handshake_message || master_secret || pad_1)

    CertificateVerify.signature.sha_hash = SHA(mastet_secret || pad_2 || SHA(handshake_message || master_secret || pad_1)

  只有密钥的拥有者才能解密得到pre-master key,master key。最终得到key block,进行hash运算得到的结果才能和发送方一致。

  而服务器方面同理。

  最后,看一下密码计算,通过密钥减缓创建共享主密钥和使用主密钥生成密码参数。

  主密钥的创建

    共享主密钥是利用安全密钥交换为此会话建立的一个一次性48字节的值。生成此密钥共分为两个阶段。第一阶段:交换次密钥pre_master_secret,第二阶段。双方共同计算主密钥 master_secret,对于次密钥有如下两种可能。

    RSA:由客户端生成48字节的次密钥pre_master_secret,用服务器的RSA公钥加密后,发往服务器,服务器再用其私钥解密的pre_master_secret。

    Diffie-Hellman:客户端和服务器同时生成Diffie-Hellman公钥。密钥交换后,各方执行Diffie-Hellman计算,创建共享次密钥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) )

    其中,ClientHello.random和ServerHello.random是在初始hello消息中交换的两个随机数。

  生成密码参数

    CipherSpec 需要在客户端写MAC密钥、服务器写MAC密钥、客户端写密钥、服务器写密钥、客户端写初始向量和服务器写初始向量,均是通过主密钥生成的。主密钥通过Hash函数把参数映射到足够长的安全字节序列。

    从主密钥生成各主要参数的方法与从次密钥中生成主密钥的方法相同:

    key_block = MD5 (master_secret || SHA('A' || pre_master_secret || ClientHello.random || ServerHello.random) ) || 

           MD5 (master_secret || SHA('BB' || pre_master_secret || ClientHello.random || ServerHello.random) ) ||

           MD5 (master_secret || SHA('CCC' || pre_master_secret || ClientHello.random || ServerHello.random) ) || ......

    直到生成足够的输出。次算法结构的结果是一个伪随机函数,可以将主密钥master_secret堪称该函数的一个伪随机种子值(seed value) ;客户端和服务器随机值则被视为复杂密码分析方法的盐值(salt Value)。

  接着看一下一个比较简单的协议:修改密码规范协议

    修改密码规范协议是SSl三个特性协议之一,也是最简单的一个。协议由一个仅包含1字节的值为1的消息组成,此消息使得挂起状态被复制到当前状态,用于更新此连接使用的密码组。用在握手协议的第四阶段 Change_CipherSpec_message中使用。

  然后是三个特定协议中的最后一个:警报协议

  警报协议用于向对等实体传递SSL相关的警报。和使用SSL的其他应用一样,警报消息按照当前状态压缩和加密。

  此协议的每个消息由2字节组成。第一个字节,值1表示警告,值2表示致命错误,传递消息出错的严重程度。如果级别为致命,则SSL将立即终止连接,而会话中的其他连接将继续进行,但不会再次会话中新建连接。第二个字节包含描述特定警报的信息代码。如下:

  

   

  最后来看一下SSL的记录协议:

    SSL记录协议为SSL连接提供如下两种服务:

      保密性:握手协议定义了加密SSL载荷的传统加密共享密钥

      消息完整性:握手协议也定义了生成消息认证代码(MAC)的共享密钥

    

     第一步是分段,每个上层消息被分成若干个小于或等于2^14字节(16384字节)的段;接着进行可选择压缩,压缩必须采用无损压缩方法,并且增加长度不为1024字节。在SSLv3(和当前的TLS)中,没有指定压缩算法,所以默认的压缩算法为空。

    接着对压缩数据计算其消息认证码MAC。为此,需要使用共享密钥,其计算方式如下:
      hash(MAC_write_secret || pad_2 || hash(MAC_write_secret || pad_1 || seq_num || SSLCompress.type || SSLCompress.length || SSLCompress.fragment))

    其中

      ||             = 连接

      MAC_write_secret      = 共享密钥

      hash            = Hash算法,MD5或SHA-1

      pad_1            = 字节0x36(0011 0110),对MD5重复48次,对SHA-1重复40次

      pad_2            = 字节0x5c(0101 1100),对MD5重复48次,对SHA-1重复40次

      seq_num          = 此消息的序列号

      SSLCompressed.type     = 处理此分段的上层协议

      SSLCompressed.length     = 压缩后的分段长度

      SSLCompressed.fragment      = 压缩后的分段(如果没有压缩,则为明文)

    接下来,将压缩消息和MAC用对称加密方法加密。加密对内容增加长度不能超过1024字节,以便整个长度不能超过2^14+2048。以下加密算法是允许的,Fortezza可被用于智能卡加密模式。

    

     对流加密而言,压缩消息和MAC一起被加密。注意MAC在加密之前计算,然后将MAC和明文或者压缩后的明文一起加密。

    对分组加密而言,填充应在MAC之后、加密之前进行,填充格式是一定长度的填充字节后跟1字节的填充长度。整个填充域的长度使得总长度(明文+MAC+填充域的长度)为规定的加密分组长度整数倍的最小长度。例如,明文(或如果使用了压缩则为压缩文本)长度为58字节,MAC长度为20字节(使用SHA-1),使用分组长度为8字节的加密算法(如DES),则加上填充长度域的1字节,总共79字节,为了达到8的整数倍,则需要填充1字节。

    最后一步是加上SSL头部:

     内容类型(8位):封装段使用的高层协议。

     主版本号(8位):表明SSL使用的主版本号,如SSLv3的值为3

     从版本号(8位):表明SSL使用的从版本号,如SSLv3的值为0

     压缩长度(16位):明文段(如果使用了压缩,则为压缩段)的字节长度,最大值为2^14+2048。

    

猜你喜欢

转载自www.cnblogs.com/Hikigaya-Hachiman/p/12501136.html