HTTPS

TLS 和 SSL

SSL是安全套接字层(Secure Socket Layer)的缩写,简而言之,它是保持互联网连接安全的标准技术,保护两个系统之间发送的任何敏感数据,防止犯罪分子读取和修改任何传输的信息,包括潜在的个人详细资料。这两个系统可以是一个服务器和一个客户端(例如,一个购物网站和浏览器)或服务器到服务器(例如,一个有个人身份信息或工资信息的应用程序)。

它通过确保用户和网站之间或两个系统之间传输的任何数据都不可能被读取。它使用加密算法对传输中的数据进行窜改,防止黑客在连接中读取数据。这些信息可能是任何敏感或个人的信息,包括信用卡号码和其他财务信息、姓名和地址。

TLS(Transport Layer Security)只是SSL的一个更新、更安全的版本。我们仍然将我们的安全证书称为SSL,因为它是一个更常用的术语,但当你从 DigiCert 购买 SSL 时,你实际上是在购买最新的TLS 证书,可选择 ECC、RSA 或 DSA加密。

当网站由SSL证书保护时,HTTPS(超文本传输协议安全)出现在URL中。点击浏览器栏上的锁符号可以查看证书的详细信息,包括签发机构和网站所有者的公司名称。

HTTPS

Netscape在 1994 年设计了 HTTPS 协议,使用安全套接字层(Secure Sockets Layer,SSL)保证数据传输的安全Embedded Software: The Works - Colin Walls,随着传输层安全协议(Transport Layer Security,TLS)的发展,目前我们已经使用 TLS 取代了废弃的 SSL 协议,不过仍然使用 SSL 证书一词。What is SSL, TLS and HTTPS? | DigiCert

HTTPS 是对 HTTP 协议的扩展,我们可以使用它在互联网上安全地传输数据,然而 HTTPS 请求的发起方第一次从接收方获取响应需要经过 4.5 倍的往返延迟(Round-Trip Time,RTT)。本文将详细介绍请求发起和响应的过程,分析为什么 HTTPS 协议需要通过 4.5-RTT 的时间获得服务提供方的响应:

  • TCP 协议 — 通信双方通过三次握手建立 TCP 连接;
  • TLS 协议 — 通信双方通过四次握手建立 TLS 连接TLS Protocol
  • HTTP 协议 — 客户端向服务端发送请求,服务端发回响应;

这里的分析建立在特定版本的协议实现以及常见场景上,随着网络技术的发展,我们能够减少需要的网络通信次数,本文会在对应章节中提到一些常见的优化方案。

HTTP 协议作为应用层协议,它需要底层的传输层协议为其提供基本的数据传输功能,HTTP 协议一般都会使用 TCP 协议作为底层协议。为了阻止错误的建立历史连接,TCP 协议通信的双方会通过三次握手建立 TCP 连接,简单回顾一下 TCP 连接建立的整个过程。

  1. 客户端向服务端发送带有 SYN 的数据段以及客户端开始发送数据段(Segment)的初始序列号 SEQ = 100
  2. 服务端收到数据段时,向客户端发送带有 SYN 和 ACK 的数据段;
    1. 通过返回 ACK = 101 确认客户端数据段的初始序列号;
    2. 通过发送 SEQ = 300 通知客户端,服务端开始发送数据段的初始序列号;
  3. 客户端向服务端发送带有 ACK 的数据段,确认服务端的初始序列号,其中包含 ACK = 301

TLS

TCP 连接的双方会通过三次握手确定 TCP 连接的初始序列号、窗口大小以及最大数据段,这样通信双方就能利用连接中的初始序列号保证双方数据段的不重不漏、通过窗口大小控制流量并使用最大数据段避免 IP 协议对数据包的分片。``

何时进行 TLS 握手?

用户导航到一个使用 HTTPS 的网站,浏览器首先开始查询网站的原始服务器,这时就会发生 TLS 握手。在任何其他通信使用 HTTPS 时(包括 API 调用和 DNS over HTTPS 查询),也会发生 TLS 握手。

通过 TCP 握手连接后,将发生 TLS 握手。

TLS握手

TLS 的作用是在可靠的 TCP 协议上构建安全的传输通道,其本身是不提供可靠性保障的,我们还是需要下层可靠的传输层协议。在通信双方建立可靠的 TCP 连接之后,就需要通过 TLS 握手交换双方的密钥了。

