一篇搞懂SSL/TLS协议(https的握手)

SSL/TLS的傻瓜教程

  • 第1部分:密码套件,散列,加密
    • SSL的历史
    • 关于加密的一些注意事项
    • 散列(Hashing)
    • 加密
      • 对称加密
      • 非对称加密
      • 我们可以对所有TLS使用非对称加密吗
  • 第2部分:了解密钥交换算法
    • 了解SSL中的加密类型
    • 密钥交换算法
    • Diffie-Hellman密钥交换说明
    • Diffie-Hellman密钥交换数学原理
    • 我们现在安全吗?
    • 该死,现在怎么办?
  • 第3部分:了解证书颁发机构
    • 证书颁发机构需要什么?
    • 数字签名
    • 认证中心使用的技术
    • 如果中间人攻击篡改了证书会怎么?
    • CA的信任链
    • 数字签名的数学算法
    • 浏览器实际上如何验证给定服务器证书的有效性?
  • 第4部分:理解TLS握手协议
    • DNS解析
    • 发起TLS握手
      • 客户端发送hello消息
      • 服务端发送hello消息
      • 服务器证书消息
      • 服务器密钥交换
      • 如何计算准主密钥(Pre-Master Secret)
      • 客户端密钥交换
      • 如何计算Master Secre
      • 消息验证码(MAC)和TLS数据完整性
      • 生成测试数据
      • 验证谈判

在网络上看到很多关于https握手过程的博文,总是感觉不是很系统详细,也没有实验结果进行支撑。最后找到了一个国外程序员博客上的关于TLS协议的详细解读,感觉非常详细系统,原文是英文的,所以我就用谷歌翻译简单翻译并转载,原文链接
https://www.wst.space/ssl-part1-ciphersuite-hashing-encryption/
https://www.wst.space/ssl-part-2-diffie-hellman-key-exchange/
https://www.wst.space/ssl-part-3-certificate-authority/
https://www.wst.space/ssl-part-4-tls-handshake-protocol/
简单来说,前两部分主要讲了加密的一些基础知识,对称加密,非对称加密,哈希,密钥交换算法等吗,如果没有基础的话可以仔细看看前面两个部分,第三部分,重点是为了解决中间人攻击的问题,而引入了CA机制,并详细介绍了这个机制的原理,第四部分,介绍了https的握手流程,并通过WireShark抓包工具,分析整个过程,并且通过参考了一些标准文档,说明了如何计算得到密钥的过程。

第1部分:密码套件,散列,加密

作为安全爱好者,我一直很喜欢SSL(如今是TLS)的工作方式。我花了几天的时间来理解这个复杂协议的基本原理。但是,一旦您了解了基本概念和算法,整个协议就会变得非常简单。在学习SSL的过程中,我学到了很多东西。数据加密技术是第一件事。我开始收集从大学中学到的密码学知识。那些日子,当我学习它们的时候就像一个?( Those days, I was like meh while studying them)。现在,我知道了为什么老师让我学好所有加密工作。我知道密码学使我的生活更轻松。我想在我的空间中分享学到的一切。我绝对希望这对您有用。因此,让我们开始吧。

SSL的历史

在谈论SSL的历史时,不应错过Mozilla Foundation。在谈论Mozilla时,我们想到的第一件事就是他们著名的浏览器Firefox。根据各种消息来源,Firefox是继Chrome和Safari之后最受欢迎的浏览器。但是Netscape是Firefox的伟大前身,在90年代,它是互联网冲浪者中最受欢迎的浏览器。无论如何,通过Microsoft引入Internet Explorer,Netscape的时代结束了,后来他们创立了伟大的Mozilla Foundation,并且它还在不断发展。Netscape在1994年为其Netscape Navigator浏览器引入了SSL。主要目标是防止中间人袭击。后来,随着互联网可访问性的增加,银行开始利用互联网进行交易。那个时候,安全是一个主要问题,Internet标准化工作组IETF (Internet工程任务组)通过制作自己的版本来标准化SSL。这是在1999年,现在该协议被称为TLS(传输层安全性,最新版本是TLS 1.3)。

关于加密的一些注意事项

首先,在深入探讨主题之前,我们需要对几件事有基本的了解。最重要的是密码学。您无需成为密码学专家即可了解SSL。但是需要基本的了解。我们将在这里讨论基本知识。那些已经知道非对称和对称密钥加密的人可以跳过本节,转到下一部分。因此,密码学处理数字和字符串。基本上,整个宇宙中的每个数字事物都是数字。当我说数字时,它是0和1。您知道它们是什么,二进制。您在屏幕上看到的图像,通过耳机收听的音乐以及所有内容都是二进制文件。但是我们的耳朵和眼睛不会理解二进制文件吗?只有大脑才能理解这一点,即使它能够理解二进制文件,也无法享受二进制文件。因此,我们将二进制文件转换为人类可以理解的格式,例如mp3,jpg等。我们将该过程称为Encoding 。这是双向处理,可以很容易地解码回其原始形式。

散列(Hashing)

散列是另一种加密技术,其中数据一旦转换为其他形式就永远无法恢复。用Layman的术语来说,没有所谓的散列处理。有许多哈希函数可以完成此任务,例如sha-512,md5等。如果无法恢复原始值,那么我们将在哪里使用它呢?密码!为手机或PC设置密码时,会创建密码的哈希并将其存储在安全的地方。下次尝试登录时,将使用相同的算法(哈希函数)再次对输入的字符串进行哈希处理,并将输出与存储的值进行匹配。如果相同,则登录。否则,您将被丢弃。
一篇搞懂SSL/TLS协议(https的握手)_第1张图片

加密

加密介于散列和编码之间。编码是一种双向过程,不应用于提供安全性。加密也是一种双向过程,但是只有在知道加密密钥的情况下,才可以检索原始数据。如果您不知道加密的工作原理,请放心,我们将在这里讨论基础知识。这足以了解SSL的基础知识。因此,有两种加密类型,即对称加密和非对称加密。

对称加密

