作者:Windson Yang
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。(www.enginego.org)
我接触过不少工程师对于对称加密,非对称加密,公钥和私钥只停留在应用的层面,而并不了解背后的原理。所以在开发过程中犯了不少错误,而通常涉及加密传输或者加密存储的错误都比较严重,这篇文章着重介绍了密码学常用的工具以及常见场景。
我们日常接触最多的就是对称密码,它最重要的性质有两点:
对称加密有很多名称,例如对称密码,私钥密码,它类似家里的保险柜,把密码设置成9527然后锁上,那么也需要使用9527才能打开。当你要把“芹菜,香菜”利用对称密码“000111”传输给朋友:
# 原文
芹菜,香菜
# 约定一个密钥(不能被第三方知道)
000111
# 把信息和对称密钥异或运算,得到密文
101100
这时候你可以直接把密文“101100”告诉你的朋友。你的朋友使用约定的密钥“000111”对密文进行再一次异或就能得到原文。实际使用中,加解密不止异或一次那么简单,通常会使用分组密码多次迭代异或。常用的对称加密算法有DES与AES。
DES | AES | |
---|---|---|
密钥长度 | 56位 | 128, 192, 256 位 |
加密方式 | 对称分组密码 | 对称分组密码 |
加密轮数 | 16轮 | 128位10轮,192位12轮,256位14轮 |
安全性 | 被攻破 | 安全 |
速度 | 较慢 | 较快 |
对称密码最大的问题是密钥配送问题,也就是如何约定只有传输者和接受者都知道并且足够长的密钥“000111”,任何得到这个密钥的人都能够解密信息。常用的解决方法有两种:
使用密钥分配中心
Sunkist与Cherry想要加密传输信息。
密钥分配中心为每个人都生成一个密钥。 > Sunkist的密钥是000000。
Cherry的密钥是111111。
当Sunkist与Cherry准备加密传输信息的时候,密钥分配中心使用伪随机生成器生成一个会话密钥。 > 会话密钥000111。
分别用Sunkist与Cherry的密钥来加密会话密钥,并且分别发送给Sunkist与Cherry。 > “Hello, Sunkist,会话密钥通过你的密钥加密后是000111。”
“Hello, Cherry,会话密钥通过你的密钥加密后是111000。”
Sunkist与Cherry使用自己的密钥对信息解密,得到会话密钥,然后使用会话密钥进行对称加密传输信息,传输完毕后销毁会话密钥。
不过这个方法的缺点也非常明显:
有什么更好地解决密钥配送方法吗?
非对称密码(也称为公钥密码)与对称密码的不同点是非对称密码包含一个公钥和一个私钥。它有几个性质:
要注意的第三点,虽然使用同样的分组迭代算法(AES,DES),但是公钥对明文运算称为“加密”,私钥对明文运算称为“签名”。在RSA算法中,公钥和私钥的数学关系满足:
公钥 * 私钥 mod L = 1
有些读者看到这个公式,可能会误以为公钥和私钥可以互换,都能用作加密,然后用另外一个解密。而且实际应用中,确实也能互相“解密”。不过,公钥在选取的时候还需要满足一些额外条件。(例如与L互质)所以使用公钥加密才符合严格的数学安全,而私钥“加密”不能。私钥只能用作签名,让持有公钥的人来验证这条信息确实是私钥持有者认证过的,实际用作加密的话安全性没有那么高。
回到密钥配送的问题,想象一个场景,Sunkist,Cherry,Wing三个约在一起见面。任意两个人沟通的内容,第三个人都可以听到。而且他们之间只能通过聊天沟通,不能通过肢体语言或者传纸条的方式交流。Sunkist与Cherry要如何传输信息而不被Wing发现?这是一个非常有趣的问题,在阅读接下来的内容之前,读者们可以先自己尝试去解决这个问题。
要解决这个问题需要两个步骤,第一是商议密钥,要在Wing在旁边的情况下约定一个只有Sunkist与Cherry知道的密钥。(很神奇吧)第二是通过密钥把信息进行对称加密传输,这一步就和文章一开始提到的对称加密一样。为了简单起见,我们假定这个场景中的三人都只会乘法,不会除法。简化版称为Sunkist-Cherry算法:
让Sunkist与Cherry都确定一个算法(通常是公开的算法)。
Sunkist: “我会Diffie-Hellman算法和Sunkist-Cherry算法。”
Cherry:“我只会Sunkist-Cherry算法,那我们就用这个算法吧。”
Wing:“我知道你们要用Sunkist-Cherry算法了。”
Sunkist与Cherry分别选择一个私人数字,这个私人数字必须保密。
Sunkist: “我不会告诉你我选择了什么数字。”(Sunkist选择了数字5,其他两人都不知道。)
Cherry: “我不会告诉你我选择了什么数字。”(Cherry选择了数字10,其他两人都不知道。)
Wing:“我知道你们要用Sunkist-Cherry算法了。”
共同选择一个数字,作为公有数字。
Sunkist: “我们就选择数字2作为公有数字吧。”
Cherry:“好的!”
Wing:“你们要用Sunkist-Cherry算法,而且公有数字是2。”
Sunkist与Cherry分别把公有数字乘以自己的私人数字得到混合数字,然后告诉对方。
Sunkist: “我的混合数字是10。”(5 * 2 = 10)
Cherry: “我的混合数字是20。”(10 * 2 = 20)
Wing:“你们要用Sunkist-Cherry算法,而且公有数字是2。Sunkist的混合数字是10,Cherry的混合数字是20。”
还记得我们一开始的假设吗?在这个世界没有人会做除法,如果Wing会除法的话那么它马上就能用混合数字除以公有数字,得到Sunkist与Cherry的私人数字了。幸好,在我们的假设下,它不知道如何计算出私人数字。
Sunkist与Cherry分别把私人数字与对方的混合数字相乘,得到最终的密钥
Sunkist把私人数字5以及Cherry的混合数字20相乘得到100。
Cherry把私人数字10以及Sunkist的混合数字10相乘得到100。
整个过程中,公钥是2,最终商议出的密钥是100,Wing虽然知道Sunkist与Cherry聊天的全部内容,知道公有数字与它们的混合数字,但是却无法计算出密钥。
Sunkist-Cherry算法利用了一个容易运算(乘法)但不可逆(除法)的数学技巧。在实际应用,我们最常使用Diffie–Hellman算法,它利用了基于有限域上的离散对数的性质。Sunkist-Cherry算法与Diffie-Hellman算法解决了当双方只能公开交换信息的时候商议密钥的问题。要注意,在这里的公私钥并不是用作互相加解密的。
非对称密码除了解决商议密钥的问题,还常常用来解决另外一个问题,例如使用RSA算法,生成一对一一对应的公私钥,经过公钥加密的内容只有私钥才能解密。通过私钥签名的内容通过公钥才能验证。在实际应用中,使用公钥加密,私钥解密用在SSH登录以及Https传输中,而私钥签名,公钥解密则用在数字签名中。我们接下来详细说明下。
Sunkist要找Cherry借钱,大家商议立一个借据。不过如果在无法见面的情况下,如何立借据并且证明这个借据是Sunkist写的呢?这里需要用到RSA算法,Sunkist使用RSA算法生成一对公私钥,把公钥公布在自己的网站上,然后把借据通过私钥签名,所有人都可以通过Sunkist的公钥尝试对签名后的内容解密,能解密成功则证明这个借据是Sunkist写的。使用数字签名有几个好处
不过有一个问题,就是Cherry如何确定这个公钥是Sunkist的,当它要获取Sunkist的公钥的时候,有可能Sunkist的网站被黑了,被换作其他人的公钥了(中间人攻击)。要解决这个问题,我们可以使用数字证书来确保获取正确的公钥。
数字证书的作用就是获取某人的正确公钥,怎么做呢?首先我们需要一个中立可信的证书机构,它有点像注册中心,大家可以把自己的公钥放在它那里保管。当Cherry要获取Sunkist的公钥的时候:
这里有个信任链,我们默认信任了浏览器中的证书机构的公钥,证书机构信任了Sunkist提交的公钥。我们使用证书机构的公钥解密就能得到Sunkist正确的公钥。
SSH登录有两种方式:
SSH在第一次登录主机的时候,会有类似的信息:
The authenticity of host 'host (12.18.429.21)' can't be established.
RSA key fingerprint is 98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d.
Are you sure you want to continue connecting (yes/no)?
什么意思呢?主机会发送它的公钥指纹,你需要验证这个公钥是该主机的。确认之后,它们一开始都需要通过Diffie–Hellman算法商议密钥进行加密传输。然后再进入认证过程,之后的数据传输使用对称加密传输。其中密码登录方式的话直接使用对称加密验证密码。而使用公钥登录分为几个步骤
Https和ssl握手的理解这篇文章有详细地解释Https传输的具体过程:
- 客户端的浏览器向服务器传送客户端 SSL 协议的版本号,加密算法的种类,产生的随机数,以及其他服务器和客户端之间通讯所需要的各种信息。
- 服务器向客户端传送 SSL 协议的版本号,加密算法的种类,随机数以及其他相关信息,同时服务器还将向客户端传送自己的证书。
- 客户利用服务器传过来的信息验证服务器的合法性,服务器的合法性包括:证书是否过期,发行服务器证书的 CA 是否可靠,发行者证书的公钥能否正确解开服务器证书的“发行者的数字签名”,服务器证书上的域名是否和服务器的实际域名相匹配。如果合法性验证没有通过,通讯将断开;如果合法性验证通过,将继续进行第四步。
- 用户端随机产生一个用于后面通讯的“对称密码”,然后用服务器的公钥(服务器的公钥从步骤②中的服务器的证书中获得)对其加密,然后将加密后的“预主密码”传给服务器。
- ….
我们可以看到,在第三步,客户端就使用数字证书技术来获取网站的公钥。接下来就商议密钥进行对称加密传输。可以说,总的来说,Https传输是先使用非对称加密验证以及商议密钥,再经过对称加密传输数据的。
当我们使用第三方的API(例如七牛云,腾讯云的人工智能API),第三方为了验证调用是某个用户发起的,会提供一个公钥和一个私钥,要注意,这里的公钥和私钥并不是一一对应的,而是从两对公私钥中,分别取出一对的公钥与另外一对的私钥。
剩下的公私钥由第三方保存。我们有两种方式使用这对公私钥:
客户端调用 我们把公钥放在客户端文件中(js文件,手机客户端),因为公钥是可以公开的,不用担心被窃取。调用的时候通过公钥把信息进行加密发到第三方,第三方用对应的私钥解密。
服务端调用 我们把私钥放在自己的服务端中。然后调用的时候通过私钥把信息进行签名发送给第三方,第三方用对应的公钥解密,如果能成功解密则代表验证成功。
无论是对称加密还是非对称加密,密钥都需要保管好,只要其他人获得密钥,就能破解信息。要注意的是,在非对称加密中,公钥是可以公开的,只需要保管好私钥就好。