在 TLS 握手过程中,客户端和服务器一同执行以下操作:

  • 指定将要使用的 TLS 版本(TLS 1.0、1.2、1.3 等)
  • 决定将要使用哪些密码套件
  • 通过服务器的公钥和 SSL 证书颁发机构的数字签名来验证服务器的身份
  • 生成会话密钥,以在握手完成后使用对称加密

TLS 1.2 的连接建立过程TLS 1.2 Connection

  1. 客户端向服务端发送 Client Hello 消息,其中携带客户端支持的协议版本、加密算法、压缩算法以及客户端生成的随机数
  2. 服务端收到客户端支持的协议版本、加密算法等信息后;
    1. 向客户端发送 Server Hello 消息,并携带选择特定的协议版本、加密方法、会话 ID 以及服务端生成的随机数
    2. 向客户端发送 Certificate 消息,即服务端的证书链,其中包含证书支持的域名、发行方和有效期等信息;
    3. 向客户端发送 Server Key Exchange 消息,传递公钥以及签名等信息;
    4. 向客户端发送可选的消息 CertificateRequest,验证客户端的证书;
    5. 向客户端发送 Server Hello Done 消息,通知服务端已经发送了全部的相关信息;
  3. 客户端收到服务端的协议版本、加密方法、会话 ID 以及证书等信息后,验证服务端的证书;
    1. 向服务端发送 Client Key Exchange 消息,包含使用服务端公钥加密后的随机字符串,即预主密钥(Pre Master Secret);
    2. 向服务端发送 Change Cipher Spec 消息,通知服务端后面的数据段会加密传输;
    3. 向服务端发送 Finished 消息,其中包含加密后的握手信息;
  4. 服务端收到 Change Cipher Spec 和 Finished 消息后;
    1. 向客户端发送 Change Cipher Spec 消息,通知客户端后面的数据段会加密传输;
    2. 向客户端发送 Finished 消息,验证客户端的 Finished 消息并完成 TLS 握手;

TLS 握手的关键在于利用通信双方生成的随机字符串和服务端的公钥生成一个双方经过协商后的密钥,通信的双方可以使用这个对称的密钥加密消息防止中间人的监听和攻击,保证通信的安全。

在 TLS 1.2 中,我们需要 2-RTT 才能建立 TLS 连接,但是 TLS 1.3 通过优化协议,,将两次往返延迟降低至一次,大幅度减少建立 TLS 连接所需要的时间,让客户端可以在 1-RTT 之后就能向服务端传输应用层数据。

TLS 1.3 中的握手有什么不同?

这里就不展开介绍 TLS 1.3 建立连接的过程了,除了减少常规握手下的网络开销,TLS 1.3 还引入了 0-RTT 的连接建立过程;60% 的网络连接都是用户在第一次访问网站或者间隔一段时间后访问时建立的,剩下的 40% 可以通过 TLS 1.3 的 0-RTT 策略解决,然而该策略与 TFO 的实现原理比较相似,都是通过重用会话和缓存来实现的,所以存在一定的安全风险,使用时也应该结合业务的具体场景。

0-RTT是更快的 TLS 握手版本,根本不需要任何往返,或客户端和服务器之间的来回通信。如果客户端和服务器之前已经相互连接(例如,如果用户之前访问过该网站),它们可以各自从第一个会话中获取另一个共享密钥,称为“恢复主密钥”。在第一个会话期间,服务器还会向客户端发送称为会话票证的东西。客户端可以使用此共享密钥,在下一次会话的第一条消息中将加密数据连同该会话票证一起发送到服务器。然后 TLS 会在客户端和服务器之间恢复。

TLS 1.3 不支持 RSA,也不支持易受攻击的其他密码套件和参数。它还缩短了 TLS 握手,使 TLS 1.3 握手更快更安全。

TLS 1.3 握手的基本步骤为:

  • 客户端问候:客户端发送客户端问候消息,内含协议版本、客户端随机数和密码套件列表。由于已从 TLS 1.3 中删除了对不安全密码套件的支持,因此可能的密码套件数量大大减少。客户端问候消息还包括将用于计算预主密钥的参数。大体上来说,客户端假设它知道服务器的首选密钥交换方法(由于简化的密码套件列表,它有可能知道)。这减少了握手的总长度——这是 TLS 1.3 握手与 TLS 1.0、1.1 和 1.2 握手之间的重要区别之一。
  • 服务器生成主密钥:此时,服务器已经接收到客户端随机数以及客户端的参数和密码套件。它已经拥有服务器随机数,因为它可以自己生成。因此,服务器可以创建主密钥。
  • 服务器问候和“完成”:服务器问候包括服务器的证书、数字签名、服务器随机数和选择的密码套件。因为它已经有了主密钥,所以它也发送了一个“完成”消息。
  • 最后步骤和客户端“完成”:客户端验证签名和证书,生成主密钥,并发送“完成”消息。
  • 实现安全对称加密