我试图使事情尽可能简单。因此,让我们通过移位算法来了解对称加密。该算法用于通过将字母向左或向右移动来加密字母。让我们以字符串CRYPTO来考虑数字+3。然后,CRYPTO的加密格式将为FUBSWR。这意味着每个字母向右移3个位置。在这里,单词CRYPTO被称为纯文本,输出FUBSWR被称为密文,值+3被称为加密 密钥(对称密钥),整个过程是一个密码。这是最古老且基本的对称密钥加密算法之一,在朱利叶斯·凯撒(Julius Caesar)时代曾首次报道其使用方法。因此,它以他的名字命名,并且是著名的凯撒密码。任何知道加密密钥并可以应用凯撒算法相反方法并检索原始纯文本的人。因此,它称为对称加密。
一篇搞懂SSL/TLS协议(https的握手)_第2张图片

非对称加密

我们知道,在对称加密中,相同的密钥用于加密和解密。一旦密钥被盗,所有数据都将消失。那是巨大的风险,我们需要更复杂的技术。1976年,Whitfield Diffie和Martin Hellman首次发布了非对称加密的概念,该算法被称为Diffie-Hellman密钥交换。然后在1978年,麻省理工学院的Ron Rivest,Adi Shamir和Leonard Adleman发表了RSA 算法。这些可以被认为是非对称密码学的基础。
一篇搞懂SSL/TLS协议(https的握手)_第3张图片
与对称加密相比,在非对称加密中,将有两个密钥而不是一个。一个称为公钥,另一个称为私钥。从理论上讲,在启动过程中,我们可以生成公共-私人我们机器的密钥对。私钥应保存在安全的地方,并且绝不应该与任何人共享。顾名思义,公钥可以与希望向您发送加密文本的任何人共享。现在,拥有您的公共密钥的人可以使用它来加密秘密数据。如果密钥对是使用RSA算法生成的,则它们在加密数据时应使用相同的算法。通常,算法将在公共密钥中指定。加密的数据只能用您拥有的私钥解密。

我们可以对所有TLS使用非对称加密吗

非对称加密也称为公钥基础结构(也称为PKI),原因不言而喻。无论如何,只要保持私钥的安全,数据都是安全的。确实不错!因此,大概到现在您可能会想,为什么我们仍在TLS中使用对称加密?我们有很多安全的PKI 。是的,同意,但是应该指出,必须在不影响可用性的情况下处理安全性。由于PKI涉及双密钥体系结构,并且密钥长度通常较大,因此加密/解密开销非常高。与对称密钥加密相比,它需要更多的时间和CPU使用率。因此,当在客户端和服务器之间发送和接收数据时,用户会感觉到更多的等待时间,并且浏览器将开始占用CPU。因此,PKI仅用于在客户端和服务器之间交换对称密钥。此后,对称密钥加密开始起作用,并且进一步的数据传输利用了该技术。

第2部分:了解密钥交换算法

本部分将根据Diffie-Hellman密钥交换来讨论密钥交换算法

了解SSL中的加密类型

假设您正在浏览Facebook。默认情况下,Facebook通过https重新路由您的所有流量。由于您使用的是TLS(现在大多数情况下我将在大多数地方使用TLS代替SSL,因为它是现在的标准)连接,因此您会在URL栏上看到一个绿色框,确认该连接是安全的。在单个会话中,您将进行多种活动,例如评论,聊天,在页面之间导航,滚动新闻提要等。每次执行此操作时,客户端和服务器之间都会共享多个请求和响应。所有这些通信都必须通过https进行,以确保数据安全。这意味着服务器和客户端浏览器正在为单个Facebook会话加密和解密数据包数百次。
一篇搞懂SSL/TLS协议(https的握手)_第4张图片
我们知道公钥加密比对称密钥加密更安全,因为解密密钥永远不会与任何人共享。但是,我们知道,在公钥基础结构(PKI)中,开销非常高。与对称密钥密码术相比,它使用更多的CPU并花费更多的时间进行加密和解密。结果,浏览器(和应用程序服务器)开始消耗您的CPU资源。而且,浏览器将花费更多时间来提供内容,因为每次都必须经历繁忙的加密步骤。解决办法是什么? 因此,我们使用对称加密来实现这一点,它更快,资源消耗更少。都好。但是客户端和服务器必须在开始加密之前就同意一个秘密密钥,对吗?他们将如何做?在共享唯一密钥的同时,坐在客户端和服务器之间的攻击者可以捕获它和Kaboom!您所有的数据都消失了。因此,必须有一种共享密钥的解决方法,并且在这里我们使用了公共密钥加密技术。同意一个秘密密钥并在客户端和服务器之间共享它称为握手,这是TLS的第一步。握手涉及多个过程。整个过程统称为公钥基础结构。还记得我们在系列的最后一部分中使用的术语吗?PKI包括证书颁发机构(CA),数字签名等。我们将在下面深入讨论基础架构。

密钥交换算法

因此,很显然,使用非对称加密来交换密钥,但是哪种算法呢?自从非对称密码术发明以来,提出了许多算法。在撰写本文时,TLS 1.2是常用的标准,RSA,Diffie-Hellman密钥交换,ECDH(椭圆曲线Diffie-Hellman),SRP(安全远程密码),PSK(预共享密钥)是密钥交换算法。 TLS 1.2支持。
在这里讨论所有算法可能不是一个好主意。相反,我们将讨论最常见且易于理解的Diffie-Hellman密钥交换算法。

Diffie-Hellman密钥交换说明

