公开密钥密码学(英语:Public-key cryptography)也称非对称式密码学(英语:Asymmetric cryptography)是密码学的一种算法,它需要两个密钥,一个是公开密钥,另一个是私有密钥;公钥用作加密,私钥则用作解密。由于加密和解密需要两个不同的密钥,故被称为非对称加密。基于公开密钥加密的特性,它还能提供数字签名的功能,使电子文件可以得到如同在纸本文件上亲笔签署的效果。
——Wikipedia
自1796年非对称加密被提出,如今已经应用于现代信息社会的方方面面:当你点开一个网页,你的数据传输是通过非对称加密进行的;当你用Telegram拨出一个电话,你的通话内容是通过非对称加密进行的;当你通过数字证书签署你的一份文件、登录你的网上银行,你的身份验证也是通过非对称加密进行的。
本文将试图尽可能简明地阐释这一信息时代信任体系的基础框架。
公钥与私钥
想象一下Alice住在华南,要和居住在华北的Bob进行加密通信,当然你会想,他们之间只需要先想好一个密码,然后告诉对方,这样的话他们用哪个密码进行交流不就可以了?
但是仔细想想这是不太方便的,首先Alice要用电话、网络或是其他远距离通信的手段和Bob交流,而这些信道本来就是不可信的(不然我们还讨论这个干啥),如果他们在这些信道上协商密码,实际上是可以被第三方窃取到的。所以要确保通信的安全,Alice和Bob只能进行线下的密码协商。实际上我们可以想象得到,这样的加密也并不是那么安全,即使双方在线下协商好了密钥,窃听者依然可以通过默默收集他们此前的通信数据,然后等待两人一不小心泄露了密码,或者密码被暴力破解时候,全部解密一网打尽。
非对称加密就很好地解决了这个问题。首先和对称加密不一样,非对称加密的密钥是成对生成的,一个为公钥(Public Key)与私钥(Private Key),公钥和私钥的生成多半基于随机数,基本不会受到用户主观意志的影响(可以防止你设一些常用密码或者和你本人信息相关的密码被猜到)。至于这两个密钥的用途嘛,顾名思义公钥是可以公开的,而私钥用户则应该妥善保存。
加密与解密
在加密和解密时,一对密钥对中的公钥用于加密数据,而只有对应的私钥才能够解密数据。
当Alice和Bob互相生成了公钥和私钥,他们之间就可以尝试进行保密通信了。首先Alice和Bob分别将自己的公钥发送给对方,此时Alice如果要发信息给Bob,只需要用Bob的公钥进行加密,然后发送给Bob,Bob再用对应的私钥解密即可。注意我们之前提到过,只有公钥对应的私钥才能解密对应的数据,而Alice和Bob在整个过程中是没有泄露过他们的私钥的,所以整个通信是基本安全的(之所以说基本是因为可能存在中间人攻击,我们将在后面讨论这个问题)。
为使上述过程更形象地理解,可以看作Bob和Alice各自生成了一把钥匙,以及和这个钥匙配套的一堆锁,然后他们把锁给对方,这样当Alice发东西给Bob时,她就用Bob的锁锁上,这样中间无论经过多少人中转,最后也只有Bob的钥匙才能打开。
数字签名
非对称加密的算法基本还支持一个数字签名功能,这里不同的算法具体实现会有略微区别,但整体上大概按照以下过程进行,仍然是Alice发送信息给Bob,只是这次不需要保密,但是Bob需要确认是Alice发送的,并且中途没有被网络上其他人更改,于是Alice先将内容写好,通过哈希函数计算出信息的摘要,然后使用私钥对摘要进行加密,最后连同信息明文一起发送给Bob。此时Bob手里是有Alice的公钥的,于是他通过Alice的公钥解密出摘要,再把他实际收到的信息做相同的哈希,把两个摘要进行对比,就可以判断是否是Alice发的以及内容是否被篡改。
当然我们解释一点,此前我们说公钥用于加密,私钥用于解密,但在一些算法中公钥和私钥是互逆的(例如RSA),当然也有一些算法只能签名或者只能加密,所以签名时实际上是用需要保密的私钥进行加密,这样也才能确定用户身份嘛。
信任链
后面中间人攻击部分我们会提到,公钥事实上是可以被伪造的,而信任链就是为了解决这一问题。
首先我们已经知道我们现有的体系已经可以实现数字签名,而信任链的原理就是对他人的公钥进行签名。比如现在Alice和Bob已经交换了公钥,并且确认没有问题,又有一个Rose是Alice的好朋友,并且已经有了Alice的公钥,她也想和Bob通信。此时Alice就可以将Bob的公钥进行签名,然后发送给Rose,由于Rose已经信任了Alice的公钥,所以也就会一并信任Bob的公钥。这样经过层层信任组成的链条就叫信任链。
中间人攻击(Man-in-the-middle attack, MITM)
上述过程看似无懈可击,但是Alice和Bob建立的通信真的安全吗?
我们来看看如下的攻击,现在有了一个人Jack受朝廷之命负责监听Alice和Bob之间的通信,并且我们假设他可以完整截取两人之间的信息,当Alice把公钥发送给Bob时,公钥被Jack截获,Jack将它保存下来然后把自己的公钥发给Bob,同样地Bob发给Alice的公钥也被截获,Alice收到的也是Jack的公钥。这样一来问题就出现了,Bob再次发消息给Alice时,Jack用自己的私钥解密可以先看到内容,然后再用Alice的公钥加密发给Alice,事实上Jack甚至可以修改内容,也能保证对方收到的签名是正确的。
那么怎么避免这种问题呢?现在基本上有两种解决方案。
第一种方案是中心化的,被应用于SSL/TLS等协议,需要一个中间机构颁发证书(又叫CA),此时Bob和Alice的公钥都需要经过CA的数字签名,而CA的公钥(又叫根证书)已经默认保存在了每个人的操作系统中,所以通过CA就可以判断对方的公钥是否有效。这种方式对CA的可信度要求比较高,例如臭名昭著的CNNIC事件就是因为伪造了Google的证书而被从大部分浏览器中剔除了根证书。
另一种方案是去中心化的,被应用于PGP等协议,此时需要用户自己通过其他信道核对密钥,一方面是通过信任链,另一方面直接核对密钥本身,然而密钥太长了核对起来也不方便,所以一般会生成一个密钥的指纹,指纹比较简短,用户只需要核对指纹就好了。
混合加密
非对称加密看起来好是好,但是有个天然的缺陷就是太慢了。所以多数情况下其实是和对称加密混合使用的。回到Alice和Bob的通信,Alice发送给Bob一个大文件,如果再用非对称加密那可就太慢了,所以Alice想到一个好办法,先随机生成一个密码,用这个密码对文件进行对称加密,再把密码本身进行非对称加密,最后一起全部发给Bob,这种非对称加密大大提高了加密的效率,使得你在网络上随便看个视频什么的加载也很快。
前向安全性
作为个人通信用途时,大部分情况下前面的机制已经够安全了,但是如果你是一个网站,你的用户访问时都是经过非对称加密的,但是你的服务器私钥CA只会签发给你一个,如果这个私钥不小心泄露了,那么用户的通信不就全部泄露了?当然不会,实际上虽然你经过CA验证的密钥只有一个,但是你和用户通信的密钥可以有很多啊,你只需要用你的密钥对这些密钥签名,再发给用户就好了嘛。这样每个用户都能使用独一无二的密钥和你的服务器通信,最后即使你服务器的密钥泄露,也不用担心用户的安全问题。