Encrypt and Security

上周有同学分享了一下加密解密,感觉自己学到了一些东西,记录一下。

加密 & 解密

有时候面试的时候会遇到有人讲自己搞过的一些加密方法,会提到「对于 xx 我们使用 md5 加密」,md5 是一种加密方法么?我觉得不是。所谓加密,是对应解密的,不能还原原文的方法,就不应该算加密了吧。

常常可以听到的,有对称加密和非对称加密。对称加密是指加密解密使用的是同一个密钥的方法,非对称的是指使用不同的密钥的方法。

对称密钥加密的方法有 DES、3DES、AES、Blowfish、IDEA、RC5、RC6。我之前用过其中的 3DES 和 Blowfish。特点是速度快。

非对称加密也叫公开密钥加密,方法有 RSA、ElGamal、背包算法、Rabin(RSA的特例)、迪菲-赫尔曼密钥交换协议中的公钥加密算法、椭圆曲线加密算法(英语:Elliptic Curve Cryptography, ECC)。基本都没怎么用过。。他的特点是其中一个公钥可以公开,公钥加密的信息只能通过私钥解密,只要保证私钥安全就可以。但是这个方法加解密速度较慢。

分组模式

加密算法并不能说一次给多少数据都可以一次加密完毕,实际处理的时候是按块处理的,先按照一定逻辑分组。

分组模式有 ECB, CBC, CFB, OFB, CTR等。ECB 就是最简单的,分组之后每块分别加密,然后组合起来。这个方法导致可以任意替换各组的位置而不影响解密。比如原来加密是「A 欠 B 1 万」,每个字刚好一个分组,那即使在不能解密的情况下,也可能导致收到的消息是 「B 欠 A 1 万」。所以就有了其它的那几个加密分组模式,其它的几个模式都会和前后的数据有一定的关系,这样就不能这样调换了。对于第一组数据,加密的时候会需要一个初始化的数据,就是 IV。

散列算法

也就是 hash 算法。常见的 md5, sha1, sha256, sha512 等。这种是对内容产生一个摘要,是一个有损的过程,根据摘要值,不能得到原文。但是可以通过比对摘要来确认摘要是不是来自相同的内容(有一定的碰撞几率)。

https

https 是指基于 tls/ssl 协议基础上面跑的 http 协议。建立 ssl 握手之后,就是基于 http 协议的逻辑来在这个 ssl 通道上面传输了。

握手验证过程中,有两个证书需要用到,一个是 CA Root certificate,还有一个是经过 CA 签名的 Web server certificate。CA 根证书是权威的 CA 机构给到各个浏览器和操作系统内置的一个公钥。Web 服务器证书是权威 CA 机构验证了网站的身份之后,使用 CA 的私钥对证书签名,签名就是根据证书内容先进行 Hash,然后使用私钥加密,把结果作为签名一起放到证书里面。

Client 访问服务器,会收到服务器返回的 Web 服务器证书,浏览器会根据 CA 根证书对这个证书的签名进行验证,因为那个跟证书其实就是一个公钥,所以可以使用他解开前面加密的签名,然后也使用相同的 Hash 算法把证书内容 hash,比对结果看是否一致,这个会在后面说数字签名的时候再讲。另外,浏览器也会检查比如有效期,域名等,没问题就认为证书有效。然后浏览器会使用证书里面的公钥,继续和 Web 服务器做握手,浏览器会产生一个随机数,用公钥加密然后返回给 Web 服务器。后续双方会使用这个随机数作为密钥,使用一种对称加密方法做后续的加密。

整个过程中,前面使用非对称加密,协商好一个密钥,后面使用这个密钥来使用对称加密来加密后续的大量的内容。

Client hello

CLient hello 消息包括

  • TLS 版本
  • 随机数,时间戳
  • Session Id,用来减少握手次数
  • 支持的加密方式 Cipher Suites
  • Server name

Server hello

Server hello 消息包括

  • TLS 版本,表示同意
  • 时间戳
  • Session Id
  • Cipher Suites
  • Web 服务器证书

验证 Web 服务器证书

  • Not Before Time
  • Not After Time
  • Common Name

验证权威 CA 机构的签名

Pre-Master Secret

数字签名

比如 A 向 B 借了 100 块钱,这个时候 B 就需要 A 给打一个欠条。一方面,如何保证借条可以安全传输,另一方面,如何保证 A 将来不赖掉这个借条呢?可以使用前面提到的私钥公钥的方法来解决。

首先 A 把自己的公钥公开,然后把借条内容做一个 Hash,把 hash 结果使用自己的私钥来加密得到一个签名,同时把借条内容和签名给 B。B 拿到借条内容和签名之后,可以用那个公钥解密签名,然后对借条内容 Hash 得到一个 hash 值,比对这两个结果看是否一致。这个过程就完成了。

如果借条或者是签名在传输过程中被串改,那么 B 就能发现内容的 hash 值和签名解密之后的值不一致。这样就保证了安全。(当然,这里还有个问题是如何保证 B 拿到的公钥是对的,这个感觉肯能是通过其他途径保证的。)

因为公钥是公开的,任何一个第三方都可以做这个验证,可以验证一致就表示是 A 自己用私钥加密的。这个就保证了 A 无法赖掉这个借条。(我们讨论过,如果公钥不公开,并且没有第三方对这个公钥做了公证,那么是无法保证 A 赖账的。)

在这个过程里面,我们把借条内容先做一个 Hash,拿到一个 hash 值,然后再加密,这个结果就是一个数字签名了。任何人拿到了公钥,都可以将签名解密,然后可以对借条做相同的 Hash,比对解密出来的值是否等同于那个 hash 值。

前面 https 里面提到的 CA 权威机构对证书签名,其实就是类似的事。