我不会直接上数学,因为我很弱。相反,让我们尝试从颜色比喻的角度来理解概念。想象一下,爱丽丝和鲍勃正在做一些海报工作。他们的对手马洛里也坐在板凳上。爱丽丝(Alice)和鲍勃(Bob)希望就单一颜色达成一致,以设计海报。他们无法大声讨论,因为马洛里会听到。那么它们将如何达到共同的颜色?解决方案是Diffie-Hellman密钥交换算法的最简单形式。让我们看看如何。
步骤:

  1. 首先,爱丽丝将选择一种常见的颜色,例如黄色,并告诉鲍勃,她将在本次会议中使用黄色。显然,马洛里会听见,没关系。
  2. 爱丽丝(Alice)和鲍勃(Bob)现在选择他们自己的秘密颜色,并且他们之间将不会共享它们。因此,马洛里永远不会知道秘密的颜色。例如,爱丽丝选择秘密颜色橙色,而鲍勃选择绿色。
  3. 在这一步中,爱丽丝将混合她的秘密颜色橙色和普通颜色黄色以产生新的颜色。
  4. 同样,鲍勃也将他的秘密颜色混合为黄色,以生成新的颜色蓝色。
  5. 爱丽丝和鲍勃将在他们之间共享这些新颜色。马洛里可以看到凉鞋颜色和蓝色,但看不到它们的秘密颜色。
  6. 交换完成后,爱丽丝将自己的秘密颜色(橙色)混入鲍勃发送的混合物中。Bob会将他的秘密颜色(绿色)混合到Alice发送的混合物中。
  7. 现在,爱丽丝和鲍勃都达到了一种共同的秘密色彩。请参考下图。Mallory将被Sandal和Blue所卡住,并且永远无法达到常见的秘密颜色,因为Mallory不知道Alice和Bob的Secret颜色。
    一篇搞懂SSL/TLS协议(https的握手)_第5张图片

Diffie-Hellman密钥交换数学原理

让我们找出上述算法背后的基本数学原理。我们需要具有模块化算术的概念,以便更好地理解Diffie-Hellman的概念。那些不希望数学的人可以跳过本部分,其他人可以接着往下看。
你知道,将7和8相加得到15。这是正常的算术运算。但这在12小时时钟的情况下是不正确的。如果现在的时间是7点,那么8小时后,时间将是3点。因此,我们可以说时钟是算术模数为12的最简单的模块化算术示例。在这种情况下,我们知道12:00类似于00:00,因此我们可以说12等于0,反之亦然。
数学上:A = b(mod p), 如果我们取b=21, p=12可以得到:21 (mod 12) = 9
让我们将其转换为Diffie-Hellman示例。阅读以下内容时,请记住颜色比喻。想象一下,爱丽丝和鲍勃都知道g和p的值,或者爱丽丝先前已经决定了这些值并将其发送给鲍勃。换句话说,这些值是公开的。
一篇搞懂SSL/TLS协议(https的握手)_第6张图片
观察到SA=SB=K,这是用于加密会话的会话密钥,那马洛里获得秘密钥匙的机会呢?
在整个过程中,请注意,Alice(a)和Bob(b)的秘密永远不会彼此共享。因此,马洛里将只知道g,p,A和B。要获得K的值,马洛里首先需要根据A = g a(mod p)和B = g b(mod p)计算a&b 。这称为离散对数问题。对于较大的p值,发现这种机制几乎是100%不可行的。在实际的TLS实现中,p的长度将在1024或2048位的范围内。也就是说,2048位密钥的长度在 2 2047和 22048之间。希望你知道 2 3 长度密钥的最大值可以为8。想象一下2048位密钥的复杂性。

使用此类密钥时,世界上最大的超级计算机将需要100多年的时间来计算a和b。但是,这些值随每个会话而变化。因此,即使攻击者计算了此值,他也无法在随后的会话中使用它们来模拟用户。这就是所谓的“ 完全向前保密” 。

我们现在安全吗?

服务器和客户端浏览器已经商定了一个秘密密钥,该密钥可以通过强密钥交换算法安全地共享。一切看起来都很好。但是等等,我足够安全吗?让我们想象一个场景,我们试图通过https连接到facebook.com。假设攻击者已经插入到你的浏览器和Facebook服务器之间。你的浏览器将告诉Facebook服务器启动TLS通道。但是攻击者可以设置自己的服务器,并通过其服务器重新路由您与Facebook.com之间的所有通信。因此,当Facebook服务器发送其公钥时,攻击者可以将其替换为他的公钥并将其转发给您。你还是获取到的是攻击者的公钥。
进入下一步。您会收到公开密钥,认为它实际上是来自Facebook.com,您的浏览器会使用它加密您的秘密密钥,并将其发送回Facebook。再次,攻击者将抓住它,猜猜会发生什么?他具有相应的私钥来解密该私钥,然后使用Facebook.com的公钥(他已经拥有)的原始值对其进行加密,然后将其转发回Facebook.com。他将继续执行此加密-解密过程,并且可以看到您和Facebook.com之间正在共享的所有内容。

该死,现在怎么办?

问题的答案是CA(证书颁发机构)。简而言之,X.509标准指定了证书颁发机构以确保数据完整性。数据完整性可确保传输中的数据不会被第三方实体篡改。换句话说,CA充当浏览器和服务器之间的中间人。确保数据完整性是CA的工作。

第3部分:了解证书颁发机构

我们根据Diffie Hellman算法讨论了SSL / TLS密钥交换。我们最终想到了需要第三方来验证服务器的真实性。并提出了证书颁发机构的名称。博客系列的后2部分的主要内容:

  • TLS加密客户端-服务器通信,并阻止中间人攻击。
  • 编码,散列和加密之间的区别。
  • TLS使用对称密钥加密来加密数据,并使用公共密钥基础结构(非对称加密)交换对称密钥。
  • 密钥交换算法本身可以被攻击者欺骗。因此,我们需要一个值得信赖的机构来验证服务器的真实性。

证书颁发机构需要什么?

想象一下,客户端浏览器正在尝试与Web服务器通信,并且它想启动TLS通道。从上面的最后一点出发,为了证明服务器的身份,客户端浏览器必须具有服务器的公钥。但是,不可能在浏览器中存储我们访问的所有网站的公钥。由于更新的网站每分钟都会诞生一次,因此需要每分钟进行更新。
解决方案是证书颁发机构。当我们安装浏览器或操作系统时,它将附带一组证书颁发机构。DigiCert就是这样一个例子。当我说浏览器随附DigiCert时,表示浏览器具有DigiCert的公钥。网站可以从DigiCert请求证书和签名。因此DigiCert将使用DigiCerts私钥对服务器证书进行加密签名。当我们启动连接时,服务器将发送嵌入了其公钥的证书。由于浏览器具有DigiCert的公钥,因此可以在服务器证书上验证DigiCert的签名。因此,写在证书上的服务器的公钥也是可信的。

