# 重温 HTTPS

在开始之前,我们需要再次捋清整个 HTTPS 握手的流程,思考下面几个问题,你是否完全懂整个流程?


A:HTTPS中共用到了两种加密,这两种加密的作用和关系是什么?

Q:非对称加密只是为了加密对称加密的密钥(还起了身份认证功能),而对称加密用于加密真正的http数据。

A:非对称加密和对称加密的密钥分别是由谁形成的?

Q:首先,对称加密的密钥是由客户端来随机生成的。而非对称加密的密钥则需要考虑密钥协商算法了,在DHE/ECDHE等算法中,非对称加密的公私钥是临时生成的,私钥保存在服务端上,公钥则由服务端发送给客户端。而其他密钥协商算法如RSA、DH_DSS、DH_RSA等,则公钥使用的就是证书上的公钥。私钥就是服务端上证书的私钥。
A:证书在整个HTTPS过程中起了什么作用?

Q:在通常的HTTPS握手中,证书其实只是起到了认证的作用,浏览器通过预存的各大CA的公钥去解密证书的内容,判断目标是否为真实目标,而不是伪造的目标(也就是说,证书中的公私钥并不会用于后续的密钥协商)。当然对于RSA和DH密钥协商算法,证书的公钥就被用于加密预主密钥。

A:在私钥不泄露的前提下,客户端和服务端怎么保证对称加密的密钥不会被破解?

Q:首先我们需要知道公钥加密只能私钥解密,并且在HTTPS的过程中,服务端的私钥是不会返回给客户端的。
而这个对称加密的密钥是由客户端随机生成的,客户端使用服务端提供的公钥去加密这个私钥,返回给服务端。服务端接收后,再使用私钥去解密。因此在私钥不泄露的情况下,对称加密的密钥也自然不会被破解。
A:客户端和服务端怎么保证它们进行密钥协商交换后,大家得到的(对称)密钥是一致的。

Q:这里还是和上一个问题一样,在密钥交换过程中,客户端并非只是单单只把加密后的密钥发送给服务端。客户端和服务端都会在密钥协商完成后,将之前自己发过的包和收到的包做一个hash杂凑再用对称密钥进行加密发送给对方。
对方在接收到后,使用协商好的密钥去解密数据。一致则代表大家密钥协商成功,开始数据传输。

反复思考上面的几个问题,理解后我们不难得以下结论;

想要解密流量,要么获取非对称加密的私钥,要么获取对称加密的密钥。

  • 私钥是只在服务端上的,根据不同的密钥协商算法,这个私钥要么就是证书的私钥,要么是临时生成的保存在服务端缓存中的。
  • 对称加密的密钥即在客户端也在服务端上。但都只是在缓存中。

也就是说,我们破解流量就可以细分为以下几种场景:

①客户端权限:读取缓存中的加密密钥,一步到位。(部分使用 openssl 和 nss 库的应用可行)

②服务端权限:读取缓存中的对称加密密钥或者私钥 (需要逆向服务器实现,工作量太大) 再或者修修改服务器的首先密钥交换协议,使用我们或者它原本的证书私钥进行加解密。(优选)

③没有权限,只有流量包,和对应的私钥。直接使用私钥加解密。(得看密钥交换协议用的是临时的还是其他)

# 客户端权限

Chrome 和 Firefox 会使用的 NSS 库来处理 TLS。而 NSS (OpenSSL 也可以) 可以写入密钥日志,以便外部程序可以解密 TLS 连接。通过设置环境变量 SSLKEYLOGFILE,将其指向一个可写入的文件。这样浏览器在启动时就会检查这个环境变量,如果存在的话,它会向指定的文件写入访问 HTTPS 站点时使用的密钥。我们只需要把这个文件给导入到 wireshark,就可以解密流量了。

image-20220401152854695

image-20220401152859510

将该文件加载到 wireshark 中,waireshark 就会自动解密,获取到 HTTP 流量

编辑 -> 首选项 ->Protocols->TLS

image-20220401152913157

image-20220401152922277

导入 sslkeylog 后,流量成功破解。

image-20220401152926732

# 服务端权限(使用证书私钥直接解密)

服务端并不像客户端有浏览器输出 sshkeylog,我们除非逆向服务器,不然密钥只出现在缓存中。

但是我们可以让服务器不走临时的密钥交换协议。

当然目前服务器默认都会选择 ECDHE 模式。因此我们要修改服务器选项,让服务器关闭临时交换算法来实现,这里用 nginx 服务器实现。

# nginx 配置 SSL

# 检查环境是否支持 SSL

nginx -V  #检查是否存在 ssl 模块,没有则需要重新 build nginx

image-20220401153924002

# 生成自签名 SSL 证书

这里使用 keytools 生成自签名证书,再用 openssl 转换格式

image-20220401153950562

# 修改 nginx 配置文件,加载证书

在 HTTP 内新增一个 server

server {
        listen 80;
        listen 443 ssl;                # 监听 443 端口,开启 ssl (必须)
        server_name domain.com;
        ssl_certificate      /root/cer.crt;  #公钥(只能是文本格式的)
        ssl_certificate_key  /root/private.key; #私钥(也要文本格式)
        # 协议优化 (可选,优化 https 协议,增强安全性)
        ssl_protocols        TLSv1 TLSv1.1 TLSv1.2;
#       ssl_ciphers          ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
#       ssl_prefer_server_ciphers  on;
#       ssl_session_cache    shared:SSL:10m;
#       ssl_session_timeout  10m;
         root /var/www/html;
 location / {
        index index.html index.htm;
       }
       
}

最终实现效果如下。

nginx -s reload

image-20220401154119525

# 修改服务器支持的加密套件

nginx 中,ssl_ciphers 配置项的可选值由 openssl 的 ciphers 定义

openssl ciphers  -v #输出当前服务器支持的加密套件,不加 - v 则是不换行输出。

image-20220401154315322

先获取当前服务器支持的套件,然后 copy 到 vscode 中,使用正则去掉所有的临时密钥交换算法

openssl ciphers #输出不换行的加密套件
(DHE|ECDHE)-[^:]{1,}: #VScode 中去掉临时密钥交换算法的正则

image-20220401154457261

然后修改 nginx 的配置文件,添加 ssl_ciphers 即可

image-20220401154503651

# 流量解密

这里已经很明了了,直接把服务器证书的私钥给导入 wireshark 就能读到明文数据了。

image-20220401154611626

image-20220401154618936

image-20220401154652128

# nps 流量解密

直接导入证书就完事了,在隧道传输流量的时候用的是 TLS 协议,和 https 是一样的

image-20220401160651917

image-20220401160702554