本文原载于我的博客,地址:https://blog.guoziyang.top/archives/57/
面试官:HTTPS协议了解吗?
我:了解的了解的(内心OS:不就是HTTP的加密版本吗)
面试官:那你可以说一下HTTPS的握手流程吗?
我:……
面试官:今天就先到这儿吧,你先回去等通知吧。
真悲哀,又凉了。
你是否也是像我一样,熟记各个框架的使用,各种源码信手拈来,却最后总是会败在这些基础的东西上?你是否觉得网络上的https讲解都太形而上,枯燥无聊,看完一遍也记不下来?
那我们就亲自抓包看一看吧,亲眼看一看https的握手流程。
本次环境使用macos下的wireshark进行抓包。
在开始抓包之前,有必要先进行一些https的简单介绍。
https的出现主要是为了解决http明文传输的问题,如果http传输的信息包含有像密码之类的敏感数据,那么任何拦截到数据包的人都可以很轻易地获取到这些信息,拦截者慎之可以任意修改数据,这是很危险的,
https对于http的问题,主要进行了如下的改进:
那么HTTPS是如何做到这一点的呢?
公所周知,HTTP在网络的五层模型中处于应用层,基于传输层的TCP。而HTTPS则是在应用层的HTTP的下层又加入了一层SSL/TLS加密层,http的数据要经过ssl加密后才会交给tcp。
至于加密原理,这里不做赘述。只提一句,HTTPS的信息加密和解密,基于对称加密算法,并不是基于非对称加密。加密和解密是使用的同一个密钥。但是,这样是有风险的,如果在交换密钥的时候(这时候还没有进行加密,肯定是明文的)密钥被拦截,那也是非常危险的,所以在https的握手期间,密钥交换过程是基于非对称加密的。
这时有人就会问,那既然非对称加密这么好,明文传输都不怕被拦截,那为什么整个HTTPS的信息加密不基于非对称加密呢?原因在于,虽然非对称加密在安全性几乎无可挑剔,但是有一个很致命的缺点,那就是资源消耗大,且速度慢,如果完全使用非对称加密的话,那上5G就非常必要的(笑)。
为了保留第一次握手的数据,我在抓包之前清理了浏览器缓存。
此次抓包使用我的主页https://guoziyang.top
作为抓包对象,在wireshark中可以设置拦截器ip.addr == 服务器ip地址 and tcp.port == 443
,过滤掉一些不必要的包。
抓包结果如下:
我们来逐步分析https的握手。
由于HTTPS也是基于TCP进行传输的,那么TCP建立连接就需要进行三次握手,和HTTP不同的是,如果只输入域名进行访问的话,默认和目标服务器的443端口建立连接,而HTTP是和80端口。
478、482和483三个TCP包对应着三次握手的过程,SYN、SYNACK和ACK,很常规,和HTTP一样,就不说了,大家应该都懂。
下面就是HTTPS协议的交互过程。
第一次握手是由客户端向服务器发送一个Client Hello包,对应着上图的No. 484包。
包的内容如下:
字段很多,挑重点的看。注意这里看的是Handshake Protocol。
首先就说明了自己的TLS协议版本,Version: TLS 1.2
声明的客户端的协议版本为TLS 1.2。在下方的Extension: supported_versions
字段中,说明了支持的服务端使用的TLS版本:
这里支持四个版本:TLS 1.0到1.3,即服务器可以使用这些版本的TLS。
接着出现了一个随机数,Random
字段:
这个字段由两个小字段组成,分别是一个四字节时间戳,和一个随机的字节数组,长28字节。random字段用于随后生成对话密钥,这个之后再说。
在Cipher Suites
字段,客户端声明了自己支持的加密协议套件,服务端可以使用这些加密算法和自己通信。注意这里说的是对称加密算法套件,是在正式传输数据时使用的对称加密。如下:
注意一下这里的加密套件的命名方式,表示组成该套件的算法,一个完整的Cipher Suites至少需要四个算法,分别是认证算法(Au,用于身份验证)、密钥交换算法(KeyExchange,用于密钥协商)、对称加密算法(Enc,用于信息加密)和信息摘要算法(Mac,用于完整性校验)
服务器收到请求后,会先返回一个ACK(No. 489),接着才对客户端进行回应。应答包为Server Hello
包。在这次抓包过程中,服务器将这次握手分成了两个包(也可以放在一个包里,也可以分成更多的包),第一个包 No. 545 如下:
服务器首先也说明自己使用的加密协议版本Version: TLS 1.2
,这个由服务器在客户端的提供的supported_versions
中挑选,如果服务器没有可以使用的加密协议,就会主动关闭通信。
该包中也有一个Random
字段,结构和第一次握手的包中的Random
结构相同,用于后续生成对话密钥。
服务器会在客户端提供的加密套件Cipher Suites
中挑选一个自己支持的,并告知客户端。在该包中,为Cipher Suite
字段,这里可以看到,服务器选用的加密套件为TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
。
第二个包为No. 548:
这里这个包名为Certificate
, Server Key Exchange
和Server Hello Done
。
Certificate包主要就用于服务器发送自己的域名证书供客户端校验,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Rwv75Ls-1591435870773)(https://img.guoziyang.top/images/2020/06/06/2020-06-06-4.09.21.png)]
这里我的服务器发送了两个证书,一个是我自己的域名证书,另一个是Let's Encrypt Authority X3
的证书,因为我的证书是从Let's Encrypt
申请的,而Let's Encrypt
不是根证书颁发机构,而是一个中级证书颁发机构,所以客户端还需要验证中级证书颁发机构的有效性。
Server Key Exchange用于发送自己的公钥,在该包中基本只有一个参数:EC Diffie-Hellman Server Params
:
这一步,服务器会通过Diffie-Hellman算法生成一个密钥对,在这里使用的是圆锥曲线算法,在Named Curve
中指明自己使用的圆锥曲线。具体算法感兴趣的同学可以去学习学习,这里就不多说了。
接着,服务器会将生成的密钥对中的公钥放在Pubkey
字段发送给客户端,在Signature Algorithm
字段中说明自己使用的签名算法,并将使用自己的私钥签名的数据存放在Signature
字段中。
最后服务器会发送一个Server Hello Done
包,这个包没有数据,仅仅通知客户端这次握手结束了。
在客户端将第三次握手的包发送给服务器之前,客户端会首先对服务器的域名证书进行校验,判断是否是有效的域名证书,例如域名是否一致、是否过期、是否是可信的CA颁发的等。如果是可信的,或者客户端选择忽略域名的有效性,那么客户端就会进行第三次握手,即上图的包No. 552:
这时客户端已经拥有了三个数:客户端随机数、服务器随机数和服务器的Pubkey,客户端会将这三个参数使用Diffie-Hellman算法生成Premaster secret,即最终的密钥。
从这个包名可以看出,这次握手的目的有三个:Client Key Exchange
、Change Cipher Spec
和Encrypted Handshake Message
。
Client Key Exchange
过程中,类似Server Key Exchange
,客户端同样生成一份自己的Pubkey。
Change Cipher Spec
这个包没有数据,仅仅是提示服务器,表示以后的数据都会通过加密的方式发送,但实际上,这个包是无关紧要的,在TLS 1.3中被正式废弃。
Encrypted handshake message
是一段加密的消息,是使用协商成功后的对称密钥加密发送的第一个消息。加密的内容是将之前所有的握手数据,计算md摘要后再进行PRF运算,最后再使用密钥进行加密,可用于服务端校验握手流程,判断是否有过中间人攻击。
第四次握手对应的包No. 588,由服务器发送给客户端,如下:
在此之前,服务器也获取到了客户端的Pubkey,服务器也拥有了三个参数,于是也生成了最终密钥,至于为什么明明Pubkey不同,最终密钥相同的原因,可以去研究下Diffie-Hellman算法。
这个包四个功能:New Session Ticket
、Change Cipher Spec
、Encrypted Handshake Message
和Application Data Protocol: http2
。
New Session Ticket
这个包类似于cookie,当客户端重新连接的时候,可以携带上该参数,就无须重新进行握手的过程,而是可以直接使用之前协商的参数进行加密。
Change Cipher Spec
和第三次握手一样,是一个无关紧要的包。Encrypted Handshake Message
包的作用也和第三次握手类似,加密的是服务器的握手记录,用于给客户端校验。
最后一个包,Application Data Protocol
,是服务器通知客户端HTTP协议的版本,这里我的服务器使用了HTTP 2。
完成了这个协商后,客户端和服务端都获取到了对称加密的密钥,就可以进行安全的通信了。而且这个密钥交换的过程也是安全的,这样,就建立了一个安全的通信信道。