参考文献

  • [SSL/TLS Handshake: Detailed Process and How does it Work](SSL/TLS Handshake: Detailed Process and How does it Work)
  • 极客时间 透视HTTP协议

HTTPS

  • 如果通信过程具备了四个特性,就可以认为是“安全”的,这四个特性是:机密性、完整性,身份认证和不可否认.
    • 机密性(Secrecy/Confidentiality)是指对数据的“保密”,只能由可信的人访问,对其他人是不可见的“秘密”,简单来说就是不能让不相关的人看到不该看的东西.
    • 完整性(Integrity,也叫一致性)是指数据在传输过程中没有被篡改,不多也不少,“完完整整”地保持着原状.
    • 身份认证(Authentication)是指确认对方的真实身份,也就是“证明你真的是你”,保证消息只能发送给可信的人.
    • 不可否认(Non-repudiation/Undeniable),也叫不可抵赖,意思是不能否认已经发生过的行为,不能“说话不算数”“耍赖皮”.

SSL/TLS

  • SSL(Secure Socket Layer,安全套接字层)是一种广泛用于保护互联网通信安全的标准安全协议。 SSL 使用非对称加密来保护信息免遭攻击者访问。 SSL 证书由受信任的证书颁发机构颁发,确保没有人(没有正确的编码/解码密钥)可以读取用户和服务器之间共享的数据。

  • SSL 证书借助两个密钥执行加密和解密过程。一种是公钥,任何试图与站点建立安全连接的人都可以使用它。另一个密钥是由 Web 服务器隐藏的私钥,用于解密从客户端收到的消息。这样,服务器和客户端浏览器之间就建立了安全连接。

  • 如果访问未安装 SSL 证书的网站,所有信息将以纯文本形式共享,这意味着任何人都可以拦截和读取您的私人数据。黑客可以利用这些信息来实现他们的恶意目的。

  • TLS 的密码套件命名非常规范,格式很固定.基本的形式是**“密钥交换算法 + 签名算法 + 对称加密算法 + 摘要算法”**

    • “握手时使用 ECDHE 算法进行密钥交换,用 RSA 签名和身份认证,握手后的通信使用 AES 对称算法,密钥长度 256 位,分组模式是 GCM,摘要算法 SHA384 用于消息认证和产生随机数.”

SSL握手

  • SSL 握手是在服务器和站点之间建立安全连接的过程。这是建立安全连接最关键的步骤之一。建立安全连接后,服务器和客户端都可以放心地相互通信。
  • SSL 握手过程主要执行三件事:
    • Negotiates Cipher Suites协商密码套件
      • 密码套件是指专注于为网络提供安全性的方法的指令集。它提供了一组有关如何使用 HTTPS、FTPS 和其他网络协议进行安全数据通信的指南。
    • Authenticates both browser and the server对浏览器和服务器进行身份验证
    • Exchanges keys 交换密钥
协商密码套件
  • TLS 握手的第一阶段使用户和服务器能够共享其资源,以便可以识别它们共同支持的加密特征。
  • 一旦浏览器和服务器确定了特定的加密策略,他们将利用它向用户提供 SSL 许可证。这称为密码套件。

img

  • 图片来源于https://www.ssl2buy.com/wiki/ssl-tls-handshake-how-does-it-work
