HTTP 如何加密?

为什么需要加密

简单来说,HTTP 协议的内容是通过明文传递的,任何中间人都可以看到完整的信息,由此就导致了两种攻击:被动窃听攻击,报文的信息可以被抓包获取,从而泄露信息;主动攻击:中间者可以修改 HTTP 协议的内容,从而给客户端或服务端发送伪造的信息。

所以需要一个机制来对 HTTP 的内容进行加密,并且确定消息来源及内容是否被篡改。HTTPS 就可以认为是 HTTP + 加密 + 认证(确定来源)+ 完整性保护(消息没有被篡改)。

对称加密和非对称加密

加密方法主要分为对称加密和非对称加密。对称加密是加密和解密使用同一个密钥,而非对称加密分为公钥和私钥,是一对的关系,信息通常通过公钥加密,私钥解密,反过来也可以。

对称加密在网络中是行不通的,这是因为要安全传输信息,就需要双方都持有一个安全的密钥,而如何安全传输密钥又回到了要安全传输信息的问题上。

非对称方案可以实现双方的加密通信,假设 A 生成公钥 pubApub_A 和私钥 priApri_A,B 生成公钥 pubBpub_B 和私钥 priBpri_B。他们首先会通过明文的方式交换公钥和私钥:

  • A 持有:私钥 priApri_A,公钥 pubBpub_B
  • B 持有:私钥 priBpri_B,公钥 pubApub_A

接着,当 A 要发送信息时,使用公钥 pubBpub_B 加密,这样 B 就能使用私钥 priBpri_B 解密。注意,私钥 priBpri_B 是从来没有发送到网络上的,所以没有人能解密,是安全的。

反过来,当 B 要发送信息时,使用公钥 pubApub_A 加密,这样 A 就能使用私钥 priApri_A 解密。他的安全性保证同理。

image

然而这样的方案在实际实践中通常不被采纳,这是因为非对称加密的计算开销要大得多,如果每次消息都进行非对称加解密的运算,服务器的负载就会大很多。所以在实际实践中,会将对称加密和非对称加密进行结合,首先使用非对称加密来传输一个对称加密的密钥(能保证对称加密的密钥是安全),在后续的通信中就使用该对称加密的密钥进行加解密,减轻了计算开销。

第一次交换对称密钥的过程就被称作是 SSL/TLS 握手过程。实际上也不需要双方都生成一对公私钥,仅需要服务端生成一对公私钥即可。在第一次访问中,服务端(B)会把公钥发送给客户端(A),然后客户端在本地生成一个对称加密的密钥 KeyKey,使用公钥加密之后发送给服务端,服务端受到之后根据自己的私钥进行解密,这样他就可以得到这个对称加密的密钥 KeyKey,随后双方使用这个 KeyKey 进行加解密。(这是一个最简化版本的 SSL/TLS 握手过程)

image-1711113479982

这样的方案确实防止了被动窃听,但是仍然可以被主动劫持攻击。假设存在一个中间人 C,他能获取到第一次明文交换的密钥信息,此时他就劫持了公钥 pubBpub_B,并将自己的公钥 pubCpub_C 分发给客户端。

此时中间人就可以劫持密钥:

  • A 发送密钥时,通过公钥 pubCpub_C 来加密
  • C 劫持该信息,使用 priCpri_C 解密后获取密钥,并用公钥 pubBpub_B 加密信息,随后发送给 B
  • B 获取信息后,通过 priBpri_B 解密信息获取密钥

A 和 B 双方都无法察觉任何异常,但是密钥就已经泄露给 C 了。

image-1711113494937

这是因为第一次分发公钥时,A 无法确定公钥是否真正来自 B,就可能存在这种被掉包的情况。所以确定公钥来源就像是要证明我是我一样,如果我要证明我是我,我可以通过身份证来证明我的身份,但是身份证为什么能证明我是我?这本质上来源于对身份证的发布者——政府的信任,假如有一个权威机构开具一个一定能被信任的证明,那么就可以解决证明我是我的问题。反过来说,假如我想用身份证证明我是我,但是身份证可能是假的,那么证明我是我就不可能。

数字证书:认证