HTTP

在已经建立好 TCP 和 TLS 通道上传输数据是比较简单的事情,HTTP 协议可以直接利用下层建立的可靠的、安全的通道传输数据。客户端通过 TCP 的套接字接口向服务端写入数据,服务端在接收到数据、进行处理后通过相同的途径返回。因为整个过程需要客户端发送请求以及服务端返回响应,所以耗时是 1-RTT。

HTTP 协议的数据交换只会消耗 1-RTT,当客户端和服务端仅处理一次 HTTP 请求时,从 HTTP 协议本身我们已经无法进行优化。不过随着请求的数量逐渐增加,HTTP/2 就可以复用已经建立的 TCP 连接减少 TCP 和 TLS 握手带来的额外开销。

Symmetric and Asymmetric Key Encryption

symmetric Key Encryption

我们平时碰到的绝大多数加密就是对称加密,比如:指纹解锁,PIN 码锁,保险箱密码锁,账号密码等都是使用了对称加密。

对称加密:加密和解密用的是同一个密码或者同一套逻辑的加密方式。

这个密码也叫对称秘钥,其实这个对称和不对称指的就是加密和解密用的秘钥是不是同一个

常见的对称加密算法:DES,AES,3DES等等。

asymmetric Key Encryption

非对称加密用的是一对秘钥,分别叫做公钥(public key)和私钥(private key),也叫非对称秘钥。非对称秘钥既可以用于加密还可以用于认证,咱先聊加密。

对称加密存在一个很大的问题,在实际应用中,因为加密和解密使用的是同一个秘钥,所以,服务器和客户端必然是要交换秘钥的,而正是因为有一个交换秘钥这一过程可能会被中间人窃取秘钥,一旦对称加密秘钥被窃取,而且被分析出加密算法的话,那么传输的数据对于中间人来说就是透明的。所以对称加密的致命性缺点就是无法保证秘钥的安全性

非对称加密的秘钥由加密算法计算得出,是成对的,可以被公开的那个秘钥称之为公钥,不能公开的那个私有的秘钥叫私钥。这对公私钥有一个特点,同时也是非对称加密为什么安全的关键就是:使用秘钥对中的一个秘钥加密,加密后的数据只能通过另一个秘钥解密。也就是说使用一对秘钥中的公钥加密数据,只能通过另一个私钥解密出数据。或者反过来,使用一对秘钥中的私钥进行加密的数据,只能通过另一个公钥解密出来。由此可见,从加密的角度来看,公钥和私钥其实作用是等同的,都可以用于加密或解密,只不过当我们使用非对称秘钥用于加密数据时往往是用公钥进行加密。

在 https 的加密中,加密传输的数据本身使用的是对称加密,加密对称秘钥时使用的非对称加密。整个过程是这样的:server 端先生成一对非对称秘钥,将可以公开的公钥发送给 client 端,client 端也决定此次数据传输使用的对称加密算法和对称秘钥,然后利用 server 端给的公钥,对对称秘钥进行加密传输。server 端接受到 client 端发送的对称加密算法和秘钥后,server 端和 client 端的数据传输都使用这个对称秘钥和算法进行对称加密。整个过程中即便 server 端的公钥被中间人知道了内容,但是没有保存在 server 端的私钥,你是无法破译使用公钥加密的对称秘钥的。公钥原本就是可以被随意公开的,拿到也没用,解密需要的是私钥。非对称加密或者说公钥加密之所以能保证加密安全就是因为私钥是保密不公开的,攻击者没有私钥无法破译

常见的非对称加密算法:RSA,ECC

非对称加密和对称加密对比

  1. 对称加密是一个秘钥,非对称加密是一对,两个秘钥
  2. 非对称加密比起对称加密更安全,因为不存在秘钥泄露问题,公钥即便被知道也没关系
  3. 由于使用非对称加密在计算上特别复杂,所以一般来说对称加密的加密解密的速度相对于非对称加密快很多
  4. 非对称秘钥还可以用于认证

非对称密钥认证

非对称加密有时也叫公钥加密,而非对称秘钥认证也被称为私钥认证。我们说使用非对称秘钥对数据进行认证其实就是说确认一个数据是否有没有被篡改过。非对称秘钥除了用于加密数据,用于认证也是非常广泛的,比如手机 apk 的签名, https 中的证书。