身份验证
  • 收到许可证后,客户端的浏览器会进行调查以确保证书是“真实的”。您不仅需要对信息进行编码以获得安全链接;还需要对信息进行编码。您还必须确保将其提交到适当的网站/组织。但他们实现这一目标的方式取决于所使用的密码套件。
  • 所有值得信赖的 SSL 证书均由授权数字证书颁发机构证书颁发机构 (CA) 授予。此类公司需要遵循严格的颁发和证书协议,以便他们授予的许可证得到批准。这主要是为了确保您只能为您拥有的域或企业获取证书。这样,你就可以假设一个CA,比如公证人。签名表明许可证上的个人或组织是其所声称的人或组织。
  • 客户端在整个TLS握手的身份验证过程中进行多次密码安全测试,以确保服务器提交的许可证是合法的。这涉及验证数字签名并确保值得信赖的 CA 已授予证书。
  • 用户此时还确认服务器拥有与证书相关的私钥。所有 SSL 许可证均使用由公钥和私钥组成的密钥对。公钥用于数据加密,私钥用于解密。这被称为“非对称加密”,因为使用单独的密钥来完成该任务。
  • 客户端可以使用公钥对随机信息进行编码,以使用最流行的公钥密码系统 RSA 创建会话密钥。服务器只有拥有私钥才能解码和使用信息,从而确保所有权证明。
密钥交换
  • 该过程的最后部分包括生成“会话密钥”,这是最终使用的安全交互的关键。
  • 会话密钥是“对称的”,这意味着编码和解码使用相同的密钥。这些密钥比非对称密钥更能有效地实现有效加密,因此非常适合在 HTTPS 链接上来回传输数据。
  • 根据所选的密码套件的不同,创建密钥的准确方法也有所不同,其中 RSA 和 Diffie-Hellman 是两种流行的方法。
  • 双方通知对方已完成握手的所有相关工作,然后双方进行校验和以确保握手的发生没有任何故意干扰或欺诈。
  • 在 HTTPS 链接中,这是必须发生的第一阶段,甚至在访问网站之前也是如此。这个 TLS 握手过程发生在很短的时间内。
  • 一旦 SSL 握手完成,并且您和服务器之间传输和获取的所有信息都受到保护,编码和验证的 HTTPS 链接就会启动。

机密性

  • 实现机密性最常用的手段是“加密”(encrypt),就是把消息用某种方式转换成谁也看不懂的乱码,只有掌握特殊“钥匙”的人才能再转换出原始文本.
  • 这里的“钥匙”就叫做“密钥”(key),加密前的消息叫“明文”(plain text/clear text),加密后的乱码叫“密文”(cipher text),使用密钥还原明文的过程叫“解密”(decrypt),是加密的反操作,加密解密的操作过程就是“加密算法”.
  • 按照密钥的使用方式,加密可以分为两大类:对称加密和非对称加密.

对称加密

  • 就是指加密和解密时使用的密钥都是同一个,是“对称”的.只要保证了密钥的安全,那整个通信过程就可以说具有了机密性

  • TLS 里有非常多的对称加密算法可供选择,比如 RC4、DES、3DES、AES、ChaCha20 等,但前三种算法都被认为是不安全的,通常都禁止使用,目前常用的只有 AES 和 ChaCha20.

  • AES 的意思是“高级加密标准”(Advanced Encryption Standard),密钥长度可以是 128、192 或 256.它是 DES 算法的替代者,安全强度很高,性能也很好,而且有的硬件还会做特殊优化,所以非常流行,是应用最广泛的对称加密算法.

  • ChaCha20 是 Google 设计的另一种加密算法,密钥长度固定为 256 位,纯软件运行性能要超过 AES,曾经在移动客户端上比较流行,但 ARMv8 之后也加入了 AES 硬件优化,所以现在不再具有明显的优势,但仍然算得上是一个不错的算法.

加密分组模式

  • 分组模式”的概念,它可以让算法用固定长度的密钥加密任意长度的明文,把小秘密(即密钥)转化为大秘密(即密文).
  • 最早有 ECB、CBC、CFB、OFB 等几种分组模式,但都陆续被发现有安全漏洞,所以现在基本都不怎么用了.最新的分组模式被称为 AEAD(Authenticated Encryption with Associated Data),在加密的同时增加了认证的功能,常用的是 GCM、CCM 和 Poly1305.

