Https详解+wireshark抓包演示
Https理论
在说HTTPS之前先说说什么是HTTP,HTTP就是我们平时浏览网页时候使用的一种协议。HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全。为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。SSL目前的版本是3.0,被IETF(Internet Engineering Task Force)定义在RFC 6101中,之后IETF对SSL 3.0进行了升级,于是出现了TLS(Transport Layer Security) 1.0,定义在RFC 2246。实际上我们现在的HTTPS都是用的TLS协议,但是由于SSL出现的时间比较早,并且依旧被现在浏览器所支持,因此SSL依然是HTTPS的代名词,但无论是TLS还是SSL都是上个世纪的事情,SSL最后一个版本是3.0,今后TLS将会继承SSL优良血统继续为我们进行加密服务。目前TLS的版本是1.2。
Https的工作原理
HTTPS在传输数据之前需要客户端(浏览器)与服务端(网站)之间进行一次握手,在握手过程中将确立双方加密传输数据的密码信息。TLS/SSL协议不仅仅是一套加密传输的协议,更是一件经过艺术家精心设计的艺术品,TLS/SSL中使用了非对称加密,对称加密以及HASH算法
TCP、Https、SSL/TLS、https的关系
网络协议套件上图截自维基百科,对协议进行了明确的划分。
-
TCP
传输控制协议,属于传输层协议,提供可靠数据传输。它为http等应用层协议提供服务。 -
Http
超文本传输协议,属于应用层协议。依赖于TCP协议。 -
SSL/TLS
安全传输层协议,用于在两个通信应用程序之间提供保密性和数据完整性。位于某个可靠的传输协议(例如 TCP)上面,属于应用层协议。 -
Https
在Http和TCP中间加入了SSL/TLS,保证数据传输的安全性
Https抓包演示
https请求上面是我使用Avanced REST client请求我的https接口,整个请求是没问题的,后面我们就会抓下这个请求的包进行分析。
为了方便演示整个流程,我使用了自己的云服务器和已备案的域名,证书直接在阿里云上申请的免费证书。
环境配置
这里web服务器我们使用Nginx。
首先将阿里下发的证书放到nginx的conf下(新建一个cert文件夹)
nginx_cert.png
配置nginx
nginx_conf.png
当然我们的后台服务得启动起来。端口9000。这里我用的Springboot。
@ApiOperation(value = "测试POST", notes = "测试POST")
@RequestMapping(value = "post", method = RequestMethod.POST)
public Result<TestEntity> testPost(@RequestBody TestBody testBody) throws Exception {
Result<TestEntity> result = new Result<TestEntity>();
TestEntity testEntity = new TestEntity();
testEntity.setId(2);
testEntity.setName(testBody.toString());
result.setData(testEntity);
return result;
}
Https握手
我们将前面发起的请求进行抓包,这里使用wireshark。
抓包.png
这是一个完整的请求抓包。这样看,不是很清晰,我来画个图来表达下这个过程发生了什么。
请求流程三次握手和四次挥手我们就不说了,这个才讲TCP的时候有详细介绍。
我们一步一步的看
第一步 Client Hello
先看一下抓出来包的内容,我们直接看到SSL层
client hello.png
这一步干啥了呢?
- TLS的版本
- 随机数:这个是用来生成最后加密密钥的影响因子之一,包含两部分:时间戳(4-Bytes)和随机数(28-Bytes)
- session-id:用来表明一次会话,第一次建立没有。如果以前建立过,可以直接带过去。后面的扩展内容会详细讲到。
- 加密算法套装列表:客户端支持的加密-签名算法的列表,让服务器去选择。
- 压缩算法:似乎一般都不用
- 扩展字段:比如密码交换算法的参数、请求主机的名字等等
这里要注意一个随机数,很重要。我们先记为Random1。
第二步 Server Hello
Server hello.png- 据客户端支持的SSL/TLS协议版本,和自己的比较确定使用的SSL/TLS协议版本
- 确定加密套件,压缩算法
- 产生了一个随机数Random2。注意,至此客户端和服务端都拥有了两个随机数(Random1+ Random2),这两个随机数会在后续生成对称秘钥时用到
第三步 Server => Client
这次传输包含三部分内容
- Certificate
- Server Key Exchange
- ServerHello Done
这里也是一个优化,三个部分一起发送。我们一个个分析
Certificate
这里主要就是把证书发送给Client。图中可以看到我的证书和证书发放机构。客户端拿到证书后就可以进行验证,同时获取到公钥,用于后面Random3的加密。
证书信息.png证书一般采用X.509标准。
Server Key Exchange
这个消息是用来发送密钥交换算法相关参数和数据的。这里要提前提一下,就是根据密钥交换算法的不同,传递的参数也是不同的。
常用的密钥交换算法:RSA、DH(Diffie-Hellman)、ECDH(Ellipticcurve Diffie–Hellman)。
ECDH.png
这里看到使用的ECDH。
Server Hello Done
这个就是Server来表示自己说完了。类似电影里别人拿对讲机说完话最后会有一个“完毕!”。
第四步 Client => Server
这次传输也包含三部分内容,也是做了一个优化
- Client Key Exchange
- Change Cipher Spec
- Encrypted Handshake Message
我们依次解读
Client Key Exchange
这个也是交换秘钥参数。
c_e_k.png
这里客户端会再生成一个随机数Random3。然后使用服务端传来的公钥进行加密得到密文PreMaster Key。服务端收到这个值后,使用私钥进行解密,得到Random3。这样客户端和服务端就都拥有了Random1、Random2和Random3。这样两边的秘钥就协商好了。后面数据传输就可以用协商好的秘钥进行加密和解密。
Change Cipher Spec
编码改变通知。这一步是客户端通知服务端后面再发送的消息都会使用前面协商出来的秘钥加密了,是一条事件消息。
Encrypted Handshake Message
这一步对应的是 Client Finish 消息,客户端将前面的握手消息生成摘要再用协商好的秘钥加密,这是客户端发出的第一条加密消息。服务端接收后会用秘钥解密,能解出来说明前面协商出来的秘钥是一致的。
第五步 Server => Client
包括三部分
- New Session Ticket
- Change Cipher Spec
- Encrypted Handshake Message
New Session Ticket
包含了一个加密通信所需要的信息,这些数据采用一个只有服务器知道的密钥进行加密。目标是消除服务器需要维护每个客户端的会话状态缓存的要求。这部分内容在后面的扩展部分会讲到
Change Cipher Spec
编码改变通知。这一步是服务端通知客户端后面再发送的消息都会使用加密,也是一条事件消息。
Encrypted Handshake Message
这一步对应的是 Server Finish 消息,服务端也会将握手过程的消息生成摘要再用秘钥加密,这是服务端发出的第一条加密消息。客户端接收后会用秘钥解密,能解出来说明协商的秘钥是一致的。
到这里双方SSL/TLS握手完成。
Https数据传输
接下来就真正的到了接口请求的阶段。TLS的Content-Type为Application Data。 传输的内容也是加密的。
数据传输.png
常见问题:
这里对整个过程中出现的常见问题做一个汇总
第四步出现多个Hello Request消息
client_key_exchange.png我们看到上面最后出现了两个Hello Request,估计很多人用wireshark打开就是这样的。而Hello Request消息是个啥东西呢?我们看下权威文档:
Hellorqeust.png
这写的很清楚嘛,这是消息应该是Server可能随时发给Client的。到我们这来怎么变成Client发给Server了,而且和消息的解析牛头不对马脚。
我们再重温下我们的流程
客户端发送了Change Cipher Spec后,后面的内容就是加密的,而加密后的内容wireshark无法解析识别。而这些二进制数据就别误解析为Hello Request。我们看下Hello Request的类型枚举值:
hellorequestenum.png
hello_request(0)
所以我觉得应该是wireshake误认为是hello request消息
既然知道问题,那怎么解决呢?
这里需要我们把私钥添加到wireshark中:
【编辑】-》【首选项】-》【SSL】-》【Edit】
wireshark首选项.png
然后将私钥添加进来
添加私钥.png
这样就搞定了。
拓展内容
这部分对Https做一个深入的了解
TLS会话恢复
完整的TLS握手需要额外延迟和计算,为所有需要安全通信的应用带来了严重的性能损耗。为了帮助减少一些性能损耗,TLS提供恢复机制,或多个连接之间共享相同的协商密钥数据。
传输层安全(TLS)
会话标识
“会话标识符”(RFC 5246)恢复机制在SSL 2.0中首次被引入,它允许服务器在“ServerHello消息”中构建和发送一个32字节的会话标识符,作为“ServerHello”消息的一部分。
在服务器内部,服务器维护一个会话ID和其对应的协商参数的缓存。反过来,客户端也同时存储会话ID信息,在后续的会话中,可以在“ClientHello”消息中携带session ID信息,指示服务器它保存了session ID对应的密钥和加密算法等信息,并且可以重用这些信息。假设在客户端和服务器都能在它们各自的缓存中找到共享的会话ID参数,那么缩减的握手就可以进行了。否则,开始一个新的会话协商,这将产生一个新的会话ID。
我们演示一下,在前面抓包的的基础上,我们再发一次请求。
会话缓存.png
最外面的红框显示了整个https访问流程,内部的框是除去TCP握手和分手的的流程。我们将流程形象画出来:
缩减的TLS握手协议
借助会话标识符,我们能够减少一个完整的往返,以及用于协商的共享密钥的公钥加密算法开销。这让我们能快速的建立安全连接,而不损失安全性。我们看一下这个会话标识: session_ticket_1.png
第二次的请求中的Client Hello的消息中Session Ticket有值了。
那这个Session Ticket是啥时候获取的呢。我们继续重温下https握手流程,在最后一部。Server最后发给Client中,第一个消息就是New Session Ticket。我们看一下:
在实际应用中,大多数Web应用程序试图通过建立到同一个主机的多个连接并行获取资源,这使得会话恢复成为必不可少的一个优化项,其可以减少延迟,计算成本。
大多数现代浏览器都会有意的等待第一TLS连接完成后,再打开到同一台服务器的新连接:后续TLS连接,可以重复使用的SSL会话参数,以避免握手的延迟和损耗。
然而,“会话标识符”机制的一个限制就是要求服务器为每个客户端创建和维护一个会话缓存。这会为服务器上带来几个问题,对于一些每天同时几万,甚至几百万的单独连接的服务器来说:由于缓存session ID所需要的内存消耗将非常大,同时还有session ID清除策略的问题。这对一些流量大的网站来说不是一个简单的任务,理想的情况下,使用一个共享的TLS会话缓存可以获得最佳性能。
上述问题没有是不可能解决的,许多高流量的网站成功的使用了会话标识符。但是,对任何多服务主机的部署,会话标识符方案需要一些认真的思考和好的系统架构,以确保良好的的会话缓存。
Session Tickets
为了解决上面的会话缓存带来的服务器部署问题,“Sesion Ticket”(RFC 5077)取代机制被引入,目标是消除服务器需要维护每个客户端的会话状态缓存的要求。相反,如果客户指示它支持Session Ticket,在TLS握手的最后一步中服务器将包含一个“New Session Ticket”信息,包含了一个加密通信所需要的信息,这些数据采用一个只有服务器知道的密钥进行加密。
这个Session Ticket由客户端进行存储,并可以在随后的一次会话中添加到 ClientHello消息的SessionTicket扩展中-因此,所有的会话信息只存储在客户端上,Session Ticket仍然是安全的,因为它是由只有服务器知道的密钥加密的。
Session Identifiers和Session Ticket机制通常分别被称为“会话缓存”和“无状态恢复”机制。无状态恢复的主要改进是消除服务器端的会话缓存,从而简化了部署,它要求客户在每一个新的会话开始时提供Session Ticket 直到Ticket过期。
在实际应用中,在一组负载平衡服务器中部署Session Ticket,也需要仔细考虑:所有的服务器都必须用相同的会话密钥,或者可能需要额外的机制,定期轮流在所有服务器上的共享密钥。
SessionId和Session Ticket的区别
Session ID的思想就是服务器端为每一次的会话生成并记录一个ID号并发送给客户端,在重新连接的时候(多次短连接场景),客户端向服务器发送该ID号,服务器查找自己的会话记录,匹配之后,重用之前的加密参数信息。
而Sessionticket的思想类似于cookie,是由服务器将ticket数据结构发由客户端管理,ticket中是包含了加密参数等连接信息。当需要重连的时候,客户端将ticket发送给服务器。这样双方就得到了重用的加密参数。
Session ticket较之Session ID优势在于服务器使用了负载均衡等技术的时候。Session ID往往是存储在一台服务器上,当我向不同的服务器请求的时候,就无法复用之前的加密参数信息,而Session ticket可以较好的解决此类问题,因为相关的加密参数信息交由客户端管理,服务器只要确认即可。
证书
证书种类
证书格式
一般来说,主流的 Web 服务软件,通常都基于 OpenSSL 和 Java 两种基础密码库。
-
Tomcat、Weblogic、JBoss 等 Web 服务软件,一般使用 Java 提供的密码库。通过 Java Development Kit (JDK)工具包中的 Keytool 工具,生成 Java Keystore(JKS)格式的证书文件。
-
Apache、Nginx 等 Web 服务软件,一般使用 OpenSSL 工具提供的密码库,生成 PEM、KEY、CRT 等格式的证书文件。
-
IBM 的 Web 服务产品,如 Websphere、IBM Http Server(IHS)等,一般使用 IBM 产品自带的 iKeyman 工具,生成 KDB 格式的证书文件。
-
微软 Windows Server 中的 Internet Information Services(IIS)服务,使用 Windows 自带的证书库生成 PFX 格式的证书文件。
证书类型
您可以使用以下方法简单区分带有后缀扩展名的证书文件:
- *.DER 或 *.CER 文件: 这样的证书文件是二进制格式,只含有证书信息,不包含私钥。
- *.CRT 文件: 这样的证书文件可以是二进制格式,也可以是文本格式,一般均为文本格式,功能与 *.DER 及 *.CER 证书文件相同。
- *.PEM 文件: 这样的证书文件一般是文本格式,可以存放证书或私钥,或者两者都包含。 *.PEM 文件如果只包含私钥,一般用 *.KEY 文件代替。
- *.PFX 或 *.P12 文件: 这样的证书文件是二进制格式,同时包含证书和私钥,且一般有密码保护。
您也可以使用记事本直接打开证书文件。如果显示的是规则的数字字母,例如:
—–BEGIN CERTIFICATE—–
MIIE5zCCA8+gAwIBAgIQN+whYc2BgzAogau0dc3PtzANBgkqh......
—–END CERTIFICATE—–
那么,该证书文件是文本格式的。
如果存在——BEGIN CERTIFICATE——,则说明这是一个证书文件。
如果存在—–BEGIN RSA PRIVATE KEY—–,则说明这是一个私钥文件。
更多内容参考主流数字证书都有哪些格式?
常见加密算法
- 非对称加密算法:RSA,DSA/DSS
- 对称加密算法:AES,RC4,3DES
- HASH算法:MD5,SHA1,SHA256
为啥数据传输时候用对称加密?
RSA性能是非常低的,原因在于寻找大素数、大数计算、数据分割需要耗费很多的CPU周期,所以一般的HTTPS连接只在第一次握手时使用非对称加密,通过握手交换对称加密密钥,在之后的通信走对称加密。