数字签名

要了解证书颁发机构的概念,我们可以追溯到几十年前,并考虑一下传统的邮政邮件系统。想象一下,爱丽丝拥有一家公司,而鲍勃是该公司的雇员。爱丽丝想向鲍勃发送一封机密邮件。作为首席执行官的爱丽丝(Alice)将起草并将其放入邮筒中。它会穿过几个邮局和几个邮递员,最后到达鲍勃的手里。鲍勃现在可以打开它并阅读。但是Bob怎样才能确保邮件实际上来自Alice?这里有两种可能性:

  • 夏娃说,攻击者可以草拟包含攻击性的邮件,将发件人地址设置为类似于爱丽丝办公室的地址,然后将其转发给鲍勃。
  • 夏娃可以是中间人,例如中间邮局的雇员,他可以在邮件到达鲍勃之前打开。他甚至可以根据需要重写内容,将其粘贴回去,然后发送回给Bob。
    在这两种情况下,都无法确保从Alice收到的邮件是合法的。在这种情况下,我们该怎么办?是的,签名。爱丽丝在将邮件发布到鲍勃时可以使用印章和签名。在这里,爱丽丝的公司印章可用于验证电子邮件的真实性和完整性。由于爱丽丝的公司是公认的实体,因此我们可以信任带有签名的邮件。这正是证书颁发机构的工作。

认证中心使用的技术

我们知道PKI用于交换TLS协议中的会话密钥。该过程可以称为认证过程。为了执行认证过程,服务器需要与客户端发送公钥。但是中级攻击者可以获取此公钥并将其替换为自己的公钥。这很危险,因为客户端永远不会知道公钥在传输过程中遭到篡改。客户端会在不知不觉中用攻击者的公钥加密对称密钥并将其转发。由于攻击者拥有相应的私钥,因此他可以解密私钥并窃取数据。
为了使客户信任接收到的公钥,引入了CA的概念。CA的工作如下。假设服务器https://example.com需要TLS证书。

  1. 服务器example.com将向CA请求TLS证书。例如Digicert。
  2. Digicert将为example.com创建证书。证书将包含必要的数据,例如服务器名称,服务器的公共密钥等。、
  3. Digicert将创建数据(证书)的哈希,并使用自己的私钥对其进行加密。
  4. 浏览器和操作系统附带了Authorcert的公共密钥,例如Digicert。
  5. 当浏览器收到签名证书时,它将使用公钥从签名生成哈希。它还将使用证书中指定的哈希算法生成数据(证书)的哈希。如果两个哈希都匹配,则签名验证成功并且证书受信任。
  6. 现在,浏览器可以使用证书中指定的example.com的公钥继续进行身份验证过程。
    在这里,我们可以称之为Digicert一个根CA 。
    一篇搞懂SSL/TLS协议(https的握手)_第7张图片

如果中间人攻击篡改了证书会怎么?

收到证书后,浏览器将验证数据,例如服务器名称,证书的有效性,签名等。想象一下,如果攻击者使用了他的自定义证书而不是example.com的证书。然后,“服务器名称”字段验证将失败,浏览器将立即断开连接。
在另一种情况下,如果攻击者保留所有此类数据,并仅用其公钥替换公钥,将会发生什么?在这种情况下,当浏览器尝试从证书数据中重新生成哈希值时,由于数据被篡改,他将获得一个不同的哈希值。因此,根据数据和签名计算出的哈希值将不匹配。
要绕过上述机制,攻击者将需要使签名与数据匹配。为此,他需要拥有Digicert(最初为example.com颁发并签名证书)的私钥。攻击者此时将失败,因为他可以创建的唯一签名来自他的私钥。这将不受我们的浏览器的信任。浏览器的证书存储区将没有攻击者的公钥,并且在发生此类攻击时将显示证书异常,如下所示。
一篇搞懂SSL/TLS协议(https的握手)_第8张图片
在尝试为浏览器设置代理时,您可能已经注意到了这一点。发生隐私错误是因为代理工具在中间充当着一个人,并向浏览器显示了自己的证书。如果您信任证书,则可以通过显示信任来单击继续。或者,您可以下载代理工具的证书,并将其添加到浏览器内的受信任权限列表中。这样,您可以在代理工具内以纯文本格式查看加密的数据。

CA的信任链

我们知道认证中心会为服务器创建并签署证书。从事这项工作的组织很少,即Digicert,Geotrust,Comodo等。如果他们正在为所有服务器签名证书,则必须对所有签名使用相同的私钥。如果被盗,那么所有的信任都会丢失。为了解决该问题并增加更多的熵,引入了中间CA的概念。
这个想法很简单。查尔斯(Charles)是可信任的人,曾经用来签署爱丽丝(Alice)的邮件。如果鲍勃看到查尔斯的签名,便会信任该邮件。现在,史密斯是查尔斯信任的另一个人。现在,如果史密斯(Smith)代表查尔斯(Charles)签署了爱丽丝(Alice)的邮件,那么鲍勃(Bob)通常不会信任它。但是,这里有了信任的想法链。鲍勃信任查尔斯,查尔斯信任史密斯。因此,鲍勃可以信任史密斯。同样,中间CA是受根CA信任的证书颁发机构。example.com的证书将由中间CA颁发。中间CA也将具有由根CA签名的证书。并且只有根CA详细信息将存储在浏览器的证书存储中。
因此,在证书验证期间,浏览器会信任Digicert RootCA。Digicert Root CA信任中间CA,因此浏览器可以信任中间CA。在下图中,您可以看到层次结构,DigiCert SHA2 High Assurance Server CADigiCert High Assurance EV RootCA的中间证书颁发机构。此层次结构的另一个优点是,根CA不必始终处于联机状态。
一篇搞懂SSL/TLS协议(https的握手)_第9张图片