非对称加密

  • 对称加密看上去好像完美地实现了机密性,但其中有一个很大的问题:如何把密钥安全地传递给对方,术语叫“密钥交换”.
  • 它有两个密钥,一个叫“公钥”(public key),一个叫“私钥”(private key).两个密钥是不同的,“不对称”,公钥可以公开给任何人使用,而私钥必须严格保密.
  • 公钥和私钥有个特别的“单向”性,虽然都可以用来加密解密,但公钥加密后只能用私钥解密,反过来,私钥加密后也只能用公钥解密.
  • 非对称加密可以解决“密钥交换”的问题.网站秘密保管私钥,在网上任意分发公钥,你想要登录网站只要用公钥加密就行了,密文只能由私钥持有者才能解密.而黑客因为没有私钥,所以就无法破解密文.
  • 在 TLS 里只有很少的几种,比如 DH、DSA、RSA、ECC 等.
  • RSA 可能是其中最著名的一个,几乎可以说是非对称加密的代名词,它的安全性基于“整数分解”的数学难题,使用两个超大素数的乘积作为生成密钥的材料,想要从公钥推算出私钥是非常困难的.
  • ECC(Elliptic Curve Cryptography)是非对称加密里的“后起之秀”,它基于“椭圆曲线离散对数”的数学难题,使用特定的曲线方程和基点生成公钥和私钥,子算法 ECDHE 用于密钥交换,ECDSA 用于数字签名.

混合加密

  • 在通信刚开始的时候使用非对称算法,比如 RSA、ECDHE,首先解决密钥交换的问题.
  • 然后用随机数产生对称算法使用的“会话密钥”(session key),再用公钥加密.因为会话密钥很短,通常只有 16 字节或 32 字节,所以慢一点也无所谓.
  • 对方拿到密文后用私钥解密,取出会话密钥.这样,双方就实现了对称密钥的安全交换,后续就不再使用非对称加密,全都使用对称加密.

摘要算法

  • 实现完整性的手段主要是摘要算法(Digest Algorithm),也就是常说的散列函数、哈希函数(Hash Function).
  • 摘要算法实际上是把数据从一个“大空间”映射到了“小空间”,所以就存在“冲突”(collision,也叫碰撞)的可能性,就如同现实中的指纹一样,可能会有两份不同的原文对应相同的摘要.好的摘要算法必须能够“抵抗冲突”,让这种可能性尽量地小.
  • 因为摘要算法对输入具有“单向性”和“雪崩效应”,输入的微小不同会导致输出的剧烈变化,所以也被 TLS 用来生成伪随机数(PRF,pseudo random function).
  • MD5(Message-Digest 5)、SHA-1(Secure Hash Algorithm 1),它们就是最常用的两个摘要算法,能够生成 16 字节和 20 字节长度的数字摘要.但这两个算法的安全强度比较低,不够安全,在 TLS 里已经被禁止使用了.
  • 目前 TLS 推荐使用的是 SHA-1 的后继者:SHA-2.
  • SHA-2 实际上是一系列摘要算法的统称,总共有 6 种,常用的有 SHA224、SHA256、SHA384,分别能够生成 28 字节、32 字节、48 字节的摘要.

完整性

  • 摘要算法保证了“数字摘要”和原文是完全等价的.所以只要在原文后附上它的摘要,就能够保证数据的完整性.
  • 真正的完整性必须要建立在机密性之上,在混合加密系统里用会话密钥加密消息和摘要,这样黑客无法得知明文,也就没有办法动手脚了.这有个术语,叫哈希消息认证码(HMAC).

数字签名

  • 加密算法结合摘要算法,我们的通信过程可以说是比较安全了.但这里还有漏洞,就是通信的两个端点(endpoint).
  • 数字签名的原理其实很简单,就是把公钥私钥的用法反过来,之前是公钥加密、私钥解密,现在是私钥加密、公钥解密.