原理很简单:比如现在我要认证一个 apk 的代码是否被串改过,首先准备一对非对称秘钥,一般来自权威机构。官方在打包 apk 时不但包含应用代码,还带上一个签名,这个签名这里简单理解为使用私钥对应用代码的 hash 值加密后的数据。在安装 apk 时,android 系统会提取 apk 中的签名,使用公钥解密签名得到原始应用代码的 hash,然后和原始应用代码的 hash 进行比对,如果内容相同,那么 apk 没有被篡改过。如果 apk 的应用代码被第三方修改了,那么从签名中解密出来的 hash 和应用代码的 hash 肯定是不同的。所以可以起到确保应用代码没有篡改,也就是认证

认证的关键其实是因为签名的存在,签名必须保证能拿到 apk 原始应用代码的 hash。至于如何保证签名没有被篡改不在本文讨论范围。

可能有人看了上面对 apk 认证的过程会有这么一个疑问:使用私钥对内容加密可以达到认证的目的,那能不能使用公钥加密来认证呢?

答案肯定是不能的,如果你使用公钥对内容进行加密,那中间人要篡改你的内容,伪造签名超简单,直接使用公钥对伪造后的内容的 hash 加密就可以了。所以使用非对称秘钥可以用于认证的另一个关键就是私钥是不公开的,中间人没法获取私钥,也就没法伪造签名。

数字证书 Digital Certificate

在非对称加密通信过程中,服务器需要将公钥发送给客户端,在这一过程中,公钥很可能会被第三方拦截并替换,然后这个第三方就可以冒充服务器与客户端进行通信,这就是传说中的“中间人攻击”(man in the middle attack)。解决此问题的方法是通过受信任的第三方交换公钥,具体做法就是服务器不直接向客户端发送公钥,而是要求受信任的第三方,也就是证书认证机构 (Certificate Authority, 简称 CA)将公钥合并到数字证书中,然后服务器会把公钥连同证书一起发送给客户端,私钥则由服务器自己保存以确保安全。数字证书一般包含以下内容:

  1. 证书所有者的公钥

  2. 证书所有者的专有名称

  3. 证书颁发机构的专有名称

  4. 证书的有效起始日期

  5. 证书的过期日期

  6. 证书数据格式的版本号

  7. 序列号,这是证书颁发机构为该证书分配的唯一标识符

    … …

数字签名 Digital Signature

这个概念很好理解,其实跟人的手写签名类似,是为了确保数据发送者的合法身份,也可以确保数据内容未遭到篡改,保证数据完整性。与手写签名不同的是,数字签名会随着文本数据的变化而变化。具体到数字证书的应用场景,数字签名的生成和验证流程如下:

  1. 服务器对证书内容进行信息摘要计算 (常用算法有 SHA-256等),得到摘要信息,再用私钥把摘要信息加密,就得到了数字签名
  2. 服务器把数字证书连同数字签名一起发送给客户端
  3. 客户端用公钥解密数字签名,得到摘要信息
  4. 客户端用相同的信息摘要算法重新计算证书摘要信息,然后对这两个摘要信息进行比对,如果相同,则说明证书未被篡改,否则证书验证失败

证书链 Certificate Chain

证书链,也称为证书路径,是用于认证实体合法身份的证书列表,具体到 HTTPS 通信中,就是为了验证服务器的合法身份。之所以使用证书链,是为了保证根证书 (root CA certificate)的安全,中间层可以看做根证书的代理,起到了缓冲的作用,如下图所示:

证书链从根证书开始,并且证书链中的每一级证书所标识的实体都要为其下一级证书签名,而根证书自身则由证书颁发机构签名。客户端在验证证书链时,必须对链中所有证书的数字签名进行验证,直到达到根证书为止。

密码规范和密码组合 CipherSpecs & CipherSuites

通信双方在安全连接中所使用的算法必须符合密码安全协议的规定,CipherSpecs 和 CipherSuites 正好定义了合法的密码算法组合。CipherSpecs 用于认证加密算法和信息摘要算法的组合,通信双方必须同意这个密码规范才能进行通信。而 CipherSuites 则定义了 SSL / TLS 安全连接中所使用的加密算法的组合,该组合包含三种不同的算法:

  1. 握手期间所使用的的密钥交换和认证算法 (最常用的是 RSA 算法)
  2. 加密算法 (用于握手完成后的对称加密,常用的有 AES、3DES等)
  3. 信息摘要算法 (常用的有 SHA-256、SHA-1 和 MD5 等)

reference

HTTP协议简介