接地气的说一说HTTPS

互联网用户都知道访问域名前加https的网站会更安全,至于为什么会更安全,能有多安全,用户也不用关心。但程序员需要关心,毕竟和自己的饭碗有关系,也不要因为不懂HTTPS原理被同行怼的哑口无言。

近几年HTTPS届发生了很多事情,其中有几个决定性的大事件。Chrome直接将普通http网站归为不安全,苹果要求App调用的接口必须为https的,BAT的小程序要求api必须为https的,百度和Google都申明对https的网站收录效果更好等。总之这一年间所有网站都大举搬迁到https。

通过这件事可以看出,科技进步的方向其实掌握在少数几个大公司手中,而这些公司无一例外掌握着互联网的入口。https确实是一项好技术,防止网页被拦截,篡改等。你总不希望一个页面上90%是广告吧。今天咱们就用接地气的方式说一说https到底是如何工作的,大家肯定能听懂(为什么我要说大家和咱们)……

一. 加密与解密

加密解密算法有很多,本文要讲的https用到了两种,对称加密与非对称加密。至于为什么要用两种,用一种难道不行吗?再次提醒md5,sha系列算法不是加密算法。

  1. 对称加密,典型的对称加密算法有AES、DES、3DES等。我们常用3DES对接口做加密,防止信息泄露。这种算法使用一个秘钥实现加密解密,客户端和服务端都要知道这个密钥才能进行数据传输,类似保险箱的密码,只要知道密码的人就能打开。优点就是传输过程安全,很难被破解,加密解密的速度也比较快。缺点是密钥需要直接分享。

  2. 非对称加密,典型的非对称加密算法有RSA、DSA、ECDH等,使用方法为私钥加密,公钥解密。最常用的场合就是ssh登录,git帐号验证。拿ssh登录验证来说,我们在本地通过RSA算法生成一个私钥和公钥,然后将公钥在服务器上保存一份。每次登录时,服务器会向用户发送一段随机字符串,用户用自己的私钥加密后,再发给服务器。服务器用事先储存的公钥进行解密,如果成功,就证明用户是可信的,不用再输入密码。非对称加密很适合用在这种需要验证权限的场合。优点就是传输过程更安全,很难被破解,使用起来更灵活,不用将私钥分享出去。缺点是加密解密很慢。

然后我们再来回答问题,https为什么要用这两种加密算法,用一种难道不行吗?

如果我们只用RSA(非对称加密),会发现加密解密特别耗资源,访问几个网站CPU就吃满了(手机端更明显)。服务器与客户端都会消耗大量计算资源,会有明显的卡顿感,这时候用都没法用就不要谈安全了。有个大概的数据可以参考:RSA加密1G的文件需要1小时,解密需要65小时。所以用非对称加密处理网页数据肯定不实际。

如果我们只用AES(对称加密),会发现速度很快,对于1G的资源加密解密都能在1分钟内完成。但是密钥如何传递到客户端是个问题,总不可能把密钥用明文传递到客户端。

为了解决对称加密需要传递密钥,非对称加密速度慢的问题。最后将两种方案结合了一下,用非对称加密算法传递密钥,用对称加密算法加密解密网页数据。如此一来性能和安全都得到了保证,完美!

二. 如何实施

我们可以大概理一下https的请求流程,看看有没有破绽。

  1. 客户端向服务端发送https请求(包含协议版本号,随机数,客户端浏览器支持的加密方法等)。
  2. 服务端接收到请求后,利用非对称加密算法生成私钥与公钥,并将公钥传给客户端(也会发送一个随机数)。
  3. 客户端生成一个随机数key,并用拿到的公钥加密随机数得到a,然后将a传给服务端。
  4. 服务端拿到a后,通过私钥解密得到随机数key。
  5. 这样客户端和服务端都有了对称加密的密钥key,就能进行通信了。
  6. 客户端将数据使用k进行加密(k是前面三个随机数生成的对话密钥),然后传递给服务端。
  7. 服务端拿到数据后通过k解密数据,得到原始信息。