数字证书和CA

  • 这里还有一个“公钥的信任”问题.因为谁都可以发布公钥,我们还缺少防止黑客伪造公钥的手段,也就是说,怎么来判断这个公钥就是你或者某宝的公钥呢?
  • CA(Certificate Authority,证书认证机构).它就像网络世界里的公安局、教育部、公证中心,具有极高的可信度,由它来给各个公钥签名,用自身的信誉来保证公钥无法伪造,是可信的.
  • CA 对公钥的签名认证也是有格式的,不是简单地把公钥绑定在持有者身份上就完事了,还要包含序列号、用途、颁发者、有效时间等等,把这些打成一个包再签名,完整地证明公钥关联的各种信息,形成“数字证书”(Certificate).
  • 知名的 CA 全世界就那么几家,比如 DigiCert、VeriSign、Entrust、Let’s Encrypt 等,它们签发的证书分 DV、OV、EV 三种,区别在于可信程度.
  • DV 是最低的,只是域名级别的可信,背后是谁不知道.EV 是最高的,经过了法律和审计的严格核查,可以证明网站拥有者的身份(在浏览器地址栏会显示出公司的名字,例如 Apple、GitHub 的网站).
  • CA 怎么证明自己呢?
    • 这还是信任链的问题.小一点的 CA 可以让大 CA 签名认证,但链条的最后,也就是 Root CA,就只能自己证明自己了,这个就叫“自签名证书”(Self-Signed Certificate)或者“根证书”(Root Certificate).你必须相信,否则整个证书信任链就走不下去了.

证书体系的弱点

  • 证书体系(PKI,Public Key Infrastructure)虽然是目前整个网络世界的安全基础设施,但绝对的安全是不存在的,它也有弱点,还是关键的“信任”二字.
  • 如果 CA 失误或者被欺骗,签发了错误的证书,虽然证书是真的,可它代表的网站却是假的.
  • 还有一种更危险的情况,CA 被黑客攻陷,或者 CA 有恶意,因为它(即根证书)是信任的源头,整个信任链里的所有证书也就都不可信了.
  • 这两种事情并不是“耸人听闻”,都曾经实际出现过.所以,需要再给证书体系打上一些补丁.
  • 针对第一种,开发出了 CRL(证书吊销列表,Certificate revocation list)和 OCSP(在线证书状态协议,Online Certificate Status Protocol),及时废止有问题的证书.
  • 对于第二种,因为涉及的证书太多,就只能操作系统或者浏览器从根上“下狠手”了,撤销对 CA 的信任,列入“黑名单”,这样它颁发的所有证书就都会被认为是不安全的.
证书信任链的验证过程
  1. 服务器去CA机构申请证书,证书中包含了要发给客户端的公钥、签发者、到期时间等等信息.如果这样简单地把证书发给浏览器,中间人可以轻松地修改成自己的公钥,之后的通信就是不安全的了.于是需要一定的加密手段,这里的做法就是使用数字签名:将证书的信息利用摘要算法计算出摘要之后,用CA的秘钥进行加密,生成数字签名.

  2. 服务器将数字证书和数字签名一起发给浏览器,因为有数字签名,所以数字证书无法被中间人做修改(修改后的摘要变动了,与签名里解密出的原始摘要不匹配,所以能够发现原文被窜改),浏览器拿到数字证书之后,去本地的信任机构中查询到对应的机构,利用其公钥解密数字签名,验证证书是否有被修改过.这一步就保证了浏览器获取到的公钥一定是正确的.

  3. 公钥正确地传给浏览器之后,接着就是协商对称加密的密钥,然后通信等等…

