HTTPS 概念
在个人过去的读书笔记中已经介绍过一次,在这一篇文章中介绍了HTTP1.1的缺点,以及SSL、TLS的历史,之后介绍了有关SSL加密的主要加密方案:公开密钥加密 和 共享密钥加密,最后简单介绍了HTTPS的交互过程,但是书中的过程比较粗,这节我们讲细一点点。
相关文章:[[《图解HTTP》 - HTTPS]]
本部分会简化介绍重复的部分,补充笔记中没有完善的细节。
这些内容比较八股又干又硬,建议仔细阅读消化。如何定义“安全” 定义安全靠的是下面几点:
- 完整性:也叫做数据一致性,指的是传输过程自出发到接受的信息是一致的。机密内容可以被黑客替换或者删除添加,一旦被接受并且核验通过,会带来大问题。
- 机密性:对于数据保密之后,只有信任的对象可以访问,其他人哪怕拿到保密信息,也无法识别出里面的内容。
- 身份认证:身份认证需要明确的证实对方身份,比如报警的时候要求警察出示身份证据,并且看到盖章许可文件才允许进入,不然黑客冒充的,不管怎么防都没有用。
- 不可否认:指的是发生的事情不能进行诋毁,某一项操作必要在通信完成之后产生一定影响。否则就是类似访问一个看起来极其逼真的网站但是却是一个空壳,空壳后面直接把你的个人信息套走。
HTTP的主要问题:
- 信息加密:保证敏感信息不会被窃取。
- 身份校验:数据完整性保证,数据无法篡改,篡改之后无法正常使用。
- 身份证书:证明服务端是真实具备公信力的。
- 信息加密:使用双层加密机制,连接使用非对称加密,同时在连接之后使用对称加密,双保险的混合加密保证安全。
- 摘要算法:保证数据的完整性,TLS协议主要推荐使用SHA-2“集合”下面的加密算法,相当于给数据生成一个解锁指纹。
- 身份证书:服务器公钥放入数字证书,自己再使用私钥再加密一遍进行传输,防止被人冒充。
- 不可否认:CA证书颁发机构本身具备权威及公信力,一旦服务端被声明存在,那就是一定存在。
- HTTP是明文传输,在传输一些敏感信息的时候可能存在窃取信息的情况。
- HTTP连接相对效率更高,因为它只需要三次握手就是完成连接操作,而TLS/SSL需要多加入四次握手才能完成连接。HTTPS 整体需要耗费更多的连接时间。
- HTTP的端口默认是80,HTTP默认端口时443。
- HTTP发送公钥需要向CA进行申请,保证服务器是受信任的。
可以直接阅读笔记的“SSL/TLS历史”部分,个人在笔记中对于SSL/TLS之间的关系,以及SSL/TLS进化历史做了一个概述,当然这些内容也可以直接去“维基百科”看相关介绍,里面有更加专业和权威的解释。
TLS含义 TLS 由记录协议、握手协议、警告协议、变更密码规范协议、扩展协议等几个子协议组成,综合使用了对称加密、非对称加密、身份认证等许多密码学前沿技术。
浏览器建立SSL协议连接,实际上就是使用多个子加密协议组合,最终选择合适的加密算法进行数据安全传输,这种算法组合本身被叫做“密码套件”。
此外TLS 的密码套件命名看起来很长,但是实际上非常规范,格式很固定。基本的形式是“密钥交换算法 + 签名算法 + 对称加密算法 + 摘要算法”。
比如:
ECDHE-RSA-AES256-GCM-SHA384
所表示的含义为:
“握手时使用 ECDHE
算法进行密钥交换,用 RSA 签名和身份认证,握手后的通信使用 AES 对称算法,密钥长度 256 位,分组模式是 GCM,摘要算法 SHA384 用于消息认证和产生随机数。
数字加密历史
数字加密的是在计算机出现以及专业的密码机出现之后诞生,但是实际上密码学有了上千年历史,下面我们依照《HTTP权威指南》中14.2节的介绍大致回顾数字加密历史。本部分为了解数字加密的技术和术语,如果已经了解了或者不感兴趣可以直接跳过。数字加密的发展大致经历了下面的过程:
文章图片
密码 密码通常是一套固定编解码规则的加密信息,被编码之后文本信息被叫做密文。比如我们用AAA代替阿拉伯数字1,BBB代替阿拉伯数字2,C....这样的规则组织出一套针对阿拉伯数字的加密,在加密的时候用密文进行替换。
- 加密前:9112955
- 加密后:IIIAAAAAABBBIIIEEEEEE
密钥加密 但是随着技术发展,人们发现使用密码机加密有一个漏洞,一旦密码机被盗或者被仿制,那么加密规则就失效了,经典案例来自二战时期的德国密码机被图灵攻破。
所以在二战之后又发明了一种可以自由改动加密规则的带有号码盘的密码机。简单来说就是给密码机本身又上了另一套加密规则。
被加锁之后的密码机,需要特殊的钥匙解锁才能正确的翻译密文,否则会被翻译乱码或者毫无规律的解密信息,密码密钥会让多个密码机看起来像是虚拟的密码机一样,每个密码机又不同的数值,他们需要配合在一起才能正常解密加密信息。
这就有点像是把一个解密的钥匙掰成很多份,每份钥匙上又有很多杂乱无章的齿,所以难以破解。
密码和密钥很容易被混为一谈,其实他们是类似“包装”的关系,密钥是对于加解密规则进行“乱序”,保护密码和加解密规则本身的手段,目的是即使被拿到加解密方法也没法正确的还原解密之后的信息。数字密码 数字计算围绕两个主题:
- 计算机的飞速发展,复杂的编解码规则实现。
- 超大密钥难以破解,可以从一个加密算法产生数万亿的虚拟加密算法,组合出不同的密钥。密钥越长,随机猜测的难度越大。
给定一段明文报文 P、一个编码函数 E 和一个数字编码密钥 e,就可以生成一段经过编码的密文 C。对称加密 密钥和密码的配合,最初形成是类似原有密码的对称加密手段。对称加密说的是所用的加密密钥和解密密钥都是同一个,比较流程的算法包裹:DES、Ttriple-DES(3DES)、RC2和RC4、ChaCh20、AES。
通过解码函数 D 和解码密钥 d,可以将密文 C 解码为原始的明文 P
但是需要注意RC4(RC2)、DES、3DES,这几种对称加密方法都是不安全的,通常禁止使用,所以还剩下AES以及ChaCh20。
- AES: 的意思是“高级加密标准”(Advanced Encryption Standard),密钥长度可以是 128、192 或 256或者更长。
- ChaCha20: 是 Google 设计的另一种加密算法,密钥长度固定为 256 位,纯软件运行性能要超过 AES,在ARMv8引擎升级之后,AES也飞速提升被反超。所以现在来看虽然优势不大,但是依然不错的一个算法。
密钥长度的重要性关于密钥长度以及破解需要的代价对比,哪怕是1995年的数据,放到现在依然有一定的价值,因为目前SHA-256依然没法在短时间破解。
目前大部分情况下除非完全自己写加解密算法,否则大部分的加解密的密码算法都是“透明”的,所以现在安全的反倒是密钥。通常的加密算法破解手段是利用穷举组合密钥的方式去“试”,这也意味着越长的密钥越安全,但是所需要的计算机资源相对也会更多。在密钥出现的早期,很短的密钥就可以满足加密算法的保护,但是到了现在随着超级计算机出现以及CPU效率的提升,密钥的长度已经来到了SHA-256。
文章图片
对称加密的最严重问题除了是密钥本身容易,还有一点是密钥共享和本身的管理问题,因为共享密钥需要传给ABCDEFG,一旦出现更改又要重新传一遍,这导致有的人用的旧密钥,有的使用新密钥,这样做法管理起来非常麻烦。
公开密钥加密 共享密钥本身的管理问题以及安全问题,后续出现了使用非对称的加密方案,所谓非对称加密就是说加密的密钥是公开对外开放使用的(不用向共享加密一样藏着掖着),所有人都可以拿到这份公开密钥加密内容,但是加密过后的信息,除了发布者知道之外都是保密的。
公开密钥加密在共享加密的基础上做了升级,改进了密钥数量膨胀问题。公开密钥架构(Public-Key Infrastructure,PKI)标准创建工作已经开展十多年了。因为非对称的加密算法要难非常多,所以在TLS里面可以看到的有下面几种:
- DH
- DSA
- RSA
- ECC
ECC(Elliptic Curve Cryptography)是继广泛流传的RSA之后的一种“椭圆曲线离散算法”的数学难题,使用特定的曲线方程和基点生成公钥和私钥,子算法 ECDHE 用于密钥交换,ECDSA 用于数字签名。
ECC目前比较常用的两个曲线是 P-256(secp256r1,在 OpenSSL 称为 prime256v1)和 x25519。P-256 是被 NIST(美国国家标准技术研究所)和 NSA(美国国家安全局)推荐使用的曲线,二X25519则是被认为是安全的、快速的曲线。
ECC 的椭圆曲线实际上并不是椭圆的,而是方程类似椭圆的周长公式(),实际的形状更像是两个抛物线。ECC与RSA对比 首先,针对RSA的算法公认的破译难度是亚指数级别的,而ECC的破译难度是指数级别的。其次ECC所需的加密密钥长度要比RSA缩短好几倍,比如1024的RSA相当于160位的ECC,224 位的 ECC 则相当于 2048 位的 RSA。因为密钥的长度合适,所以十分受欢迎。
ECC 拓展:https://en.wikipedia.org/wiki/Elliptic-curve_cryptography
需要注意,虽然ECC很快了,和对称加密的几个算法比起来,还是差了好几个量级,比如下面的代码:
aes_128_cbc enc/dec 1000 times : 0.97ms, 13.11MB/srsa_1024 enc/dec 1000 times : 138.59ms, 93.80KB/srsa_1024/aes ratio = 143.17rsa_2048 enc/dec 1000 times : 840.35ms, 15.47KB/srsa_2048/aes ratio = 868.13
通过对比可以看到,AES加密是非常快的,只需要纳秒级别的时间,而2048位的RSA需要它的几百倍。
所以这也是为什么TLS需要使用对称加密和非对称加密混合使用的根本原因,如果全部按照非对称加密传输,那么页面的传输响应时间可能要凭空多出几秒,这对用户来说是无法接受的。
数字签名 数字签名是除开对于报文加解密的另一种思路,签名的目前是保证数据一致性和防篡改。同时签名可以数据的所属来源是否真实可靠。
产生数字签名的方式实际上还是 公开密钥加密,但是把思路反过来了,数据是通过私钥进行加密的,只有公钥可以解锁。
数字签名通常会在传输之前双方各持有一份,传输的内容在中间被破解并且被篡改,在收到后可以先拿公钥解密验证一下,然后再取出数字签名匹配一下,如果其中任意一方匹配不上,那就可以说明传输的数据被人篡改了,又因为私钥是保密的,黑客没法伪造签名,只能干巴巴看着到嘴的肉飞走~~
文章图片
数字签名通常是使用单向加密的摘要算法,摘要算法主要负责计算内容的哈希值(HMAC),这个哈希码是唯一的,并且无法反向推导。
我们可以把摘要看作是一个指纹,在哈希算法中可以保证指纹不会被篡改,但是这里显然有一个漏洞:虽然不能改签名,但是可以换签名和内容。
所以单纯的摘要算法只能保证数据完整性,但是不能保证数据安全,为此就需要加入非对称加密算法,加强双向数据的传输。
非对称加密通常需要两个钥匙,一把私钥由自己保管,公钥可以对外开放使用。密钥可以双向破解,也就是可以私钥加密,公钥解密,也可以公钥加密,私钥解密。
但是处理方式不同,那么其实现的效果也不一样:
- 公钥加密,私钥解密(机密性):公钥加密的数据只能被私钥破解,这样保证数据的安全。
- 私钥加密,公钥解密(完整性):私钥加密的数据只能被公钥解析,私钥加密的数据一旦被篡改或者替换,公钥解密之后可以证明数据的是否符合要求。
这样也就实现了双保险,但是这样的双保险还是存在风险,我们可以看看下面这个攻击过程:
- 客户端向服务端索取公钥进行通信,此时黑客从中间插手,伪造一对公钥和私钥,然后继续向服务器获取公钥。
- 服务器误以为是客户端发来的,所以果断加密信息然后返回公钥。此时黑客拦截,黑客压根不用管怎么解密这些内容,此时它用自己的【私钥+数字签名】伪造一份数字签名给,然后发给客户端。
- 客户端拿到被伪造的公钥,使用公钥加密后续通信使用的对称密钥,传给服务端。
- 中间人截取请求,用自己的私钥解密信息, 拿到对称密钥,然后仿照客户端的请求使用真实公钥加密自己伪造的对称密钥。
- 服务端使用私钥解开之后,得知对称加密算法,然后开始和黑客进行正常通信。
- 黑客鸠占鹊巢,成功破解,这时候黑客既可以利用伪造的身份从服务器获取信息,也能伪装成服务器向客户端发送一些虚假信息,这样实在是危险。
文章图片
数字签名只解决了关于安全的两个方面问题,所以实质无法保证安全通信。所以很无奈,既然怕你们乱来,那只好设置一个授权机构,以后客户端可以找我核实对方身份,而服务端需要我这里的文书才算是真的认可。
数字证书 数字证书是无止尽加解密的最后一道关口,数字证书通常包含了某些组织或者某些公司的相关信息。数字证书也可以看作是数字签名的进步版本,也是解决无穷无尽的加密套娃的最终解决方案。
数字证书的概念在生活中比较常见,可以想象为美国电影里面搜捕公民的房间需要向上级申请搜捕令才能进入。数字证书是目的防止HTTPS加密,密钥被篡改的“无限套娃”加密的问题,因为数字签名和密钥信息需要传输,中间必须要有一个足够可靠的第三方伙伴证明。
这个证明被称之为CA,客户端收到包含CA数字证书签名的服务端的请求之后(私钥加密),通过用浏览器内置的CA证书公钥解密,只要证书通过校验,就可以认为服务端是稳定的。
通过上面的解释我们知道,客户端和服务端的在SSL握手中的非对称加密方法是是公钥加密,私钥解密,数字签名的摘要作为防篡改校验。
而数字证书的用法则是把这个过程反过来,变为私钥加密、公钥解密。并且利用需要数字证书给数字签名再加个章,这样的效果相当于是一个三方校验。
所以你数字证书的授权机构怎么保证自己靠谱呢?我们从下面几个点来进行说明:
- CA如何证明自己?
- CA证书的格式以及标准。
- 证书的弱点。
- 传输过程中CA证书被篡改或者被替换,如何进行加密传输认证。
但是CA又要怎么证明自己呢?
CA证书通过信任链证明自己,首先根证书开始,向下逐渐找到信任程度更弱的中间CA机构,通过层信任的方式找到最终的CA证书颁发机构进行验证。
无论如何认证,CA 认证最终总会走 ROOT 根证书,ROOT也可以叫做自签名证书,这个是需要强制相信的, 否则自证明的体系是走不下去的。这种自证体系有点像是如何保证最高法院的权威性一样,CA本身包含机器严格的保护措施。
下面这个图也可以说明,实际上根证书是最重要的,客户端和服务端绝对不能泄露根证书,否则CA的体系和HTTPS都会不攻自破。
文章图片
浏览器的Security的CA证书中,也可以看到类似结构:最上层Root是根证书,GlobaSync 属于中间证书,最底层是具体服务器的证书。
这里接着补充这个层层信任是咋个信任法?其实也挺简单的,那就是上级持有下一级认证的公钥,先验根证书,然后从根证书公钥往下找中间证书层层认证。
我们以B站的校验过程为例:
- 首先待验证的网站是 bilibili.com,首先找到上级证书GlobaSIgn RSA SSL CA 2018,发现它的上级证书 GlobaSign Root CA - R1,他发现这个证书是没有上级的,所以可以认为是根证书。
- 既然GlobaSign Root CA - R1是根证书,那么一般情况下浏览器或者操作系统会内置证书,如果发现内置证书,比如下面就会会取出GlobaSign Root CA - R1的公钥,验证GlobaSIgn RSA SSL CA 2018是否可信,如果验证通过,说明GlobaSIgn RSA SSL CA 2018这个中间CA是可以信任的。
- 既然GlobaSIgn RSA SSL CA 2018是可以信任的,那么也同样取出公钥认证bilibili.com是否合规。
可以看到B站的认证体系有不少年头了。整个CA认证体系最终形成一条信任链条,可以证明网站是合法的了。
文章图片
从上面的图可以看出,为了验证CA的合法性,会从上级一路向下去验证合法性。另外,如果我们自己捏造自证证书注册到操作系统内(Linux利用OpenSSL命令),用浏览器打开会发现这个证书是不会收到Google浏览器信任的(因为浏览器不承认),并且会提示“此网站不受信任”。
证书具体认证过程 证书认证包含两种情况,一种是客户端认证CA证书的流程,另一种是CA证书自证流程。
客户端认证CA证书的流程这里直接放一张小林做的图,非常直观的展示了CA如何配合信息加密完成整个HTTPS加密传输。个人为了方便记忆,抽象的步骤如下:
- 服务器注册公钥到CA。
- CA私钥加密对于服务器公钥数字签名颁发数字证书。
- 客户端收到数字证书,使用CA公钥(浏览器内置)解密。然后使用数字签名验证是否存在篡改。
- 通过数字证书取到服务器公钥,使用服务器公钥进行非对称加密。
文章图片
然后是服务器自证过程,前面提到CA 证书是多层嵌套的自认证体现,为了保证CA证书的安全,根证书是CA认证的起点也是最核心的一环,以B站的证书为例,整个认证的体系如下:
操作系统或者浏览器认证顺序:
内置证书 -> GlobalSign Root CA -> GlobalSign RSA OV SSL CA 2018 -> *.blibli.com
文章图片
为什么非对称加密之后,还需要使用摘要算法计算出一个哈希值?CA证书的格式以及标准 CA证书当然并不是直接给服务器私钥上套个签名就完事了,CA的证书格式有着极其严格的规范和要求,下面是CA证书主要包含的信息:
其实非对称加密足以保证安全了,加上CA证书基本可以万无一失,而使用摘要算法计算哈希值,实际上是考虑导效率问题,因为内容长度的匹配比较耗费性能,而哈希可以是固定长度的唯一值,哈希值匹配的效率要高出很多。
此外非对称加密可以加密的消息体长度有上限(public key length in bits / 8 - 11),否则会报错,所以必须先hash保证定长结果。
? 对象的名称(人、服务器、组织等);
? 过期时间;
? 证书发布者(由谁为证书担保);
? 来自证书发布者的数字签名。
虽然CA证书在出世的时候没有严格的全球规范标准,但是在后续的发展,目前TLS1.2被公认的证书格式是X509,
X.509 v3证书 X.509证书字段包含下面的内容:
文章图片
文章图片
我们可以通过具备HTTPS安全连接的网站,通过F12的方式查看证书,比如B站的证书信息如下:
文章图片
证书的弱点 世界上没有绝对安全的事情,CA证书也不例外。CA证书的弱点在于自身的可靠性,比如给不受信任网站颁发受信任的证书,或者给CA证书本身被伪造,那么所有的防护都是一层纸, 所有信任链也就不存在了。
这些事情都是过去真实发生过的,感兴趣可以上网查找相关资料。比如很多年前MCS集团用CNNIC签发的中级证书,发行了多个冒充成Google的假证书。于是在2015年4月份,chrome、firefox都宣布不再信任CNNIC的证书,这件事情的影响十分恶劣,后续证书已经被CNNIC整顿并且被替换为合法的网站https://xfcnnic.net.cn/。
那么CA机构是如何解决这签错网站和本身受到污染这两个问题的?
- “签错”网站:利用CRL(证书吊销列表,Certificate revocation list)和 OCSP(在线证书状态协议,Online Certificate Status Protocol),及时废止有问题的证书。
- CA本身被污染:所有CA全部停止工作,浏览器把被攻克CA拉入黑名单。CA本身被入侵的可能性很低。
篡改证书
假设证书在传输过程被黑客篡改,黑客是没有CA的公钥的,,因为CA公钥一般再客户端的浏览器内置,自然无法破解签名,如果把数字签名改掉,浏览器收到请求通过验签手段对比解密之后的签名,发现原文不一致,也就自然发现了问题。
证书替换
证书替换还要分为两种情况,一种是自己伪造的证书,另一种是真的骗过CA搞了一份真的证书。
伪造证书这个事情,黑客能干,其实用户自己也能随时干。但是毫无疑问在浏览器以及操作系统自证的证书的是不受到认可,所以通常情况下客户端收到这些伪造证书都会立马判断出证书被人篡改过的事实。
那如果黑客煞费苦心,真的用合法的另一个网站拿到一份CA认可的证书,直接把证书替换了怎么办?
这时候客户端收到的证书确实是真的证书,也确实会拿到错误的公钥,但是不要忘了数字签名还有被颁发者的其他信息,比如域名是造不了假的,比对一下就出现问题了。
这里可能又会问,CA自己“黑吃黑”,本身被污染了怎么办?不能怎么办,这时候浏览器会把CA拉黑,CA所有的服务也会停止。在国内这么干是找死,而在国外.....你得有本拉登的逃命实力。
CA证书被黑客入侵本身就是互联网地震,这种时候坐着吃瓜就好了、证书内容
在RFC的TLS1.2的原文当中,有这么一句话:
The certificate type MUST be X.509v3, unless explicitly negotiated这个X.509 v3 证书是啥?X509V3 是用于TLS1.2 加密的公认标准,它提供了一种标准的方式, 将证书信息规范至一些可解析字段中。虽然有的证书机构会有个别字段不一样,但是整体上还是使用X509V3格式。
这里引用一段维基百科的介绍:
X.509 - 维基百科,自由的百科全书 (wikipedia.org)HTTPS的主要改进 关于HTTPS的改进我们再次简单回顾之前的内容:
X.509是密码学里公钥证书的格式标准。X.509证书已应用在包括TLS/SSL在内的众多网络协议里,同时它也用在很多非在线应用场景里,比如电子签名服务。X.509证书里含有公钥、身份信息(比如网络主机名,组织的名称或个体名称等)和签名信息(可以是证书签发机构CA的签名,也可以是自签名)
- 编解码的过程时在 SSL层完成的,HTTP层不需要做出过多的改变,就可以完美兼容HTTPS。
- 传输由原来的直接通过HTTP传输给TCP,到建立安全连接之后,从HTTP先传输到SSL层进行编码,然后传输到TCP层。
- 使用双重加密:对称加密算法、非对称加密算法以及通过数字证书保证,防止中间人攻击。
- 摘要算法:使用SHA-256等长密钥的加密方式,对于数据的完整性保护,一旦被篡改,数据无法通过校验。
文章图片
下面时针对HTTP和HTTPS的传输过程对比图:
文章图片
下面我们深入导HTTPS 1.2 的细节,了解TLS1.2的建立过程。
RSA和ECDHE算法区别 现代互联网都是使用ECDHE进行密钥交换算法,这里补充和RSA交换算法的两个关键差别点。
第一个,因为用到了密钥交换算法,所以传输过程中ECDHE会多出
Server Key Exchange
这个步骤。第二个,因为使用了 ECDHE,客户端可以不用等到服务器发回“Finished”确认握手完毕,立即就发出 HTTP 报文,省去了一个消息往返的时间浪费。这个叫“TLS False Start”,意思就是“抢跑”,和“TCP Fast Open”有点像,都是不等连接完全建立就提前发应用数据,提高传输的效率。
TCP Fast Open 是加速打开两个端点之间连续 TCP 连接的扩展。它通过使用 TFO cookie(一个 TCP 选项)工作,TFO cookies是存储在客户端上的,并在与服务器初始连接时设置的加密 cookie。换句话说,TFO 是 TCP 中的一种可选机制,它可以让过去创建完整 TCP 连接的端点取消握手步骤的往返,并立即发送数据。主要提高了接续通信的速度,主要是针对首字节时间高延迟网络特别有益。HTTPS 1.2 建立连接
HTTPS的核心是TLS协议,下面就结合TLS协议了解一下协议的组成了。
TLS1.2 协议组成 我们从 TLS1.2 协议组成说起:他的协议号是 RFC5246。网址为:https://www.ietf.org/rfc/rfc5246.txt
Four protocols that use the record protocol are described in this document: the handshake protocol, the alert protocol, the change cipher spec protocol, and the application data protocol.
从官方介绍来看,TLS1.2 主要由四个协议组成,也就是说一共有四份子协议:
- 记录协议(Record Protocol)
- 警报协议(Alert Protocol)
- 握手协议(Handshake Protocol)
- 变更密码规范协议(Change Cipher Spec Protocol)
记录协议规定了 TLS传输的基本单位是记录(record),record 可能包含长度,描述和内容,有点类似TCP的Segament的概念,通用是可以进行分块传输的。同样record具备分块、压缩、编码、解压缩、重组等等和TCP分块类似功能。
和TCP不同的是,TLS 可以把多个记录一次性放到TCP里面进行传输,并且不需要返回ACK。
此外,这个协议还规定了安全通信的能力:
- 可靠连接。使用 MAC(Message Authentication Code,消息验证码,TLS 目前使用的 HMAC 也属于 MAC 的一种)为数据提供完整性校验。同样,在握手阶段也可以不使用该功能。
- 私有连接:比如对称加密使用的AES和CHACHA20(协议中列举的几个后续被证明不安全),对称加密的密钥是不固定的,但是需要注意Record 协议也可以提供不加密的封装,比如在握手阶段的 Hello 报文。
意味着整个连接过程是“可定制化”的,很多参数都是可选的,这些内容将会在下文介绍。警报协议(Alert Protocol)
警报协议通常在传输出现问题的时候起到警示作用,警报协议用于终止一些存在危险隐患的请求,有点类似HTTP状态码,我们可以举几个例子方便理解:
- bad_record_mac:错误的MAC地址
- decode_error:解码异常
- protocol_version:表示旧版本不受支持
用于协商会话的安全属性,通常被封装在一个或者多个 TLSPlaintext 的结构中,简单来说主要是指定当前会话状态。
struct {
ContentType type;
ProtocolVersion version;
uint16 length;
opaque fragment[TLSPlaintext.length];
} TLSPlaintext;
握手协议是整个TLS里面最复杂也是最为核心的部分,当然也是真正天天都在用的协议,在这里定义了TLS握手协议,随机数以及密码套件等等信息,规定了整个TLS握手的细节。
握手协议的主要内容可以分为下面三点:
- 身份认证:指的是利用CA端进行身份认证,身份认证的过程会用到非对称加密算法,比如RSA算法,注意HTTPS支持客户端和服务端双向认证,默认是服务单单向认证,客户端认证是可选的。
- 安全参数协商:安全参数认证指的是保证被认证数据的机密性,需要用到比如哈希算法、密钥、加密算法等算法,对于数据加密处理。
- 可靠协商:可靠协商指的是防止数据传输过程中被篡改。
ClientHello
、SeverHello
、Certificate
、ServerKeyExchange
、CertificateRequest
、ServerHelloDone
、ClientKeyExchange
、CertificateVerify
、ChangeCipherSpec
、Finished
。变更密码规范协议(Change Cipher Spec Protocol)
主要用在握手阶段的协议,通知对方在出现这个标识之后,所有的内容都将会使用密文进行传输。接下来来看看整个握手的流程,RFC用极简风格演示了整个TLS握手协议,丑是丑了点,但意外的特别好懂。
文章图片
Application Data 协议
除开上面四个主要协议之外,还有
Application Data
协议。Application Data 协议用在通信阶段,封装了应用层的数据,通过 Record 协议封装之后,再通过 TCP 协议转发出去。
此协议用于握手连接完成之后的数据传输规范,实际上可以看作是SSL和TCP的协议的对接。
TLS1.2 协议握手流程 HTTPS通信步骤 这里有必要强调是TLS1.2协议,因为TLS1.3协议对于很多细节和内容进行了简化,这些内容将在后续内容继续介绍。
这里我们填一下在[[《图解HTTP》 - HTTPS]]的读书笔记中埋下的坑,在这本书中仅仅对于HTTPS传输做了大致的介绍,并没有深入到各个步骤的细节内容,下面就根据每一步来解释更加详细的传输步骤。
首先我们应该明白,HTTPS需要四次握手,一共需要在客户端和服务端之间来回两次,才能确定一个HTTPS的连接是安全的。
这部分内容建议结合WireShark 介绍TLS1.2加密的实验,目前网络上大部分的资料介绍的都是TLS1.2的加密过程。
之前从数字加密的历史以及HTTP到HTTPS的历史以及SSL/TLS的历史梳理了大概。现在我们从RFC协议的规范层面,来看看HTTPS连接是如何定义的。
首先给出RFC中的流程定义,官方给了两个版本,第一个版本包含完整的请求,而第二个版本则是交互步骤中必须经过的过程。
文章图片
master_secret = PRF(pre_master_secret, "master secret",
ClientHello.random + ServerHello.random)
[0..47];
简化之后的版本如下:
- 表示可选发送的可选消息或取决于情况而必须 消息。
文章图片
接着我们再画一个整个HTTPS的交互流程图,这是本次内容的重点。
文章图片
补充:截图完发现 4-START 第四次握手这里是开始,不是结束 =-=。整个流程还是有点点复杂的,但是拆分成四次握手,记住一些核心步骤并不难理解。
TCP三次握手 TCP三次握手的流程图这里还是用的之前已经画过的:
文章图片
- 第一步:客户端主动打开TCB端口,服务器被动打开TCB端口。客户端作为发起方携带一个SYN标志,并且携带一个ISN序号Seq=x,但是需要注意的是第一步的过程这个ISN序号是隐藏传递的(因为没有传递数据),因为如果请求不存在数据的交换则不会被显示。客户端发送SYN命令之后进入设置SYN=1,并且设置自己的状态为SYN-SENT(同步-已发送状态)。
- 第二步:服务器收到客户端TCP报文之后,也将SYN=1,并且回送一个新的ISN序号ack=x+1,并且将ACK=1表示自己收到了,然后在返回参数回送自己新的序列号表示自己的确认请求Seq=y,将状态设置为SYN-RCVD(同步收到)状态,(表示希望收到的序号为xxxx1522),最后也是指定MSS。
- 第三步:客户端收到服务器的确认报文之后,还需要向服务端返回确认报文,确认报文的ACK=1,并且回传服务器传递的ISN序号+1(ack = y+1),以及自己的ISN序号+1(Seq = x+1),此时TCP连接进入已连接状态。注意ACK是可以携带数据的,但是如果不携带数据则不消耗序列号。
- 最后一步:当服务器收到客户端的确认,也进入已连接状态。
HTTPS 第一次握手
文章图片
首先,客户端会发送一个 ClientHello 的请求,同时传递SSL握手需要的一些必要参数,主要的字段参数如下:
- client_version:客户端支持的TLS版本号
- client_random:客户端生成的随机数,是后续对称加密密钥的必要参数之一。
- session_id:如果客户端想要重用HTTPS会话,则在连接的时候需要携带此参数,否则为空。
- cipher_suites:列举客户端支持的加密套件,也是让服务端选择加密方式的参考。如果session_id 字段不为空(暗示恢复会话请求),则这个字段必须携带。
- compression_methods:客户端支持的压缩会话列表,如果session_id 字段不为空(暗示恢复会话请求),则这个字段必须携带。
- extensions:扩展字段,为了方便以后扩展新字段,或者协议对接方需要自行扩展协议使用。也就是所谓的留后门。extensions字段TLS1.3重要兼容字段。
扩展字段需要着重记忆,因为TLS1.3的核心部分。第一次握手以客户端质询服务端是否支持HTTPS为开端,给出一些自己可以提供的参数作为参考,大致的意思是“你好,我想要进行HTTPS连接,请问您这边可以支持么”。
ClientHello 属于子协议handshake的一部分。另外附带RFC协议的结构体定义,比文字描述来的直白很多:
struct {
ProtocolVersion client_version;
Random random;
SessionID session_id;
CipherSuite cipher_suites<2..2^16-2>;
CompressionMethod compression_methods<1..2^8-1>;
select (extensions_present) {
case false:
struct {};
case true:
Extension extensions<0..2^16-1>;
};
} ClientHello;
服务器收到ClientHello请求,如果它可以支持则会回送ServerHello 响应内容,同样需要回送相关字段,表示自己认识这些内容:
server_version
:为了向后兼容,服务端会根据客户端的 client_version 选择合适的以协议SSL通信。server_random
:服务端独立于client_random
独自生成。也是加密密钥的关键参数。cipher_suite
:默认从cipher_suites
选择合适的加密套件。compression_method
:从客户端传递的压缩算法列表,同样从中选择合适算法。extensions
:扩展内容
ServerHello 也属于子协议handshake的一部分。同样是结构体定义,比较直观:
struct {
ProtocolVersion server_version;
Random random;
SessionID session_id;
CipherSuite cipher_suite;
CompressionMethod compression_method;
select (extensions_present) {
case false:
struct {};
case true:
Extension extensions<0..2^16-1>;
};
} ServerHello;
服务器收到请求之后会进行相关答复,他对客户端说“你好,我支持XX协议,我看到了你给我的信息,经过考虑我将使用XX加密算法,XX压缩算法.....”
第一次握手是试探性的问候一些双方是否可以支持HTTPS,是一种高效验证手段。
HTTPS 第二次握手
文章图片
第二次握手是服务端开始的,ServerHello发送完成之后接着他需要传递 Certificate(非对称加密公钥) +数字证书给客户端进行CA认证。
CA 认证
在CA认证的步骤中,首先是服务端如何组织证书,这里又回到
X509 V3
的证书格式,可以大致总结出数字证书的关键字段:- Version:X509证书版本,目前大部分都是 3这个数字;
- Serial Number:每个CA生成的数字证书都需要有一个唯一序列号。这个序列号的意义在于一旦某个数字证书被破解,也无法破解同类型的数字证书,另一方面是方便标识证书;
- Issuer:表示证书的颁发者。遵循X509格式;
- Validity:证书的有效开始时间和截止时间。过期需要续费;
- Subject:证书的描述的实体,比如B站是上海幻电;
- Subject Public Key Info:CA证书的公钥加密算法;
文章图片
有了证书之后,接着是是对上面的数字证书内容做摘要算法生成一个数字签名,然后CA再用自己的私钥给数字证书的摘要套一层私钥加密,最后再交个客户端。
下面是整个数字证书和签名认证的图例:
文章图片
Certificate 在图中所属的位置是数字签名,实际上Certificate 还需要加到数字证书上进行传输才算是完整的。
注意:随着 密钥交换方式的加强,Certificate 在算法中还会再套一层“迷彩服”,这些内容会随着密钥交换方式的变化而出现很大的差别。数字证书传递之后,接下来是关键的一步,也就是密钥交换算法的协商,密钥交换的算法实际上是对于会话密钥(对称加密方法)选择,也就是如何安全的把实际交互用的对称密钥告知服务器。
这里再补充一点,因为客户端通常缺乏客户端证书认证,所以客户端如何把后续要对称加密传输的密钥告知服务器是一件麻烦事情,因为黑客一旦窃取到这个信息前面的交互都前功尽弃了。非对称加密对于加密密钥的保护是一种方案,比如传统的RSA,但是RSA存在很大漏洞,这一点在后续的内容进行解释。
密钥交换算法是指如何安全的进行密钥传输,之前介绍HTTPS利用非对称加密进行数据传输,非对称加密本身也不是百分百安全的,因为 私钥是有可能被破除的,为了更加安全的传输服务器的公钥,密钥交换的手段实际上也在不断改进。加密套件
介绍下一步之前,这里要补充一下【加密套件】的内容,加密套件涉及了下面几种内容的加密:
- 摘要算法
- 会话密钥加密算法
- 密钥交换算法(服务端非对称密钥加密)
- 签名算法(CA的非对称加密)
文章图片
以上面的内容为例,加密套件使用的是
ECDHE_RSA With P-256 and AES_128GCM
,指的是:- 密钥协商算法使用 ECDHE;
- 签名算法使用 RSA;
- 握手后的通信使用 AES 对称算法,密钥长度 128 位,分组模式是 GCM;
- 摘要算法使用 P-256;
前向安全简单理解是,如果有黑客不断的窃取密钥交换的信息,RSA一旦被破除加密出公钥的私钥公式,那么他可以用这个私钥破除之前所有的消息,获取所有的内容。
也就意味着,如果黑客破开某个大型网站的服务器私钥,那么可以借此私钥对于所有网站信息进行窃取,这种情况后果不堪设想。
随着量子计算机的发展和不断成熟,传统的加密手段在不断更新换代,密码安全也在遭遇前所未有的挑战。为了破除RSA的魔咒,目前的HTTPS传输的密钥交换算法采用了ECDHE,ECDHE对DH算法的封装和改进,DH算法解决了密钥在双方不直接传递密钥的情况下完成密钥交换,这个神奇的交换原理完全由数学理论支持,至于怎么支持的,个人也并不是很清楚,就不误人子弟了,可以自行上网查找相关资料。
Server Key Exchange
ECDHE的交换方案我们可以简答理解为:(随机私钥+ 数学推导公式 = 随机公钥 + 非对称加密)。
通过签名的内容我们知道,只要是在网络上传输真实公钥,就是存在安全隐患的,所以现在的密钥交换通常使用ECDHE,HTTPS中Server Key Exchange中选择RSA加密还是ECDHE加密差别是很大的。
这一篇文章以现在流行的ECDHE加密为主,将不会再补充RSA的校验(但是会提醒区别),少记点东西给自己减轻点负担嘛。
由于ECDHE本身的复杂性,Server Key Exchange 首先会随机生成一个椭圆曲线的私钥以及随机公钥,这个公钥叫做椭圆函数曲线公钥。
意思是说我选的这个算法很复杂,再给你一个公钥,这个公钥在后面的对称加密密钥获取有帮助,但是为了防止这个公钥被拿到,我在公钥上面再用RSA私钥再加密一遍,然后RSA公钥也给你,你拿到后用公钥解开。
到这里Certificate本身的含义就发生了很大变化了, 原本对于RSA算法来说,只需要把这个值设置为非对称加密的公钥即可,到这里RSA实际上掩盖的是被数学公式运算过的椭圆曲线公钥(Server Pararms),也就是说RSA加密的是可以被当作是一个随机生成的公钥,这个公钥就可以保证哪怕真的被破解出私钥,也是具备前向安全性的。
这里不好理解,我简化一下:简单理解是狸猫换太子,服务器给客户端的RSA公钥,解开来是加密算法的公钥,但是不能用这个公钥加密,这个公钥只是一个算法的“媒介”。
客户端拿到公钥需要小心保管这个算法“媒介”,黑客哪怕真的拿到这个媒介,实际上也是没啥用的,这一点同样下文介绍。
ECDHE我们可以理解为,算法在服务器和客户端这两边各给了半片钥匙,要凑齐一个钥匙,需要都有这两个半片钥匙,再搞点特殊的粘合剂粘在一起,才是真正的“公钥”。
哪怕有了数字证书,加密手段还一套又一套。如果想了解这个公式的计算可以看看3.4 HTTPS ECDHE 握手解析 | 小林coding (xiaolincoding.com)。我数学差就不带大家看了。CertificateRequest*
在发送Server Done之前,如果本次HTTPS是双向认证,那么客户端此时可以通过CertificateRequest* 索要客户端证书,保证双方向的信息安全。
客户端证书使用较少,个人也没有实验环境,这里不做过多解释,PASS。
Server Done
把上面一套事情做完之后,服务器发送Server Done作为第二次握手的结束标志。接下来就是等待服务器是否认可。
HTTPS 第三次握手
文章图片
第三次握手首先会收到服务端的证书信息以及 Certificate, Certificate 经过了ECDHE加入的魔法已经很复杂了,所以我们放松一下,先看看服务端证书校验部分。
文章图片
证书校验过程签名实际前文提到过了,这里简单概括几个关键步骤:
- 根证书自上到下验证,进一步加强CA证书的可靠性和安全性。
- 使用CA的公钥解密出证书,然后按照通用的签名算法,对于数字证书做加签比对,任意一方不对等都可以认为请求是否问题的,此时客户端可以拒绝HTTPS通信。
- 证书信任链,信任链从根证书取出公钥验证下一级证书,直到拿到服务器证书为止。
- 浏览器内置CA公钥,不经过网路传输,会导致黑客没法做数字证书的手脚。
- 数字证书认证分为两层,第一层是CA非对称加解密认证,第二层是数字证书形成的“指纹”验证,如果证书有任何改动,指纹都是匹配不上的,如果私钥加密信息被篡改,解开的信息会是客户端无法识别的乱码。
文章图片
证书验证没问题的情况下,如果服务端上要求Certificate*,客户端也要按照基本要求发送客户端证书+数字签名+客户端公钥,但是这里可以发现有个漏洞,客户端无法证明自己就是自己,为什么会这么说,先别急,我们先看看单向认证如何处理。
我们接着说ECDHE这个复杂的要死的算法,拿数字证书里面的服务端Certificate,取出服务端的随机公钥,注意这里拿到的是非对称加密的公钥,不是密钥交换算法的公钥。
公钥、公钥,非对称加密公钥,椭圆函数曲线公钥,数字证书公钥,客户端证书公钥,哪来那么多公钥!服务器接着也按照服务端(实际上是ECDHE)的要求,也自己生成一套椭圆曲线的公钥(Client Params),然后用非对称加密的公钥给他加密一遍确保安全,再也发送 ClientKeyExchange传给服务器。
到这一步,客户端此时把两个“半片”钥匙都拿到了,接着就看看服务器能不能知道自己的“暗号”了。
CertificateVerify* (客户端认证)
还记得签名说的服务端的客户端认证么,发送完ClientKeyExchange之后,如果有客户端证书校验,还需要加一步CertificateVerify操作,里面包含了之前交互的所有报文+签名。
这里可能会有疑问,客户端不是有证书么?为啥还要自己再发个指令证明一遍自己是自己,为什么不能像服务器一样放到 ClientKeyExchange里面发给服务器减少交互步骤?
这个问题老外很多年前就提到过,具体看看下面的帖子。这里我把下面的文章回答内容大概看了下,然后按照自己的思路理解一遍:
https://security.stackexchang...
第一个问题:能够给材料做私钥加密不能证明客户端有对应的证书的私钥。
这里个人举个例子解释一下,比如你在大学上选修课,你上课上到一半想要逃课回寝室打游戏,但是又不能让位置空着,于是你从别的班花钱请了个小弟帮你顶包骗过老师。
这时候老师要怎么证明这个学生看上去是不是不对劲呢?最简单的办法是把这个同学叫起来,问一问他这节课之前讲了哪些内容呀,这种被顶包上去的,一问心里慌了把你供出来了,老师很生气直接说“我的课你也敢逃,那你以后都不用来了,期末成绩直接不及格!”,你心里后悔,要是把之前讲过的课和顶包同学解释一遍,就不会出问题了。
大概就是这么个道理,应该比较形象了吧,嗯。
第二个问题:客户端认证是可选的,和单向认证的相关内容放到一起实际上不太合适,万一哪一天客户端认证火起来就更难办了。
为了不要有过多的双向认证干扰,客户端认证就介绍到这,更多内容可以自行上网搜索。
Change Cipher Spec
做完ECDHE传输之后,我们可以看到目前的情况是这样的,客户端和服务端各自持有 (Client Params) 以及 (Server Params) 这两个公钥,于是它们会开始计算真实的会话密钥。
计算会话密钥之前,有一步随机数生成操作,因为TLS 设计者不信任客户端和服务端任意一方,所以他会要求双方拿着 (Client Params) 以及 (Server Params) 这两个公钥+一顿复杂算法生成出一个随机数,叫做 PreMaster 。
PreMaster实际上依然不是会话密钥,这时候还需要拿到第一次握手传递的 Client Random和 Server Random 在加上PreMaster做PRF(伪随机数函数) 运算,生成最终的MasterKey,也就是会话密钥。
所以会话密钥的最终计算公式如下:
master_secret = PRF(pre_master_secret, "master secret",ClientHello.random + ServerHello.random)
是不是很复杂,很复杂对了,不复杂黑客就闯进来了。这样的会话密钥黑客基本上是破不开的,因为PreMaster 不走网络传输,都是双方内部用固定算法算出来的。
这个工作方式实际上比较像我们前面介绍的密码机,也就是说哪怕拿到密码也破不开,只有密码机认识这一串密码,而生成密码机的机器在客户端和服务端两边放着。master_secret叫做主密钥,在RFC中规定是一位固定 48 Bit 的随机数。此外,为再一次确保master_secret 安全性(有完没完!!!),master secret 还不是最终密钥(放过我=-=),还需要再次加上加密套件的 摘要算法,比如这里用的是 AES_128GCM,这样就避免了会话密钥被重复计算的隐患。
双方都有了master_secret和派生的会话密钥,客户端就可以发一个 Change Cipher Spec,告诉服务端我的会话密钥生成了,接下来用“那个”做对称加密吧。
Finished
Change Cipher Spec发送完了,然后再发一个“Finished”消息(Encrypted Handshake Message,所以数据摘要),把之前所有发送的数据做个摘要,这时候再用会话密钥加密一下,让服务器做个验证。
为啥还要在做摘要再验一遍,这里应该比较好理解了,因为按照ECDHE密钥交换算法,最后这个摘要肯定是只有服务端解得开的,中间被窃取是不可能被破解的。
同样的,这个指令也是一个“测试”,如果对方解不开,肯定也是存在猫腻的,无形中又多了层安全网。
这时候你是黑客,你一定是???你们咋就会话加密了?你们的算法呢?你们的交流呢?咋就开始密文传输呢?HTTPS 第四次握手 走过万里长征,最后的交互就简单了。服务器收到客户端的回应,注意这时候传过来的已经全是外人看不懂的密文了,我们抓包看到的也是一堆乱码,这时候服务端通过自己生成的会话密钥解一下密文,然后也按照客户端的Change Cipher Spec和Finished来一套,让客户端也确认一下。
两边都核实无误,第四次握手就完成了,可以看到第四次握手是很简单的,简单的确认操作。
最终HTTPS连接构建完成,客户端开开信息上网,服务端安安心心传数据,黑客无可乘之机。
小结 我们遍历了整个HTTPS TLS1.2 的历史发展和设计,可以看到HTTPS本身虽然细节很多,但是掌握大体内容之后并不是特别难,重点部分在于理清对称密钥加密是如何计算,也就是目前主流 ECDHE密钥交换算法的一些大概流程,还有CA数字认证的这一个关键过程。
需要注意的是整个HTTPS的交互过程是非常灵活的,除非某些关键步骤之外,其他的子协议是类似“插件”一般可选的,虽然我们传统意义上认为HTTPS就该是有CA认证,就该有非对称加密和算法,实际上设计上而言这些东西都无关紧要。
从官方给的案例来看就是下的情况:
文章图片
上面这张图的简化流程可以给大伙比喻成一段话:Hello,你好,请使用密文传输,Over,好的,使用密文传输,Over,哔哔哔哔哔.....,哔哔哔哔哔.......。实际上HTTPS就是通过这一段话不断扩展出来更多细节的。
之所以设置的这么灵活,个人认为是设计者不想把流程设计的那么死,另一方面是由于子协议自身模块化的思想决定了不能把所步骤当成一个整体来看待。
不过话说回来,现实情况是TLS协议本意是灵活扩展,但是TLS1.2经过长达数十年互联网膨胀,已经牵一发而动全身,TLS1.3的升级也因为历史发展问题,不得不对TLS1.2做出让步和妥协。
值得一提的是,这一节HTTPS建立连接的过程没有以RSA作为密钥交换算法介绍,而是介绍现在真正主流的ECDHE(因为RSA在TLS1.3宣布禁止使用RSA算法),这算法很复杂,要真正完全理解需要比较强的数学功底,但是我们跳过数学的部分,重点介绍了在HTTPS中的作用,个人把它形象理解为两个半片钥匙,通过魔法合成魔法门,这三个内容搭配成随机摘要算法的“根”,最后再用“根”生成出强随机和安全性的会话密钥。
还有令我感到亮眼的地方是,密钥交换算法ECDHE在双方各有“暗号”的情况下,会根据暗号生成一个新的难以破解的暗号,这进一步加强了会话密钥的安全。
ECDHE最大功劳是真正会话密钥的交换不需要网络传输,而是通过数学公式推导算出来的,这最大程度保证安全性。当然是在量子计算机还没到来之前。从软件设计思想来看,协议制定和软件更新换代通用面临向后兼容问题,没有完美的标准,只有不算完美的前后兼容。
写在最后 以上便是我对HTTPS整个握手流程的看法。理论性很强,但是对于后面的实战十分重要。每个人看到的细节不一样,所以有疑问一定要多提问,多思考和反思,越是复杂的东西,越是要打破砂锅问到底。
比如个人不太懂 ECDHE 算法的公钥上再加入RSA算法是如何识别的,最后绕进去发现自己把非对称加密密钥和椭圆曲线函数公钥搞混了。
以上就是HTTPS TLS1.2 的概念部分,内容很长,感谢耐心观看,如有描述错误或者语句错误的地方欢迎指出。