数字签名的数学算法

我们在了解密钥交换过程的同时讨论了Diffie-Hellman算法。同样,也有许多算法可用于数字签名。这将在服务器证书中指定。请参阅下面的example.com证书。
一篇搞懂SSL/TLS协议(https的握手)_第10张图片
该证书显示具有RSA加密的SHA-256。RSA是一种流行的签名算法,我们将在这里讨论。像任何其他非对称加密算法一样,RSA也具有公钥-私钥对。此处的区别在于,签名(将其视为加密)是通过使用中间CA的私钥完成的。浏览器使用相应的公钥完成签名验证(将其视为解密)。
RSA将对证书进行哈希处理,然后再对其进行签名。有一个重要的原因。如果您深入了解该算法,则将知道如果数据长度大于其密钥长度,则RSA无法加密数据。假设我们使用2048位密钥进行加密,那么证书数据不应超过2048位(即255个字节)。这并不总是可行的,因为证书包含了很多信息。因此,在加密之前,将哈希函数应用于证书,该证书将生成指定长度的唯一随机字符串。如果使用example.com,则使用SHA-256哈希算法。

浏览器实际上如何验证给定服务器证书的有效性?

我们知道服务器使用中间证书颁发机构的签名。因此,在与浏览器通信时,服务器将共享两个证书。一个,其中包含服务器的公共密钥,它是实际的服务器证书。其次,由根CA颁发的中间CA证书。这是验证链的图形表示。
一篇搞懂SSL/TLS协议(https的握手)_第11张图片
在签名验证期间,浏览器首先使用根CA的公共密钥验证中间证书的数字签名,该证书已经存储在浏览器中。如果成功,浏览器现在可以信任中间证书及其公钥。现在,使用此公用密钥,浏览器将验证原始服务器证书的签名。组织可以注册为中间CA,以为其域签名证书。谷歌就是这样一个例子。
一篇搞懂SSL/TLS协议(https的握手)_第12张图片
Google Internet Authority G3是由GlobalSign根CA – R2信任的中间CA。这意味着Google可以通过此中间CA验证其域。由于浏览器是随GlobalSign Root CA一起提供的,因此浏览器将信任它们。必须注意,Google被授权单独注册其域名。这样可以防止Google为Microsoft签名证书。

第4部分:理解TLS握手协议

这个部分探讨了整个SSL / TLS握手过程
在这篇文章中,使用名为WireShark的工具来查看网络流量。我正在使用Linux(Ubuntu 16.04)计算机,可以使用以下命令轻松安装WireShark:

$ sudo apt install wireshark 

以sudo的身份打开WireShark,然后选择提供Internet连接的接口。在我的情况下是eth0。然后按WireShark右上角的“开始捕获数据包”按钮。Wireshark将立即开始捕获通过计算机的全部流量。现在,让我们在浏览器中加载github.com。Github使用TLS进行所有通信。因此它将重定向到https,并且github.com将被加载到https中。现在关闭浏览器,查看WireShark的功能。

DNS解析

这不是TLS的一部分,但我们能在WireShark中看到它。
一篇搞懂SSL/TLS协议(https的握手)_第13张图片
我已将Google DNS设置为我的DNS服务器。地址为8.8.8.8。在该图中可以看到,已经向8.8.8.8发送了一个查询github.com的A地址的请求。地址是我们要连接的Github的IP。
一篇搞懂SSL/TLS协议(https的握手)_第14张图片DNS服务器以github.com的IP响应为192.30.253.113。蓝色选择显示相应的部分。现在,浏览器已经有了目标IP,该IP将用于连接服务器。

发起TLS握手

