这个世界出现了 HTTPS , 是出于隐私和安全考虑的, 假如这个世界没有 HTTPS , 而只有使用明文传输的 HTTP 会怎么样:

明文


我们首先想象出一个 客户端(C)和服务端(S)

然后 C 向 S 明文传输了一个数据, 叫做 data(未加密)

这个 data 是以明文的方式存在于网络之中, 假如这时一个黑客(hacker)杀了进来

这个 hacker 很容易就可以获取到这个 data, 那假如这个 data 是你的账户密码, hacker 可以很轻松的把你的钱全部转走

显然这种方式是不可取的, 尤其是在电子货币高度发达的今天


对称加密


防止 hacker 再次把你的密码获取, 你有一个算法(f_1), 并且有一个密钥(k)

那么你现在传输数据, 首先会计算加密后的数据

加密后的数据 = f_1(k, data) = x_1

那么这个 f_1 还有一个兄弟算法叫做 f_2

原 data = f_2(k, x_1) = data

了解了对称加密的基本原理后, 我们在回到刚才的传输过程

C 向 S 索要 k

C 把加密好的 x_1 发送给 S , S 可以使用 k 解密

假如这时又有一个 hacker 杀了进来, 截获到了 x_1 , 但是 hacker 不知道 k 也就无法得知 data

但真的是这样吗?

S 在制定 k 的时候不可能知道一共有多少个客户端, 也不可能为每一个客户端都量身订制一个 k , 也就是说这个 k 只能有一个

问题来了, S 不知道 hacker 是黑客, 照样会老老实实的把 k 告诉 hacker , 那么 hacker 有了 k , 自然可以把 x_1 解密出 data

如果每个人都用相同的 k 做密钥, 那么加密毫无意义


非对称加密


你现在有一个算法(f)和公钥(pk), 服务端(S)拥有私钥(sk)和公钥(pk)且只有服务端知道私钥

公钥加密data: f(pk,data) = x
私钥解密x: f(sk,x) = data
私钥加密data: f(sk,data) = x’
公钥解密x’: f(pk,x’) = data

首先 C 向 S 索取 pk

C 加密 data ( f(pk,data) ) 得到 x , 并发送给 S

S 通过 sk 解密 x 得到 data ( f(sk,x) = data )

这个过程看似很完美, hacker 截取到 x , 但因为没有 sk 所以无法解密

但是这个过程有一个致命的缺点, 就是 S 如何向 C 发送数据

S 向 C 发送数据肯定不能使用 pk 加密, 要不然就和对称加密一样了, 那么只能使用 sk 加密出 x’

S 向 C 发送 x’

C 使用 pk 解密出 data

这里犯了和对称加密一样的错误, 因为 hacker 也可以拿到 pk , 因为 pk 人人可以拿到, 所以非对称加密也是不可行的


有一群聪明的数学家从对称加密和非对称加密中获取了灵感, 分析了它们的缺点

对称加密的缺点是 k 只能有一个, 否则就是完美无缺的

非对称加密的缺点是 S 向 C 传输数据的过程

这群数学家想出了一个近乎完美的解决办法, 就那就是将二者结合起来


对称 + 非对称


首先, S 拥有 pk & sk

C 向 S 索取 pk

C 生成一个字符串(num1)

C 使用 pk 加密 num1 发送给 S, 假设它叫 y

然后 S 对 y 使用 sk 解密出 num1

这个 num1 就作为之后加密用的 k

S 通知 C 协商完毕, 之后的加密都使用 num1(k)

传输数据过程就变为和对称加密一样, 只不过 k 变成了临时协商的 num1

因为 num1 是临时协商的, 所以 hacker 截获到了 密文(x) 也无法解密

但这并不是无懈可击的, 我们的 hacker 脑洞大开, 想出了一种新的解决办法


中间人攻击


在 C 向 S 索取 pk 之前, hacker比他们还快一步,充当了一个假的 S 向 C 提供 pk’

因为不是 S 的 pk 我们把 hacker 的 pk 叫做 pk’

C 以为获取到了 pk 很开心, 很快就生成了 num1

hacker 装扮成好人, 去找 S 索取 pk

然后 C 把 num1 使用 pk’ 加密为 yy(和y区分), 发送给 hacker

hacker 拿到 yy 后使用自己的 sk’ 解密出 num1 后, 依旧演戏, 通知 C 协商完毕, 后面的加密使用 num1

然后 hacker 做了第二手打算, 充当自己是无辜的良民, 使用刚才获取到的 pk 把 num1 加密发送给 S

S 不知道是 hacker 的请求, 还傻乎乎的通知 hacker 协商成功

之后 C 向 hacker 发送加密数据 x, hacker 也向 S 发送 x

S 向 hacker 返回 x’, hacker 向 C 返回 x’

且因为 hacker 获取到了 num1, 过程中所有数据 hacker 都可以解密

假如你向小明转账 1000元, hacker 就可以把收款人改成自己, 并且向你返回错误的信息让你误以为转账成功

那么有没有什么办法可以避免中间人问题呢

我们分析一下, C 在向 S 获取 pk 的第一步, hacker 就已经截胡, 这就导致 C 认为 hacker 就是 S


对称 + 非对称 + CA


解决中间人问题其实也很简单,我们引入一个权威的 CA 机构

因为 C 向 hacker 请求 pk 不知道这个 pk 其实是 pk’, 所以我们可以让 CA 把这个 pk 认证一下, 只有通过的才是真正的 pk, 否则就是 hacker 的 pk’

首先 CA 有公钥和私钥, 我们管它们叫 cpk & csk

然后 S 也有公私钥 pk & sk

CA 把使用 csk 加密过的 pk 办法给 S, 我们称之为 licence(lic)(证书)

C 就不请求 pk 了, 而是向 S 请求 lic

那么 C 只要得到了 cpk 就可以解密出原来的 pk 了(和非对称加密原理类似)

那么 C 向 CA 去获取 cpk 的过程中依旧有可能会被 hacker 拦截, 那么怎么办?

其实并不需要去找 CA 请求, 因为 cpk 早就存放在了 C 的操作系统中

补充一下, hacker 也是可以拿到 pk 的, 因为 hacker 本身可以是 C

再来看, C 生成 num1 并使用 pk 加密, 假如这时 hacker 变成了中间人

C 向 hacker 发送加密后的 y, 因为 hacker 只有 pk 所以不能解密, 也就对 y 无能为力, 更不能解密过程中的任何数据


举例


这里我们以访问 bilibili 为例

  1. 小明向 bilibili 发送请求, 请求中包含的数据有 “本机支持的SSL版本”、”非对称算法”、”num1”

  2. bilibili 知道了请求, 把 “要使用的SSL版本”、”对称算法”、”num2”定了下来, 和 lic 一起打包发送给小明

  3. 小明去认证 bilibili 发送来的 lic, 如果真实有效则继续, 否则终止(或浏览器提示证书有问题)

  4. 小明向 bilibili 发送数据, 包含 “num3”、”使用hash加密后的1和2步的所有数据(假如是xx)”

  5. bilibili 验证 xx 是否等于 hash(1, 2), 如果不等于代表是 hacker 上传的数据, bilibili 如果验证是小明本人, 则把 num1、num2和num3 生成一个 k

  6. bilibili 向小明发送数据 hash(1, 2, 4)(假如是zz)

  7. 因为小明参与了之前的交互,所以可以验证 zz 是否是 hash(1, 2, 4), 如果是, 小明也使用 num1、num2和num3 生成一个 k, 因为算法一致, 小明的k 和 bilibili的k 应该是一致的

至此协商过程完毕, 双方可以放心进行数据传输