传统的HTTP方式在网络传输时,传输数据都是明文的,很容易出现数据被监听和窃取的情况:
另外,传输的数据还有可能被一些别有用心的人篡改,导致浏览器与服务器之间收发的内容不一致:
也就是说,使用HTTP明文传输至少存在着 数据被监听 以及 数据被篡改 这两大风险,因此HTTP是一种不安全的协议。
既然HTTP明文传输是一种不安全的协议,那么显然就需要在网络传输过程中对数据进行加密处理。
而加密的方式有两种:对称密钥加密 和 非对称密钥加密。
对称加密是加密与解密使用相同的密钥;非对称加密则是加密与解密使用不同的密钥(公钥、私钥)。
对称加密的优点是加解密效率高,而在网络传输中是非常讲究效率的,因此很明显应该优先选择使用对称加密:
由于在网络中传输的数据都是密文,所以不怕被监听者获取到,因为通信的第三方在没有密钥的情况是无法解密的。
然而,这种机制存在的一个致命问题是:在服务器与浏览器首次通信进行密钥协商时,使用的一定是明文,只有在确定使用的密钥后才能进行加密通信,这个漏洞在只使用对称加密的情况是永远无法解决的,因此需要引入非对称加密。
非对称加密的优点是安全性更高,缺点是加解密效率相比于对称加密的方式会差很多。因此,引入非对称加密的唯一目的就是保证通信双方在进行对称加密的密钥交换阶段的安全性,在密钥交换完毕后,通信双方就会开始使用对称加密进行数据交换,而不会再使用非对称加密。
可以看到,浏览器通过使用服务器发布的非对称加密的公钥,将对称加密的密钥A进行加密,通过网络传输到服务器,服务器再使用私钥解密,从而获得对称密钥A,这段报文即使被监听者窃取也无法解密,因为只有拥有私钥的服务器才能够解密。
然而,这种机制下同样存在一个关键问题:浏览器如何安全的获得服务器的公钥?
如果浏览器使用明文的方式向服务器请求公钥,则可能出现服务器公钥被其他人篡改的可能。
也就是说,用户在网络上获取任何网站的公钥时,都有公钥被第三方篡改的风险,而一旦使用被篡改的假公钥来对数据进行加密,就有可能被第三方以假的私钥进行解密,导致用户信息泄露。
为此,为了解决服务器公钥传输安全性的问题,引入了CA机构,用于专门为各个网站签发数字证书,从而保证浏览器可以安全的获得各个网站的公钥。
前文已经说到,为了解决服务器公钥有可能被伪造或篡改的问题,而引入了数字证书。
数字证书由CA机构(Certificate Authority)签发,CA机构是PKI体系(Public Key Infrastructure,公钥基础设施)的核心。
如果用户想要得到一份属于自己的数字证书,首先需要向CA提出申请。在CA判明申请者的身份后,便为它分配一个公钥,并且CA将该公钥与申请者的身份信息(例如域名)绑定在一起,设定证书有效期,并为之签名后, 形成证书发给申请者。
CA机构本身同样拥有一对公钥和私钥,它会用自己的私钥给数字证书签名后发送给申请者,而CA机构数量有限,任何正版操作都会将所有主流CA机构的公钥内置在操作系统当中,因此无需从网络中获取CA机构的公钥。
在验证证书时只需遍历操作系统中所有内置的CA机构的公钥,只要有任何一个公钥能够正常解密出数据,就说明证书是合法的。
然后,当浏览器向服务器发送请求时,服务器会先将自己的证书返回给浏览器,浏览器通过所在的操作系统的内置CA公钥进行解密,如果可以成功解密,并且获得的服务器信息(例如域名)与浏览器所请求的服务器信息一致,则说明这个证书是可信的,从而在证书中获得了服务器的公钥。
为了不影响主流程的连贯性,前面只是说了关于加密的主要概念,下面对一些实现细节详细说明:
HTTPS为了保证数据传输的安全,使用的是对称加密和非对称加密结合的方式:
使用非对称加密进行对称加密的密钥协商,之后就使用对称加密进行数据传输。
HTTPS在数据传输阶段并没有使用非对称加密,主要原因是:
① 对称加密由于实现更简单,所以加解密的效率更高;
② 非对称加密在某一个传输方向的数据是不安全的:由于服务器的公钥是公之于众的,网络上的所有结点都可以获得服务器公钥,也就可以对服务器发给客户端的信息进行正确解密,例如:
"客户端" -> "服务器": { 我的账号是aaa, 密码是123,请把我的账户余额发给我看看 }(使用服务器公钥加密)
//这条消息只能通过服务器的私钥进行解密,是安全的
"服务器" -> "客户端": { 你的账户余额是:0.1 元 } (使用私钥加密)
//这条服务器返回的消息,任何人都可以使用服务器的公钥进行解密,它是不安全的
① 客户端通过非对称加密算法的掩护,安全的和服务器商量好一个对称加密算法和密钥来保证后面通信过程内容的安全;
② 因为私钥只有服务器拥有,因此客户端可以通过对方是否有私钥来判断对方是否就是服务器。
对一份数据进行一次hash计算,得到的hash值就是这份数据的 摘要,也称为 指纹。
在非对称加密中,如果使用公钥对数据加密,用私钥去解密,这是 加密; 反之,如果用私钥对数据加密,用公钥去解密,这是 签名。
两者的用途完全不同。由于所有人都持有公钥,所以 “签名” 并不能保证数据的安全性,因为所有人都可以用公钥去解密。但 签名 却能用于保证消息的准确性和不可否认性。
因为公钥和私钥是一一对应的,所以当一个公钥能解密某个密文时,说明这个密文一定来自于私钥持有者,从而证明了消息发送者身份的真实性。
但又因为非对称加密效率太低,所以 私钥只加密原文的摘要,这样运算量就小的多,而且得到的数字签名也很小,方便保管和传输。
签名和公钥一样完全公开,任何人都可以获取。但这个签名只有用私钥对应的公钥才能解开,拿到摘要后,再比对原文验证完整性,就可以像签署文件一样证明消息确实是你发的。
这两个行为有专业术语,叫 “签名” 和 “验签”。
① 证明数据发送者身份真实可靠;
② 验证接收到的数据未被篡改过。
(来自真正的服务器,且数据完整原始)
常见的摘要算法有 MD5、SHA-1、SHA-256。
作为摘要算法,必须有以下特点:
(1)对于任意长度的输入,通过摘要算法的计算后,得出的值必须为 固定长度;(例如MD5,无论输入数据多大,输出总是128位的散列值)
(2)摘要算法是 不可逆 的(单向的),只能根据原始数据计算出摘要值,不能根据摘要值反推出原始数据。
以 www.baidu.com 为例,点击浏览器地址栏左侧的小锁标志,可以查看到百度的数字证书(Google浏览器):
可以看到证书的三层关系:
end-user
: 即 “baidu.com”,该证书包含百度的公钥,访问者就是使用该公钥将数据加密后再传输给百度,即 在HTTPS中使用的证书;intermediates
:证书的签发人(Issuer),用来认证公钥持有者身份的证书,负责确认HTTPS使用的 end-user 证书确实是来源于百度。这类 intermediates 证书可以有很多级,也就是说 签发人 Issuer 可能会有很多级;root
:可以理解为 最高级别的签发人 Issuer,负责认证 intermediates身份的合法性。这其实代表了一个信任链条,最终的目的就是为了保证 end-user 证书是可信的,该证书的公钥也是可信的。
参考内容:
《写一篇最好懂的HTTPS讲解》
《公钥,私钥和数字证书关系》
《数字证书原理,公钥私钥加密原理》
《数字签名算法MD5和SHA-1的比较》
《摘要、签名与数字证书都是什么?》
《关于证书链的一点认知》
《HTTP权威指南》
《Nginx核心知识100讲》