解析IP后,浏览器将通过http请求页面。如果服务器支持TLS,则它将通过说出协议升级请求来响应浏览器。新位置(例如https://github.com)将使用端口号443指定。然后浏览器将启动TLS握手请求。大多数现代浏览器都记住与Web服务器的最后连接。如果最后一次连接是通过https建立的,则下次浏览器将自动启动https请求,而无需等待服务器。
TLS握手分为以下几个步骤

  • Client Hello
  • Server Hello
  • Sharing the certificate and server key exchange(分享证书,服务器公钥交换)
  • Change cipher spec 更改密码规范
  • Encrypted handshake 加密握手

客户端发送hello消息

从这里开始,我将在图像中以蓝色突出显示讨论主题。下图显示了Client Hello。
一篇搞懂SSL/TLS协议(https的握手)_第15张图片
我们知道TLS是在TCP之上实现的协议。TLS本身就是一层,最底层称为记录协议。这意味着所有数据都被视为记录。通过导线,典型的记录格式如下所示:
HH V1:V2 L1:L2 数据
HH是一个单字节,指示记录中的数据类型。定义了四种类型:change_cipher_spec(20),警报(21),握手(22)和application_data(23)。
V1:V2是协议版本,超过两个字节。对于当前定义的所有版本,V1的值为0x03,而V2的SSLv3值为0x00,TLS 1.0的值为0x01,TLS 1.1的值为0x02,TLS 1.2的值为0x03。
L1:L2是数据的长度(以字节为单位)(使用big-endian约定:长度为256 * L1 + L2)。数据的总长度不能超过18432字节,但实际上它甚至不能达到该值。在该图中,可以看到内容类型为Handshake,建议TLS版本为1.0,数据长度为512。实际数据位于名为“ Handshake Protocol:Client Hello ” 的下拉列表中。让我们继续观察客户端Hello中正在共享哪些数据。
Client hello中的内容:浏览器与服务器共享以下详细信息。一篇搞懂SSL/TLS协议(https的握手)_第16张图片

  • 客户端版本(Version):客户端支持的协议版本的列表,按优先顺序排列。首选的是客户端希望支持的最大协议版本。

  • 客户端随机数(Random):一个32字节的数据,其中前4个字节以纪元格式表示当前日期时间。对于那些不知道纪元时间的人来说,它是自1970年1月1日起的秒数。其余的28个字节由加密强度高的随机数生成器(例如Linux中的/ dev / urandom)生成。客户随机数将在以后使用。现在,请记住这一点。

  • 会话ID(Session ID):如果客户端第一次连接到服务器,则该字段将保持为空。在上图中,您可以看到会话ID正在发送到服务器。发生这种情况是因为我以前已经通过https连接到github.com。在此期间,服务器将使用会话ID映射对称密钥,并将会话ID存储在客户端浏览器中。将为映射设置时间限制。如果将来浏览器连接到同一服务器(当然,在时间限制到期之前),它将发送会话ID。服务器将对映射的会话进行验证,并使用以前使用的对称密钥恢复该会话。在这种情况下,不需要完整的握手。

  • 密码套件(Cipher Suits):客户端还将发送已知的密码套件列表。客户端按优先顺序排列密码套件。但这完全取决于服务器要遵循的顺序。TLS中使用的密码套件有一种标准格式。
    一篇搞懂SSL/TLS协议(https的握手)_第17张图片
    让我们从列表中进行示例分析。
    密码套件:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256(0xc02b)

      - TLS:只是表示TLS协议
      - ECDHE:这是密钥交换算法。
      - ECDSA:签名或认证算法
      - ES_128_GCM:称为批量加密算法。对称密钥加密算法是AES,密钥长度为128位。
      - SHA256:消息认证码(MAC)算法。我们将详细讨论MAC。
    
  • 压缩方式(Compression methods):为了减少带宽,可以使用压缩。但是对TLS进行了成功的攻击,其中使用压缩时可以捕获与HTTP标头一起发送的参数。Cookies可能因这种攻击而被劫持,该漏洞被称为CRIME。从TLS 1.3开始,协议已禁用TLS压缩。

  • 拓展项(Extension):可以将其他参数(例如服务器名称,填充,支持的签名算法等)指定为扩展名。随意研究指定为扩展的内容。
    这些是客户端Hello的一部分。如果服务器已收到客户端问候,则服务器会发出确认。然后服务器将发送服务器Hello消息。

服务端发送hello消息

收到客户端Hello 服务器后,必须发送服务器Hello 消息。服务器将检查客户端Hello指定的条件,例如TLS版本和算法。如果服务器接受并支持所有条件,它将发送证书以及其他详细信息。否则,服务器将发送握手失败消息。
一篇搞懂SSL/TLS协议(https的握手)_第18张图片
在该图中,可以看到服务器响应0x0303,表示服务器同意使用TLS 1.2。让我们检查Server Hello中的记录。
服务器Hello的内容服务器问候消息包含以下信息。
一篇搞懂SSL/TLS协议(https的握手)_第19张图片
我们将在这里讨论重要的参数。

  • 服务器版本(Server Version):如果服务器支持,服务器将选择客户端指定的TLS版本。在这里选择TLS 1.2
  • 服务器随机数(Server Random):与客户端随机数类似,服务器随机数也将为32字节长。前4个字节代表服务器的Unix纪元时间,后跟28个字节的随机数。客户端和服务器随机变量将用于创建加密密钥,稍后我们将对其进行说明。
  • 加密套件(Cipher Suits):还记得我们已经在客户端Hello中将已发送支持的密码套件发送到github.com吗?Github从列表中挑选了第一个。即 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256。
  • 会话ID(Session ID):服务器将约定的会话参数存储在TLS缓存中,并生成与其对应的会话ID。它与服务器Hello一起发送到客户端。客户端可以使用此会话ID键入商定的参数。将为此定义一个有效期限。客户端会将这个ID包含在客户端Hello中。如果客户端在此到期之前再次连接到服务器,则服务器可以检查与会话ID对应的缓存参数,并在不需要完全握手的情况下重新使用它们。这非常有用,因为服务器和客户端都可以节省大量计算成本。
    当涉及到流量巨大的应用程序(例如Amazon和Google)时,这种方法有一个缺点。我每天将有数以百万计的人连接到服务器,服务器必须使用会话密钥保留其所有会话参数的TLS缓存。这是巨大的开销。为了解决此问题,引入了会议票证的概念。在这里,客户端可以在客户端问候中指定是否支持会话票证。然后,服务器将创建一个新会话票证,并使用仅服务器知道的私钥来加密会话参数。这将存储在客户端上,因此所有会话数据仅存储在客户端计算机上。由于密钥仅服务器知道,因此票证仍然是安全的。
    此数据可以作为名为SessionTicket的扩展名包含在客户端Hello中。在我们的例子中,该参数为空。因为这是第一次连接到github.com的浏览器或上一个会话已过期。
    在这里插入图片描述
  • 压缩方式(Compression methods):如果支持,服务器将同意客户端的首选压缩方法。在这里,您可以看到服务器响应为空响应意味着不需要压缩。

服务器证书消息

服务器不会在ServerHello消息中发送任何证书;它以适当命名的证书消息发送证书。
一篇搞懂SSL/TLS协议(https的握手)_第20张图片
在我们的情况下,证书消息的长度为3080字节。难怪它是带有所有信息的服务器证书。服务器按照信任链的顺序发送完整的证书列表。该链的第一个是服务器的证书,其次是颁发服务器证书的中级CA的证书。然后,下一个中间CA的证书继续进行,直到根CA的证书为止。服务器规定不发送根CA证书,因为在大多数情况下,浏览器可以从任何中间CA识别根CA。在我们的案例中,您可以看到第一个证书是github.com,第二个证书是中间的Digicert SHA2扩展验证服务器CA。在下图中检查id-at-commonName 参数。
一篇搞懂SSL/TLS协议(https的握手)_第21张图片
让我们分析证书的内容,并查看浏览器如何对其进行验证。
证书中的内容:
证书被发送到浏览器,因此,当我们访问github.com时,我们可以查看Github的证书。在Firefox中
一篇搞懂SSL/TLS协议(https的握手)_第22张图片
可以通过单击“ 详细信息” 选项卡查看github的中间CA和根CA。
一篇搞懂SSL/TLS协议(https的握手)_第23张图片让我们了解这些字段是什么以及它们的用途。

  • Version and Serial Number:版本和序列号,版本表示使用的X.509标准版本。X.509是用于定义公钥证书格式的标准。X.509有3个版本,而github使用的是最新版本3。从RFC 5280开始,序列号必须是CA为每个证书分配的正整数。对于给定CA颁发的每个证书,它必须是唯一的(即,颁发者的名称和序列号标识唯一的证书)。CA必须将serialNumber强制为非负整数。
  • Certificate Signature Algorithm and Value:证书签名算法和值,浏览器需要知道签名算法才能验证签名。如果使用RSA签名,则必须使用相同的算法来验证签名。对于Github,使用具有RSA加密功能的PKCS#1 SHA-256。这意味着SHA-256用于生成哈希,而RSA用于签名。
  • Issuer:发行人,该字段包含颁发证书的授权机构的详细信息。Github的证书由Digicert的中间CA颁发。
  • Validity:有效期,此字段具有两个值Not Not 和Not After 。这些值不言自明。如果当前日期时间不在这些值之间,则证书无效。浏览器将不信任该证书。
  • Subject Public Key Info:主题公钥信息,该字段包含公钥和用于生成公钥的算法。该密钥用于交换密钥,我们将在后面讨论。
  • Fingerprints:指纹,有两个指纹SHA 1和SHA-256,它们都是由浏览器生成的,并且永远不会发送到服务器。这些指纹是通过分别使用SHA 1和SHA-256函数对证书的DER格式进行哈希处理而生成的。我们可以通过将证书下载到我们的计算机并应用哈希功能来验证这一点。

我们在这里讨论的证书信息是关于github.com的服务器证书的。Github的中间CA证书也将在同一请求中发送给客户端,并且以上所有字段也适用于该证书。您可以通过转到详细信息选项卡并单击如下所示的中间CA来进行检查。
一篇搞懂SSL/TLS协议(https的握手)_第24张图片

服务器密钥交换

在服务器问候和证书消息之后,有可选的服务器密钥交换。仅在服务器提供的证书不足以允许客户端交换主密码之前,才发送此消息。让我们看看为什么github.com必须发送服务器密钥交换消息。
一篇搞懂SSL/TLS协议(https的握手)_第25张图片
我们已经看到github.com 在会话中首选密码套件TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256。这意味着各方使用椭圆曲线Diffie Hellman算法交换密钥。在Diffie-Hellman中,客户端无法自行计算Pre-master机密。双方都对计算进行了贡献,因此客户端需要从服务器获取Diffie-Hellman公钥。(不要对Master Pre-Secret 这个术语感到困惑,我们将在下面进行更深入的讨论。)使用椭圆曲线Diffie-Hellman时,公钥不在证书中。因此,服务器必须在单独的消息中向客户端发送其DH公钥,以便客户端可以计算主密码。可以在上图中看到。请注意,此密钥交换也由签名保护。

服务器密钥交换完成后,服务器将发送“ 服务器问候已完成” 消息。客户端将开始计算“预掌握密钥”。让我们看看如何。

如何计算准主密钥(Pre-Master Secret)

“准主密钥”的计算取决于达成共识的密钥交换算法的类型。当使用RSA进行密钥交换时,从客户端(即浏览器)计算出Pre-Master机密。客户端通过将协议版本(2个字节)和客户端随机生成的一些字节(46个字节)进行级联来生成48字节的premaster机密。客户端应该从加密安全的伪随机数生成器(PRNG)中获取这46个字节。实际上,这意味着使用操作系统提供的PRNG,例如/ dev / urandom 。然后,使用服务器的公共密码和共享密码对该主密码器进行加密,以便服务器以后可以使用它创建主密码器。

但是,在Github的情况下,如上所述,Diffie-Hellman算法用于密钥交换。这里的情况有点不同。服务器立即生成一对DH私钥。然后将公共密钥与客户端共享。如上所述,这是服务器密钥交换消息。作为响应,客户端还将创建一个DH密钥对,并通过“ 客户端密钥交换” 消息与服务器共享公用密钥,如下所示。
一篇搞懂SSL/TLS协议(https的握手)_第26张图片您可以看到正在共享客户端公钥。现在,如果您了解Diffie-Hellman算法的工作原理,那么您就会知道客户端和服务器可以从这些共享的公共密钥中访问公共密钥。新生成的密钥称为Pre-Master key 。

使用Diffie Hellman算法进行TLS密钥交换有一个优势。客户端和服务器都会为每个新会话生成一个新的密钥对。一旦计算了预掌握密钥,客户端和服务器的私钥将立即被删除。这意味着私钥以后再也不会被窃取,以确保完美的前向保密性。

客户端密钥交换

上面我们已经讨论过,客户端的DH公共密钥是通过客户端密钥交换消息共享给服务器的。但是,如果使用了RSA,则客户端将如上所述使用其自己的方式计算Pre-Master Secret,并使用服务器的公钥(RSA公钥)对其进行加密,然后通过客户端密钥交换消息将其发送回服务器。然后,服务器可以使用其私钥对其进行解密。无论采用哪种算法,客户端和服务器都已经达到了通用的“预掌握”机密。完成此操作后,客户端将发送“ 更改密码规范” 消息,如下所示。
一篇搞懂SSL/TLS协议(https的握手)_第27张图片让我们继续前进,看看如何从“Pre-Master Secret”中计算出“Master Secre”。

如何计算Master Secre

客户端和服务器现在拥有什么所有随机数据?主机和服务器之间在Hello消息期间共享的主机预密码和随机值(还记得吗?),双方将使用PRF(伪随机函数)使用这些值来计算主机密码。根据RFC 5346,

master_secret = PRF(pre_master_secret, "master secret", ClientHello.random + ServerHello.random) [0..47];
  • pre_master_secret –双方计算的48字节的Master机密。
  • “master secret" –它只是一个使用ASCII字节的字符串。
  • ClientHello.random –客户端问候中共享的随机值
  • ServerHello.random –服务器问候中共享的随机值。

主密钥的大小将始终为48个字节长。好吧,到目前为止没有更多的混乱。双方都可以使用Master机密对数据进行加密并来回发送。同意,但是程序尚未结束。您认为在两侧使用相同的密钥是一个好主意吗?当然不是!TLS为客户端和服务器使用单独的密钥,这两个密钥都是从主密钥本身派生的。换句话说,主密码不会直接用于加密数据。而是将单独的加密密钥用于客户端和服务器。由于双方都拥有两个密钥,因此客户端可以轻松解密服务器使用其密钥加密的数据,反之亦然。
还没有结束。TLS具有用于对称密钥加密的附加安全性机制。

消息验证码(MAC)和TLS数据完整性

窃听者可以对传输中的加密数据执行两种可能的攻击。尝试解密数据或尝试修改数据。只要密钥安全,我们就认为解密几乎是不可能的。但是数据修改呢?客户端和服务器如何知道攻击者未修改接收到的数据?如前所述,TLS不仅可以加密数据。TLS还保护数据免受未检测到的修改。换句话说,我们也可以说TLS检查数据完整性。让我们看看它是如何完成的。
当服务器或客户端使用主密钥对数据加密时,它还会计算纯数据的校验和(哈希)。该校验和称为消息验证码(MAC)。然后,在发送MAC之前,将其包含在加密记录中。密钥用于从记录中生成MAC,以确保传输中的攻击者无法从记录中生成相同的MAC。因此,MAC被称为HMAC(哈希消息认证码)。另一端,解密器收到消息后,会将MAC与纯文本分开,并用其密钥计算纯文本的校验和,并将其与接收到的MAC进行比较。如果找到匹配项,那么我们可以得出结论,数据在传输过程中没有被篡改。
客户端和服务器必须使用相同的哈希算法来创建和验证MAC。还记得Github同意的密码套件的最后一部分吗?
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_ SHA256。即,SHA256 是用于处理HMAC的哈希函数。为了提高安全性,客户端和服务器使用单独的MAC密钥。让我们看看所有这些。
MAC Keys and IV keys
根据规范,有4个密钥用于加密和验证每个消息的完整性。

  • Client write encryption key: 客户端写加密密钥:客户端用于加密数据,服务器用于解密数据。
  • Server write encryption key: 服务器写加密密钥:服务器用于加密数据,客户端用于解密数据。
  • Client write MAC key:客户端写入MAC密钥:客户端用来创建MAC,服务器用来验证MAC。
  • Server write MAC key: 服务器写入MAC密钥:服务器用于创建MAC,客户端用于验证MAC。
    这些密钥块再次由主密钥上的相同PRF反复生成,直到为密钥创建了足够的字节为止。
key_block = PRF(SecurityParameters.master_secret, "key expansion", SecurityParameters.server_random + SecurityParameters.client_random);

如您所见,连同客户端服务器随机值和字符串“密钥扩展”,主密钥也用于增加密钥的熵。PRF可以生成任意长度的密钥。这很有用,因为默认情况下,不同的哈希函数具有不同的长度。在我们的情况下,使用SHA256,即256位。但是使用MD5时,默认长度为128位。除此之外,我们知道我们正在将AES与GCM算法一起使用,这是一种分组密码,它需要一组位才能用作初始化向量(IV)。在讨论密码套件时,我们提到了IV用于改善AES加密的熵。换句话说,当同一文件被多次加密时,IV有助于生成不同的密文。这些随机字节也由相同的PRF生成,分别称为客户端写入IV 和服务器写入IV 。这些术语是不言自明的。我没有对IV的细节进行更多的研究,因为它是一个巨大的话题,超出了本文的范围。

生成测试数据

双方都有加密密钥,我们已准备好进行加密。但是,像对待每个进程一样,在将TLS应用于应用程序层之前,我们需要测试并验证客户端加密的数据可以由服务器解密,反之亦然。为此,客户端将使用伪随机函数(PRF)如下计算12字节的verify_data。

verify_data = PRF(master_secret, "client finished", MD5(handshake_messages) + SHA-1(handshake_messages) ) [12]

其中handshake_messages 是所有握手消息的缓冲区。TLS适用于1.2之前的版本。与1.2版相比有一些细微变化。也就是说,verify_data的长度取决于密码套件,并且并不总是12。任何未明确指定verify_data_length的密码套件都具有等于12 的verify_data_length 。此外,伪随机函数(PRF)中的MD5 / SHA-1组合具有已由密码套件指定的PRF代替。因此,根据最新规范,

Verify_data = PRF(master_secret, finished_label, Hash(handshake_messages)) [0..verify_data_length-1];

因此,我们拥有测试数据,密钥和用于加密测试数据的算法。客户端所需要做的就是使用客户端加密密钥(或简称为客户端写入密钥)使用AES加密测试数据。如上所述也产生了HMAC。客户端获取结果,并添加记录头字节“ 0x14”以指示“已完成”,并通过“ 客户端已完成” Client Finished message消息将其发送到服务器。这是受实体之间协商的算法和密钥保护的第一个消息,以及客户端发送的最后一个握手消息。由于该消息是完全加密的,因此WireShark将仅看到加密的内容,并以“ 加密的握手消息”名称调用完成握手,如下所示。
一篇搞懂SSL/TLS协议(https的握手)_第28张图片

验证谈判

服务器执行几乎相同的操作。它发出变更密码规范,然后发出包含所有握手消息的完成消息。在变更密码说明消息标记的点上的服务器切换到新协商的加密套件和密钥。来自客户端的后续记录将被加密。随之而来的是,服务器的Finished Message 将包含客户端的Finished Message 的解密版本。客户端收到此数据后,将使用服务器写入密钥对其进行解密。因此,这向客户端证明了服务器能够成功解密我们的消息。TLS握手已完成。

所有加密将基于协商算法。在我们的例子中,算法是AES_128_GCM。这里没有深入解释它的意义,因为当涉及到其他网站时,该服务器指定的算法将有所不同。如果您有兴趣了解每种算法的工作原理,那么维基百科列出了它们。我也在通过TLS基础知识来学习密码学。

你可能感兴趣的:(计算机网络,https,tls,ssl,加密解密)