这个流程看着很完美!!但是如果在第2步,服务端发送给客户端的信息被第三方截取了会怎样呢?

  1. 黑客拿到服务端发送给客户端的公钥,然后自己生成一个私钥和公钥,将公钥传递给客户端。
  2. 客户端生成一个随机数key,并用拿到的公钥加密随机数得到a,然后将a传给黑客。
  3. 黑客拿到a后,通过私钥解密得到随机数key,并用之前拿到的公钥加密随机数key得到b,然后将b传给服务端。

后面就不用说了,黑客已经拿到所有信息。请求被劫持后我们整个流程完全没用。那如何保证我们的流程在第2步不被黑客拦截并且破译呢?

出问题的地方是从第二步开始,所以我们必须在第二步验证服务端的身份。而https采用的方式是利用第三方颁发的证书。在第二步的时候我们将公钥和证书信息一起发送给客户端(证书的签名信息中包含服务端的公钥)。如果客户端能识别证书的真伪,其实整个链路就通了。下面说说客户端是如何识别证书的真伪的。

客户端浏览器先获取到服务端返回的证书,浏览器内置的根证书(CA机构的公钥)自带公钥,可以来解密服务端发过来的证书上的签名,然后对比一下解密后的信息和服务端证书中所呈现的信息是否一致,如果一致就通过。解密后的信息有服务端的公钥,域名,机构,过期时间等等。通过浏览器的根证书可以保证证书是真的。

假设,黑客又劫持了第二步服务端发给客户端的密钥和证书,然后自己使用一个真实的证书和密钥来替换行不行呢?肯定是不行的,因为当前域名和证书上的域名不一致就肯定是假的。

最后说说如何保证数据传输的完整性,如果我们的数据在传输的过程中被串改了,服务端拿到key之后,会直接用key去解码被串改的数据,解码出来的肯定是一串垃圾信息,但是计算机并不知道这是垃圾信息,会当作正常信息去处理,这里可能会存在安全隐患。为了保证算法的完整性,客户端会通过散列算法计算一个散列值。常见的散列算法有md5,sha1,sha256等等,这里HTTPS使用的散列算法为MAC(通常为密钥+sha256)。最后的验证流程会变成,客户端先计算明文的 MAC 值,然后将明文和 MAC 值同时用key加密。服务端拿到加密信息后先解密,然后对解密后的信息计算出MAC值,如果与客户端传递的MAC值一致才进行后续操作。

三. 总结

https使用了很多算法,包括加密、解密、散列等。随着计算机计算能力的加强,很多之前不可破解的算法都开始被攻破,所以我们在服务端可以自己选择使用哪种算法来规避安全风险。例如常用的openssl,就有提供如下算法组合方案,可通过命令openssl ciphers -V | column -t 查看。例如:

1
2
3
4
0xC0,0x30  -  ECDHE-RSA-AES256-GCM-SHA384    TLSv1.2  Kx=ECDH        Au=RSA    Enc=AESGCM(256)    Mac=AEAD
0xC0,0x2C - ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD
0xC0,0x28 - ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA384
0xC0,0x24 - ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA384

比如第一条ECDHE-RSA-AES256-GCM-SHA384 表示使用ECDH 做密钥协商、RSA 做证书验证,AES256做对称加密,SHA384做Mac散列算法。

为啥又多出一个ECDH呢?我们在第三步的时候 “客户端生成一个随机数key,并用拿到的公钥加密随机数得到a,然后将a传给服务端。” 如果a被破解了整个协商过程又白费了,虽然理论上可以保证a不被破解。但为了安全,我们使用ECDH算法来协商密钥而不用传递他,ECDH算法可以在不共享任何秘密的情况下协商出一个密钥。这里的RSA只做证书的签名认证。

为了实现https的加密传输,我们做了很多努力。但还是有一个安全问题,如果证书颁发机构和黑暗势力同流合污的话整套机制将毫无防御能力。

最后推荐一个关于https的网站 http://www.ssleye.com/