HTTPS建立连接

  • 当在浏览器中键入"https"开头的URI并按下回车会发生什么呢?
    • 首先浏览器会先要从URI里面提取出协议和域名.因为协议名是"https",所以浏览器就知道了端口号是默认为443,再用DNS解析域名,得到目标IP地址,然后就可以使用三次握手与网站建立TCP连接了.
    • 在 HTTP 协议里,建立连接后,浏览器会立即发送请求报文.但现在是 HTTPS 协议,它需要再用另外一个“握手”过程,在 TCP 上建立安全连接,之后才是收发 HTTP 报文.
    • 这个“握手”过程与 TCP 有些类似,是 HTTPS 和 TLS 协议里最重要、最核心的部分,懂了它,你就可以自豪地说自己“掌握了 HTTPS”.

TLS协议的组成

  • TLS 包含几个子协议,你也可以理解为它是由几个不同职责的模块组成,比较常用的有记录协议、警报协议、握手协议、变更密码规范协议等

  • 记录协议(Record Protocol)规定了 TLS 收发数据的基本单位:记录(record).它有点像是 TCP 里的 segment,所有的其他子协议都需要通过记录协议发出.但多个记录数据可以在一个 TCP 包里一次性发出,也并不需要像 TCP 那样返回 ACK.

  • 警报协议(Alert Protocol)的职责是向对方发出警报信息,有点像是 HTTP 协议里的状态码.比如,protocol_version 就是不支持旧版本,bad_certificate 就是证书有问题,收到警报后另一方可以选择继续,也可以立即终止连接.

  • 握手协议(Handshake Protocol)是 TLS 里最复杂的子协议,要比 TCP 的 SYN/ACK 复杂的多,浏览器和服务器会在握手过程中协商 TLS 版本号、随机数、密码套件等信息,然后交换证书和密钥参数,最终双方协商得到会话密钥,用于后续的混合加密系统.

  • 最后一个是变更密码规范协议(Change Cipher Spec Protocol),它非常简单,就是一个“通知”,告诉对方,后续的数据都将使用加密保护.那么反过来,在它之前,数据都是明文的.

  • 下面的这张图简要地描述了 TLS 的握手过程,其中每一个“框”都是一个记录,多个记录组合成一个 TCP 包发送.所以,最多经过两次消息往返(4 个消息)就可以完成握手,然后就可以在安全的通信环境里发送 HTTP 报文,实现 HTTPS 协议.

    img img img

TLS1.2的握手过程

  • 第一阶段:C/S两端共享Client Random、Server Random 和 Server Params信息
    • 客户端—>服务器: 客户端的版本号、支持的密码套件,还有一个随机数(Client Random)
      • 用户通过发送“Hello”消息开始握手过程。此消息包含用户支持的 TLS 类型和密码套件。它还包括一串任意字节,称为“客户端随机”。
    • 服务端—>客户端: 客户端的版本号、选择的客户端列表的密码套
      • 如:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384、随机数随机数(Server Random)
      • 在回复中,服务器发送带有 SSL 证书的文本。该文本还包含为此过程选择的密码套件以及由服务器生成的随机字节串(称为“服务器随机”)。
    • 服务端—>客户端: 服务端证书(Server Certificate)
    • 服务端—>客户端: 发送Server Key Exchange类型的请求,携带椭圆曲线的公钥(Server Params)用以实现密钥交换算法,另附私钥签名
    • 服务端—>客户端: 发送完毕
  • 第二阶段:证书验证
    • 验证条件: 客户端证书链逐级验证、证书公钥验证签名,服务端身份验证成功(证书合法)
    • 客户端—>服务端: 发送Client Key Exchange类型的请求,携带椭圆曲线的公钥(Client Params)用以实现秘钥交换算法
  • 第三阶段:主密钥生成
    • 客户端、服务端分别使用Client Params、Server Params通过ECDHE算法计算出随机值pre-master,然后用Client Random、Server Random 和 Pre-Master三个值作为原材料,用PRF伪随机数函数(利用密码套件的摘要算法再次强化结果值maser secert的随机性)计算出主密钥Master Secret,
    • 主密钥并不是会话秘钥,还会再用PRF扩展出更多的密钥,比如客户端发送用的会话密钥(client_write_key)、服务器发送用的会话密钥(server_write_key)
    • 客户端—>服务端: 客户端发一个“Change Cipher Spec”,然后再发一个“Finished”消息,把之前所有发送的数据做个摘要,再加密一下,让服务器做个验证.
    • 服务端—>客户端: 服务器也是同样的操作,发“Change Cipher Spec”和“Finished”消息,双方都验证加密解密 OK,握手正式结束.