所以回到非对称加密方案中,假如存在一个权威机构来证明公钥的所属,那么就能避免被 C 掉包公钥的情况。也就是说通过某种机制,我们可以在第一次交换公钥时确定,该公钥确实是来自 A、B 双方的,而不是 C 的公钥。

所以就迎来了 CA 机构(Certificate Authority),他颁布的数字证书就是可以证明我是我。我们先暂时假设该数字证书是无法被伪造的,后续再来证明为什么无法被伪造。

数字证书里包含一系列的信息,例如 B 的公钥、网站信息等等这些数据信息,还包含一个对这些数据信息进行编码的数字签名,当 A 受到该数字证书时,通过数字签名就能确定公钥是来自 B 的,而不是被 C 掉包了。我们暂时把数字签名当作是一个黑盒,他通过某种机制来验证证书里的数据信息是没有被修改过的,从而就可以确认公钥 B 是没有被修改过的。

通过数字证书这样的机制,就可以防止中间人攻击。

数字签名:完整性保护

然而数字签名是如何工作的呢?

首先发送方对证书数据(自身的公钥和网站信息等内容)进行一个哈希计算,得到一个哈希值;随后用发送方私钥对哈希值进行加密,此时就得到了数字签名。随后将数字签名和证书一起发送。

接收方接到证书之后,要验证该数字签名,也就是验证证书有没有被篡改过。首先根据证书数据通过同样的哈希计算,得到一个哈希值 H1H^1。接着使用发送方的公钥对数字签名进行解密,得到一个哈希值 H2H^2,比对这两个哈希值是否一致就可以验证证书是否被修改过。

到这里你可能会发现一个 Bug,数字证书是通过数字签名的完整性来保证的,也就是数字证书通过数字签名来保证证书内容没有被修改过。然而数字签名需要数字证书来认证其公钥是真正来自对方的,也就是数字签名需要数字证书来保证这个公钥是没有被劫持的。这就陷入了一个死循环。

image-1711113506849

数字证书链

然而上述过程仍然可能被一个中间人 C 劫持,他只要让接收方的公钥是自己发布的即可,过程如之前所述的类似。所以为了防止这种情况,我们可能真的需要一个全世界都公认的公钥,这个公钥由现实的权威机构保证,是不可能被篡改的。所以我们的操作系统和浏览器会内置一个根证书,包含了这些信息。

由于根证书可信,所以根证书可以给某些中间机构颁发证书,这些证书也是可信的。那么这些被根证书认证的中间机构的证书也是可信的,于是这些中间机构又可以给某些服务器颁发证书。这样一条信任链就形成了,在客户端拿到服务器的证书 C 之后,就可以根据信任链查找根证书,如果根证书和本地安装的是一致的,那么就是可信的。

image-1711113515666

假设根证书没有在本地安装,可以自行进行安装,但是此时就需要自己负责根证书的安全问题。

SSL/TLS 握手

SSL 和 TLS 的概念目前比较模糊,可以认为是一个东西,并不做区分。

TLS 大致的握手过程如下:

  1. 客户端 Hello:包含自己的 TLS 版本和可选的加密算法
  2. 服务端 Hello:决定使用的 TLS 版本和加密算法,并附带数字证书
  3. 客户端密钥交换:验证证书之后,生成一个随机数作为预主密钥,使用服务端公钥加密后发送,本地会根据预主密钥生成一个会话密钥(对称密钥)
  4. 服务端密钥交换:接收到信息后使用私钥解密,得到预主密钥,同样生成一个会话密钥
  5. 完成握手:由于双方生成密钥算法一致,所以双方的对称密钥是一致的,接下来就可以进行加密通信了

实际上根据加密算法和其他更复杂的安全要求,握手流程会更复杂一些,例如客户端和服务端在 Hello 报文中都会附带一个随机数,在后续生成对称密钥时要结合预主密钥和这两个随机数。这样是为了如果客户端泄露预主密钥,则可能导致会话密钥被破解。如果会话密钥是由双方一起生成的,破解者就要同时破解客户端和服务端,难度会大很多。这样的设计同时也可以避免预主密钥生成被预测的问题,假设客户端使用的是某种可复现的随机算法,且随机种子被泄露,破解者就可以轻松破解会话密钥。

上一篇 下一篇

评论 | 0条评论