mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-17 08:46:40 +08:00
757 lines
28 KiB
HTML
757 lines
28 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
|
||
<head>
|
||
|
||
<head>
|
||
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||
|
||
<link rel="icon" href="/static/favicon.png">
|
||
|
||
<title>27 更好更快的握手:TLS1.3特性解析.md.html</title>
|
||
|
||
<!-- Spectre.css framework -->
|
||
|
||
<link rel="stylesheet" href="/static/index.css">
|
||
|
||
<!-- theme css & js -->
|
||
|
||
<meta name="generator" content="Hexo 4.2.0">
|
||
|
||
</head>
|
||
<body>
|
||
<div class="book-container">
|
||
|
||
<div class="book-sidebar">
|
||
|
||
<div class="book-brand">
|
||
|
||
<a href="/">
|
||
|
||
<img src="/static/favicon.png">
|
||
|
||
<span>技术文章摘抄</span>
|
||
|
||
</a>
|
||
|
||
</div>
|
||
|
||
<div class="book-menu uncollapsible">
|
||
|
||
<ul class="uncollapsible">
|
||
|
||
<li><a href="/" class="current-tab">首页</a></li>
|
||
|
||
</ul>
|
||
<ul class="uncollapsible">
|
||
|
||
<li><a href="../">上一级</a></li>
|
||
|
||
</ul>
|
||
<ul class="uncollapsible">
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/00 开篇词|To Be a HTTP Hero.md.html">00 开篇词|To Be a HTTP Hero.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/01 时势与英雄:HTTP的前世今生.md.html">01 时势与英雄:HTTP的前世今生.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/02 HTTP是什么?HTTP又不是什么?.md.html">02 HTTP是什么?HTTP又不是什么?.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/03 HTTP世界全览(上):与HTTP相关的各种概念.md.html">03 HTTP世界全览(上):与HTTP相关的各种概念.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/04 HTTP世界全览(下):与HTTP相关的各种协议.md.html">04 HTTP世界全览(下):与HTTP相关的各种协议.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/05 常说的“四层”和“七层”到底是什么?“五层”“六层”哪去了?.md.html">05 常说的“四层”和“七层”到底是什么?“五层”“六层”哪去了?.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/06 域名里有哪些门道?.md.html">06 域名里有哪些门道?.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/07 自己动手,搭建HTTP实验环境.md.html">07 自己动手,搭建HTTP实验环境.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/08 键入网址再按下回车,后面究竟发生了什么?.md.html">08 键入网址再按下回车,后面究竟发生了什么?.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/09 HTTP报文是什么样子的?.md.html">09 HTTP报文是什么样子的?.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/10 应该如何理解请求方法?.md.html">10 应该如何理解请求方法?.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/11 你能写出正确的网址吗?.md.html">11 你能写出正确的网址吗?.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/12 响应状态码该怎么用?.md.html">12 响应状态码该怎么用?.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/13 HTTP有哪些特点?.md.html">13 HTTP有哪些特点?.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/14 HTTP有哪些优点?又有哪些缺点?.md.html">14 HTTP有哪些优点?又有哪些缺点?.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/15 海纳百川:HTTP的实体数据.md.html">15 海纳百川:HTTP的实体数据.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/16 把大象装进冰箱:HTTP传输大文件的方法.md.html">16 把大象装进冰箱:HTTP传输大文件的方法.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/17 排队也要讲效率:HTTP的连接管理.md.html">17 排队也要讲效率:HTTP的连接管理.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/18 四通八达:HTTP的重定向和跳转.md.html">18 四通八达:HTTP的重定向和跳转.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/19 让我知道你是谁:HTTP的Cookie机制.md.html">19 让我知道你是谁:HTTP的Cookie机制.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/20 生鲜速递:HTTP的缓存控制.md.html">20 生鲜速递:HTTP的缓存控制.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/21 良心中间商:HTTP的代理服务.md.html">21 良心中间商:HTTP的代理服务.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/22 冷链周转:HTTP的缓存代理.md.html">22 冷链周转:HTTP的缓存代理.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/23 HTTPS是什么?SSLTLS又是什么?.md.html">23 HTTPS是什么?SSLTLS又是什么?.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/24 固若金汤的根本(上):对称加密与非对称加密.md.html">24 固若金汤的根本(上):对称加密与非对称加密.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/25 固若金汤的根本(下):数字签名与证书.md.html">25 固若金汤的根本(下):数字签名与证书.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/26 信任始于握手:TLS1.2连接过程解析.md.html">26 信任始于握手:TLS1.2连接过程解析.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a class="current-tab" href="/专栏/透视HTTP协议/27 更好更快的握手:TLS1.3特性解析.md.html">27 更好更快的握手:TLS1.3特性解析.md.html</a>
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/28 连接太慢该怎么办:HTTPS的优化.md.html">28 连接太慢该怎么办:HTTPS的优化.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/29 我应该迁移到HTTPS吗?.md.html">29 我应该迁移到HTTPS吗?.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/30 时代之风(上):HTTP2特性概览.md.html">30 时代之风(上):HTTP2特性概览.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/31 时代之风(下):HTTP2内核剖析.md.html">31 时代之风(下):HTTP2内核剖析.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/32 未来之路:HTTP3展望.md.html">32 未来之路:HTTP3展望.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/33 我应该迁移到HTTP2吗?.md.html">33 我应该迁移到HTTP2吗?.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/34 Nginx:高性能的Web服务器.md.html">34 Nginx:高性能的Web服务器.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/35 OpenResty:更灵活的Web服务器.md.html">35 OpenResty:更灵活的Web服务器.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/36 WAF:保护我们的网络服务.md.html">36 WAF:保护我们的网络服务.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/37 CDN:加速我们的网络服务.md.html">37 CDN:加速我们的网络服务.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/38 WebSocket:沙盒里的TCP.md.html">38 WebSocket:沙盒里的TCP.md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/39 HTTP性能优化面面观(上).md.html">39 HTTP性能优化面面观(上).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/40 HTTP性能优化面面观(下).md.html">40 HTTP性能优化面面观(下).md.html</a>
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/透视HTTP协议/结束语 做兴趣使然的Hero.md.html">结束语 做兴趣使然的Hero.md.html</a>
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
|
||
</div>
|
||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||
|
||
<div class="sidebar-toggle-inner"></div>
|
||
|
||
</div>
|
||
<script>
|
||
|
||
function add_inner() {
|
||
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
|
||
inner.classList.add('show')
|
||
|
||
}
|
||
function remove_inner() {
|
||
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
|
||
inner.classList.remove('show')
|
||
|
||
}
|
||
function sidebar_toggle() {
|
||
|
||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
|
||
let content = document.querySelector('.off-canvas-content')
|
||
|
||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||
|
||
sidebar_toggle.classList.remove('extend')
|
||
|
||
sidebar.classList.remove('hide')
|
||
|
||
content.classList.remove('extend')
|
||
|
||
} else { // hide
|
||
|
||
sidebar_toggle.classList.add('extend')
|
||
|
||
sidebar.classList.add('hide')
|
||
|
||
content.classList.add('extend')
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
function open_sidebar() {
|
||
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
|
||
sidebar.classList.add('show')
|
||
|
||
overlay.classList.add('show')
|
||
|
||
}
|
||
|
||
function hide_canvas() {
|
||
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
|
||
sidebar.classList.remove('show')
|
||
|
||
overlay.classList.remove('show')
|
||
|
||
}
|
||
</script>
|
||
<div class="off-canvas-content">
|
||
|
||
<div class="columns">
|
||
|
||
<div class="column col-12 col-lg-12">
|
||
|
||
<div class="book-navbar">
|
||
|
||
<!-- For Responsive Layout -->
|
||
|
||
<header class="navbar">
|
||
|
||
<section class="navbar-section">
|
||
|
||
<a onclick="open_sidebar()">
|
||
|
||
<i class="icon icon-menu"></i>
|
||
|
||
</a>
|
||
|
||
</section>
|
||
|
||
</header>
|
||
|
||
</div>
|
||
|
||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||
|
||
overflow-x: auto;
|
||
|
||
overflow-y: hidden;">
|
||
|
||
<div class="book-post">
|
||
|
||
<p id="tip" align="center"></p>
|
||
|
||
<div><h1>27 更好更快的握手:TLS1.3特性解析</h1>
|
||
|
||
<p>上一讲中我讲了 TLS1.2 的握手过程,你是不是已经完全掌握了呢?</p>
|
||
|
||
<p>不过 TLS1.2 已经是 10 年前(2008 年)的“老”协议了,虽然历经考验,但毕竟“岁月不饶人”,在安全、性能等方面已经跟不上如今的互联网了。</p>
|
||
|
||
<p>于是经过四年、近 30 个草案的反复打磨,TLS1.3 终于在去年(2018 年)“粉墨登场”,再次确立了信息安全领域的新标准。</p>
|
||
|
||
<p>在抓包分析握手之前,我们先来快速浏览一下 TLS1.3 的三个主要改进目标:<strong>兼容</strong>、<strong>安全与性能</strong>。</p>
|
||
|
||
<h2>最大化兼容性</h2>
|
||
|
||
<p>由于 1.1、1.2 等协议已经出现了很多年,很多应用软件、中间代理(官方称为“MiddleBox”)只认老的记录协议格式,更新改造很困难,甚至是不可行(设备僵化)。</p>
|
||
|
||
<p>在早期的试验中发现,一旦变更了记录头字段里的版本号,也就是由 0x303(TLS1.2)改为 0x304(TLS1.3)的话,大量的代理服务器、网关都无法正确处理,最终导致 TLS 握手失败。</p>
|
||
|
||
<p>为了保证这些被广泛部署的“老设备”能够继续使用,避免新协议带来的“冲击”,TLS1.3 不得不做出妥协,保持现有的记录格式不变,通过“伪装”来实现兼容,使得 TLS1.3 看上去“像是”TLS1.2。</p>
|
||
|
||
<p>那么,该怎么区分 1.2 和 1.3 呢?</p>
|
||
|
||
<p>这要用到一个新的<strong>扩展协议</strong>(Extension Protocol),它有点“补充条款”的意思,通过在记录末尾添加一系列的“扩展字段”来增加新的功能,老版本的 TLS 不认识它可以直接忽略,这就实现了“后向兼容”。</p>
|
||
|
||
<p>在记录头的 Version 字段被兼容性“固定”的情况下,只要是 TLS1.3 协议,握手的“Hello”消息后面就必须有“<strong>supported_versions</strong>”扩展,它标记了 TLS 的版本号,使用它就能区分新旧协议。</p>
|
||
|
||
<p>其实上一讲 Chrome 在握手时发的就是 TLS1.3 协议,你可以看一下“Client Hello”消息后面的扩展,只是因为服务器不支持 1.3,所以就“后向兼容”降级成了 1.2。</p>
|
||
|
||
<pre><code>Handshake Protocol: Client Hello
|
||
|
||
Version: TLS 1.2 (0x0303)
|
||
|
||
Extension: supported_versions (len=11)
|
||
|
||
Supported Version: TLS 1.3 (0x0304)
|
||
|
||
Supported Version: TLS 1.2 (0x0303)
|
||
|
||
</code></pre>
|
||
|
||
<p>TLS1.3 利用扩展实现了许多重要的功能,比如“supported_groups”“key_share”“signature_algorithms”“server_name”等,这些等后面用到的时候再说。</p>
|
||
|
||
<h2>强化安全</h2>
|
||
|
||
<p>TLS1.2 在十来年的应用中获得了许多宝贵的经验,陆续发现了很多的漏洞和加密算法的弱点,所以 TLS1.3 就在协议里修补了这些不安全因素。</p>
|
||
|
||
<p>比如:</p>
|
||
|
||
<ul>
|
||
|
||
<li>伪随机数函数由 PRF 升级为 HKDF(HMAC-based Extract-and-Expand Key Derivation Function);</li>
|
||
|
||
<li>明确禁止在记录协议里使用压缩;</li>
|
||
|
||
<li>废除了 RC4、DES 对称加密算法;</li>
|
||
|
||
<li>废除了 ECB、CBC 等传统分组模式;</li>
|
||
|
||
<li>废除了 MD5、SHA1、SHA-224 摘要算法;</li>
|
||
|
||
<li>废除了 RSA、DH 密钥交换算法和许多命名曲线。</li>
|
||
|
||
</ul>
|
||
|
||
<p>经过这一番“减肥瘦身”之后,TLS1.3 里只保留了 AES、ChaCha20 对称加密算法,分组模式只能用 AEAD 的 GCM、CCM 和 Poly1305,摘要算法只能用 SHA256、SHA384,密钥交换算法只有 ECDHE 和 DHE,椭圆曲线也被“砍”到只剩 P-256 和 x25519 等 5 种。</p>
|
||
|
||
<p>减肥可以让人变得更轻巧灵活,TLS 也是这样。</p>
|
||
|
||
<p>算法精简后带来了一个意料之中的好处:原来众多的算法、参数组合导致密码套件非常复杂,难以选择,而现在的 TLS1.3 里只有 5 个套件,无论是客户端还是服务器都不会再犯“选择困难症”了。</p>
|
||
|
||
<p><img src="assets/eeeb1d30acbc0e69541ce0620346b765.jpg" alt="img" /></p>
|
||
|
||
<p>这里还要特别说一下废除 RSA 和 DH 密钥交换算法的原因。</p>
|
||
|
||
<p>上一讲用 Wireshark 抓包时你一定看到了,浏览器默认会使用 ECDHE 而不是 RSA 做密钥交换,这是因为它不具有“<strong>前向安全</strong>”(Forward Secrecy)。</p>
|
||
|
||
<p>假设有这么一个很有耐心的黑客,一直在长期收集混合加密系统收发的所有报文。如果加密系统使用服务器证书里的 RSA 做密钥交换,一旦私钥泄露或被破解(使用社会工程学或者巨型计算机),那么黑客就能够使用私钥解密出之前所有报文的“Pre-Master”,再算出会话密钥,破解所有密文。</p>
|
||
|
||
<p>这就是所谓的“<strong>今日截获,明日破解</strong>”。</p>
|
||
|
||
<p>而 ECDHE 算法在每次握手时都会生成一对临时的公钥和私钥,每次通信的密钥对都是不同的,也就是“一次一密”,即使黑客花大力气破解了这一次的会话密钥,也只是这次通信被攻击,之前的历史消息不会受到影响,仍然是安全的。</p>
|
||
|
||
<p>所以现在主流的服务器和浏览器在握手阶段都已经不再使用 RSA,改用 ECDHE,而 TLS1.3 在协议里明确废除 RSA 和 DH 则在标准层面保证了“前向安全”。</p>
|
||
|
||
<h2>提升性能</h2>
|
||
|
||
<p>HTTPS 建立连接时除了要做 TCP 握手,还要做 TLS 握手,在 1.2 中会多花两个消息往返(2-RTT),可能导致几十毫秒甚至上百毫秒的延迟,在移动网络中延迟还会更严重。</p>
|
||
|
||
<p>现在因为密码套件大幅度简化,也就没有必要再像以前那样走复杂的协商流程了。TLS1.3 压缩了以前的“Hello”协商过程,删除了“Key Exchange”消息,把握手时间减少到了“1-RTT”,效率提高了一倍。</p>
|
||
|
||
<p>那么它是怎么做的呢?</p>
|
||
|
||
<p>其实具体的做法还是利用了扩展。客户端在“Client Hello”消息里直接用“<strong>supported_groups</strong>”带上支持的曲线,比如 P-256、x25519,用“<strong>key_share</strong>”带上曲线对应的客户端公钥参数,用“<strong>signature_algorithms</strong>”带上签名算法。</p>
|
||
|
||
<p>服务器收到后在这些扩展里选定一个曲线和参数,再用“key_share”扩展返回服务器这边的公钥参数,就实现了双方的密钥交换,后面的流程就和 1.2 基本一样了。</p>
|
||
|
||
<p>我为 1.3 的握手过程画了一张图,你可以对比 1.2 看看区别在哪里。</p>
|
||
|
||
<p><img src="assets/4d1df4d07dbb1c2500fc4ea69ecf7ab0.png" alt="img" /></p>
|
||
|
||
<p>除了标准的“1-RTT”握手,TLS1.3 还引入了“0-RTT”握手,用“pre_shared_key”和“early_data”扩展,在 TCP 连接后立即就建立安全连接发送加密消息,不过这需要有一些前提条件,今天暂且不说。</p>
|
||
|
||
<h2>握手分析</h2>
|
||
|
||
<p>目前 Nginx 等 Web 服务器都能够很好地支持 TLS1.3,但要求底层的 OpenSSL 必须是 1.1.1,而我们实验环境里用的 OpenSSL 是 1.1.0,所以暂时无法直接测试 TLS1.3。</p>
|
||
|
||
<p>不过我在 Linux 上用 OpenSSL1.1.1 编译了一个支持 TLS1.3 的 Nginx,用 Wireshark 抓包存到了 GitHub 上,用它就可以分析 TLS1.3 的握手过程。</p>
|
||
|
||
<p><img src="assets/7a2bc39fdbb421cf72a01e887e9156db.png" alt="img" /></p>
|
||
|
||
<p>在 TCP 建立连接之后,浏览器首先还是发一个“<strong>Client Hello</strong>”。</p>
|
||
|
||
<p>因为 1.3 的消息兼容 1.2,所以开头的版本号、支持的密码套件和随机数(Client Random)结构都是一样的(不过这时的随机数是 32 个字节)。</p>
|
||
|
||
<pre><code>Handshake Protocol: Client Hello
|
||
|
||
Version: TLS 1.2 (0x0303)
|
||
|
||
Random: cebeb6c05403654d66c2329…
|
||
|
||
Cipher Suites (18 suites)
|
||
|
||
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
|
||
|
||
Cipher Suite: TLS_CHACHA20_POLY1305_SHA256 (0x1303)
|
||
|
||
Cipher Suite: TLS_AES_256_GCM_SHA384 (0x1302)
|
||
|
||
Extension: supported_versions (len=9)
|
||
|
||
Supported Version: TLS 1.3 (0x0304)
|
||
|
||
Supported Version: TLS 1.2 (0x0303)
|
||
|
||
Extension: supported_groups (len=14)
|
||
|
||
Supported Groups (6 groups)
|
||
|
||
Supported Group: x25519 (0x001d)
|
||
|
||
Supported Group: secp256r1 (0x0017)
|
||
|
||
Extension: key_share (len=107)
|
||
|
||
Key Share extension
|
||
|
||
Client Key Share Length: 105
|
||
|
||
Key Share Entry: Group: x25519
|
||
|
||
Key Share Entry: Group: secp256r1
|
||
|
||
</code></pre>
|
||
|
||
<p>注意“Client Hello”里的扩展,“<strong>supported_versions</strong>”表示这是 TLS1.3,“<strong>supported_groups</strong>”是支持的曲线,“<strong>key_share</strong>”是曲线对应的参数。</p>
|
||
|
||
<p>这就好像是说:</p>
|
||
|
||
<p>“还是照老规矩打招呼,这边有这些这些信息。但我猜你可能会升级,所以再多给你一些东西,也许后面用的上,咱们有话尽量一口气说完。”</p>
|
||
|
||
<p>服务器收到“Client Hello”同样返回“Server Hello”消息,还是要给出一个<strong>随机数</strong>(Server Random)和选定密码套件。</p>
|
||
|
||
<pre><code>Handshake Protocol: Server Hello
|
||
|
||
Version: TLS 1.2 (0x0303)
|
||
|
||
Random: 12d2bce6568b063d3dee2…
|
||
|
||
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
|
||
|
||
Extension: supported_versions (len=2)
|
||
|
||
Supported Version: TLS 1.3 (0x0304)
|
||
|
||
Extension: key_share (len=36)
|
||
|
||
Key Share extension
|
||
|
||
Key Share Entry: Group: x25519, Key Exchange length: 32
|
||
|
||
</code></pre>
|
||
|
||
<p>表面上看和 TLS1.2 是一样的,重点是后面的扩展。“<strong>supported_versions</strong>”里确认使用的是 TLS1.3,然后在“<strong>key_share</strong>”扩展带上曲线和对应的公钥参数。</p>
|
||
|
||
<p>服务器的“Hello”消息大概是这个意思:</p>
|
||
|
||
<p>“还真让你给猜对了,虽然还是按老规矩打招呼,但咱们来个‘旧瓶装新酒’。刚才你给的我都用上了,我再给几个你缺的参数,这次加密就这么定了。”</p>
|
||
|
||
<p>这时只交换了两条消息,客户端和服务器就拿到了四个共享信息:<strong>Client Random</strong>和<strong>Server Random</strong>、<strong>Client Params</strong>和<strong>Server Params</strong>,两边就可以各自用 ECDHE 算出“<strong>Pre-Master</strong>”,再用 HKDF 生成主密钥“<strong>Master Secret</strong>”,效率比 TLS1.2 提高了一大截。</p>
|
||
|
||
<p>在算出主密钥后,服务器立刻发出“<strong>Change Cipher Spec</strong>”消息,比 TLS1.2 提早进入加密通信,后面的证书等就都是加密的了,减少了握手时的明文信息泄露。</p>
|
||
|
||
<p>这里 TLS1.3 还有一个安全强化措施,多了个“<strong>Certificate Verify</strong>”消息,用服务器的私钥把前面的曲线、套件、参数等握手数据加了签名,作用和“<strong>Finished</strong>”消息差不多。但由于是私钥签名,所以强化了身份认证和和防窜改。</p>
|
||
|
||
<p>这两个“Hello”消息之后,客户端验证服务器证书,再发“Finished”消息,就正式完成了握手,开始收发 HTTP 报文。</p>
|
||
|
||
<p>虽然我们的实验环境暂时不能抓包测试 TLS1.3,但互联网上很多网站都已经支持了 TLS1.3,比如<a href="https://www.nginx.com/">Nginx</a>、<a href="https://github.com/">GitHub</a>,你可以课后自己用 Wireshark 试试。</p>
|
||
|
||
<p>在 Chrome 的开发者工具里,可以看到这些网站的 TLS1.3 应用情况。</p>
|
||
|
||
<p><img src="assets/44d8c3349ffdea5a1e4e13d222bc743c.png" alt="img" /></p>
|
||
|
||
<h2>小结</h2>
|
||
|
||
<p>今天我们一起学习了 TLS1.3 的新特性,用抓包研究了它的握手过程,不过 TLS1.3 里的内容很多,还有一些特性没有谈到,后面会继续讲。</p>
|
||
|
||
<ol>
|
||
|
||
<li>为了兼容 1.1、1.2 等“老”协议,TLS1.3 会“伪装”成 TLS1.2,新特性在“扩展”里实现;</li>
|
||
|
||
<li>1.1、1.2 在实践中发现了很多安全隐患,所以 TLS1.3 大幅度删减了加密算法,只保留了 ECDHE、AES、ChaCha20、SHA-2 等极少数算法,强化了安全;</li>
|
||
|
||
<li>TLS1.3 也简化了握手过程,完全握手只需要一个消息往返,提升了性能。</li>
|
||
|
||
</ol>
|
||
|
||
<h2>课下作业</h2>
|
||
|
||
<ol>
|
||
|
||
<li>TLS1.3 里的密码套件没有指定密钥交换算法和签名算法,那么在握手的时候会不会有问题呢?</li>
|
||
|
||
<li>结合上一讲的 RSA 握手过程,解释一下为什么 RSA 密钥交换不具有“前向安全”。</li>
|
||
|
||
<li>TLS1.3 的握手过程与 TLS1.2 的“False Start”有什么异同?</li>
|
||
|
||
</ol>
|
||
|
||
<p>欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。</p>
|
||
|
||
<p><img src="assets/ab532f0074ddb136cd96c76c3a385164.png" alt="unpreview" /></p>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div>
|
||
|
||
<div style="float: left">
|
||
|
||
<a href="/专栏/透视HTTP协议/26 信任始于握手:TLS1.2连接过程解析.md.html">上一页</a>
|
||
|
||
</div>
|
||
|
||
<div style="float: right">
|
||
|
||
<a href="/专栏/透视HTTP协议/28 连接太慢该怎么办:HTTPS的优化.md.html">下一页</a>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||
|
||
</div>
|
||
|
||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997d1c0f893cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||
|
||
</body>
|
||
|
||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||
|
||
<script>
|
||
|
||
window.dataLayer = window.dataLayer || [];
|
||
function gtag() {
|
||
|
||
dataLayer.push(arguments);
|
||
|
||
}
|
||
gtag('js', new Date());
|
||
|
||
gtag('config', 'G-NPSEEVD756');
|
||
|
||
var path = window.location.pathname
|
||
|
||
var cookie = getCookie("lastPath");
|
||
|
||
console.log(path)
|
||
|
||
if (path.replace("/", "") === "") {
|
||
|
||
if (cookie.replace("/", "") !== "") {
|
||
|
||
console.log(cookie)
|
||
|
||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
setCookie("lastPath", path)
|
||
|
||
}
|
||
function setCookie(cname, cvalue) {
|
||
|
||
var d = new Date();
|
||
|
||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||
|
||
var expires = "expires=" + d.toGMTString();
|
||
|
||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||
|
||
}
|
||
function getCookie(cname) {
|
||
|
||
var name = cname + "=";
|
||
|
||
var ca = document.cookie.split(';');
|
||
|
||
for (var i = 0; i < ca.length; i++) {
|
||
|
||
var c = ca[i].trim();
|
||
|
||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||
|
||
}
|
||
|
||
return "";
|
||
|
||
}
|
||
</script>
|
||
</html>
|
||
|