TLS1.3

  • TLS1.3精简了加密算法,通过support_groups、key_share、signature_algorithms这些参数就能判断出密钥交换算法和签名算法,不用在cipher suite中协商了
  • RSA握手时,client key exchage会使用RSA公钥加密pre master后传给服务端,一旦私钥被破解,那么之前的信息都会被破译,根本原因还是在于RSA的这一对公钥私钥并不是临时的。
  • 相同点:都在未收到Finished确认消息时就已经向对方发送加密信息了,不同点:TLS1.3将change cipher spec合并到了hello中

HTTPS的优化

  • 可以有多种硬件和软件手段减少网络耗时和计算耗时,让 HTTPS 变得和 HTTP 一样快,最可行的是软件优化;
  • 应当尽量使用 ECDHE 椭圆曲线密码套件,节约带宽和计算量,还能实现“False Start”;
  • 服务器端应当开启“OCSP Stapling”功能,避免客户端访问 CA 去验证证书;
    • Nginx里面可以使用指令"ssl_stapling on"开启"OCSP Stapling"
  • 会话复用的效果类似 Cache,前提是客户端必须之前成功建立连接,后面就可以用“Session ID”“Session Ticket”等凭据跳过密钥交换、证书验证等步骤,直接开始加密通信。

HTTPS配置

申请证书

注意事项
  • 第一,申请证书时应当同时申请 RSA 和 ECDSA 两种证书,在 Nginx 里配置成双证书验证,这样服务器可以自动选择快速的椭圆曲线证书,同时也兼容只支持 RSA 的客户端.
  • 第二,如果申请 RSA 证书,私钥至少要 2048 位,摘要算法应该选用 SHA-2,例如 SHA256、SHA384 等.
  • 第三,出于安全的考虑,“Let’s Encrypt”证书的有效期很短,只有 90 天,时间一到就会过期失效,所以必须要定期更新.你可以在 crontab 里加个每周或每月任务,发送更新请求,不过很多 ACME 客户端会自动添加这样的定期任务,完全不用你操心.

配置 HTTPS

  • 在Nginx配置文件中添加

    1
    2
    3
    4
    5
    listen 443 ssl http2;
    ssl_certificate cert/www.holelin.cn.pem; #需要将cert-file-name.pem替换成已上传的证书文件的名称.
    ssl_certificate_key cert/www.holelin.cn.key; #需要将cert-file-name.key替换成已上传的证书密钥文件的名称.
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    #表示使用的加密套件的类型.
  • 为了提高 HTTPS 的安全系数和性能,你还可以强制 Nginx 只支持 TLS1.2 以上的协议,打开“Session Ticket”会话复用:

    1
    2
    3
    4
    5
      
    ssl_protocols TLSv1.2 TLSv1.3;#表示使用的TLS协议的类型.
    ssl_session_timeout 5m;
    ssl_session_tickets on;
    ssl_session_ticket_key ticket.key;
  • 密码套件的选择方面,建议是以服务器的套件优先.这样可以避免恶意客户端故意选择较弱的套件、降低安全等级,然后密码套件向 TLS1.3“看齐”,只使用 ECDHE、AES 和 ChaCha20,支持“False Start”.

    1
    2
    3
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    #表示使用的加密套件的类型.
    ssl_prefer_server_ciphers on;
  • 重定向跳转

    1
    2
    return 301 https://$host$request_uri;
    # 或者 rewrite ^ https://$host$request_uri permanent;