2.7 HTTPS

2.7 HTTPS #

2.7.1 什么是 https #

如果通信过程具备了四个特性,就可以认为是 “安全” 的,这四个特性是:机密性、完整性,身份认证和不可否认。HTTPS 为 HTTP 增加了这四大安全特性。

机密性由对称加密 AES 保证,完整性由 SHA384 摘要算法保证,身份认证和不可否认由RSA非对称加密保证

HTTPS 规定了新的协议名 “https”,默认端口号 443,至于其他的什么请求 - 应答模式、报文结构、请求方法、URI、头字段、连接管理等等都完全沿用 HTTP,没有任何新的东西。也就是说,除了协议名 “http” 和端口号 80 这两点不同,HTTPS 协议在语法、语义上和 HTTP 完全一样,优缺点也 “照单全收”(当然要除去 “明文” 和 “不安全”)。

HTTPS 把 HTTP 下层的传输协议由 TCP/IP 换成了 SSL/TLS,由 “HTTP over TCP/IP” 变成了 “HTTP over SSL/TLS”,让 HTTP 运行在了安全的 SSL/TLS 协议上,收发报文不再使用 Socket API,而是调用专门的安全接口。

2.7.2 SSL/TLS #

SSL 即安全套接层(Secure Sockets Layer),在 OSI 模型中处于第 5 层(会话层),由网景公司于 1994 年发明,有 v2 和 v3 两个版本,而 v1 因为有严重的缺陷从未公开过。SSL 发展到 v3 时已经证明了它自身是一个非常好的安全通信协议,于是互联网工程组 IETF 在 1999 年把它改名为 TLS(传输层安全,Transport Layer Security),正式标准化,版本号从 1.0 重新算起,所以 TLS1.0 实际上就是 SSLv3.1。

TLS 由记录协议、握手协议、警告协议、变更密码规范协议、扩展协议等几个子协议组成,综合使用了对称加密、非对称加密、身份认证等许多密码学前沿技术。

浏览器和服务器在使用 TLS 建立连接时需要选择一组恰当的加密算法来实现安全通信,这些算法的组合被称为 “密码套件”(cipher suite,也叫加密套件)。

TLS 的密码套件命名非常规范,格式很固定。基本的形式是 “密钥交换算法 + 签名算法 + 对称加密算法 + 摘要算法”。

2.7.3 OpenSSL #

OpenSSL 是一个著名的开源密码学程序库和工具包,是 SSL/TLS 的具体实现,几乎支持所有公开的加密算法和协议,已经成为了事实上的标准,许多应用软件都会使用它作为底层库来实现 TLS 功能,包括常用的 Web 服务器 Apache、Nginx 等。

2.7.4 对称加密 #

“对称加密” 就是指加密和解密时使用的密钥都是同一个,是 “对称” 的。只要保证了密钥的安全,那整个通信过程就可以说具有了机密性。

TLS 里有非常多的对称加密算法可供选择,比如 RC4、DES、3DES、AES、ChaCha20 等,但前三种算法都被认为是不安全的,通常都禁止使用,目前常用的只有 AES 和 ChaCha20。

AES 的意思是 “高级加密标准”(Advanced Encryption Standard),密钥长度可以是 128、192 或 256。它是 DES 算法的替代者,安全强度很高,性能也很好,而且有的硬件还会做特殊优化,所以非常流行,是应用最广泛的对称加密算法。

ChaCha20 是 Google 设计的另一种加密算法,密钥长度固定为 256 位,纯软件运行性能要超过 AES,曾经在移动客户端上比较流行,但 ARMv8 之后也加入了 AES 硬件优化,所以现在不再具有明显的优势,但仍然算得上是一个不错的算法。

加密分组模式 #

对称算法有一个 “分组模式” 的概念,它可以让算法用固定长度的密钥加密任意长度的明文。这是因为,明文的长度不固定,而密钥一次只能处理特定长度的一块数据,这就需要进行迭代,以便将一段很长的明文全部加密,而迭代的方法就是分组模式。

最新的分组模式被称为 AEAD(Authenticated Encryption with Associated Data),在加密的同时增加了认证的功能,常用的是 GCM、CCM 和 Poly1305。

综上,就可以得到 TLS 密码套件中定义的对称加密算法。比如,AES128-GCM,意思是密钥长度为 128 位的 AES 算法,使用的分组模式是 GCM;ChaCha20-Poly1305 的意思是 ChaCha20 算法,使用的分组模式是 Poly1305。

2.7.5 非对称加密 #

对称加密存在一个很大的问题:如何把密钥安全地传递给对方,术语叫 “密钥交换”。

因为在对称加密算法中只要持有密钥就可以解密。如果密钥被黑客窃取,那他就可以在之后随意解密收发的数据,通信过程也就没有机密性可言。

非对称加密(也叫公钥加密算法)可以解决对称加密的秘钥传递问题。

“密钥” 约定俗成的度量单位是 “位”(bit),而不是 “字节”(byte)。比如,说密钥长度是 128,就是 16 字节的二进制串,密钥长度 1024,就是 128 字节的二进制串。

非对称加密有两个密钥,一个叫 “公钥”(public key),一个叫 “私钥”(private key)。两个密钥是不同的,“不对称”,公钥可以公开给任何人使用,而私钥必须严格保密。

公钥和私钥虽然都可以用来加密解密,但公钥加密后只能用私钥解密,反过来,私钥加密后也只能用公钥解密。

非对称加密完美解决了 “密钥交换” 的问题。网站秘密保管私钥,在网上任意分发公钥,你想要登录网站只要用公钥加密就行了,密文只能由私钥持有者才能解密。而黑客因为没有私钥,所以就无法破解密文。

非对称加密算法的设计要比对称算法难得多,在 TLS 里只有很少的几种,比如 DH、DSA、RSA、ECC 等。

RSA 是其中最著名的一个,几乎可以说是非对称加密的代名词,它的安全性基于 “整数分解” 的数学难题,使用两个超大素数的乘积作为生成密钥的材料,想要从公钥推算出私钥是非常困难的。

ECC(Elliptic Curve Cryptography)是非对称加密里的 “后起之秀”,它基于 “椭圆曲线离散对数” 的数学难题,使用特定的曲线方程和基点生成公钥和私钥,子算法 ECDHE 用于密钥交换,ECDSA 用于数字签名。目前比较常用的两个曲线是 P-256(secp256r1,在 OpenSSL 称为 prime256v1)和 x25519。P-256 是 NIST(美国国家标准技术研究所)和 NSA(美国国家安全局)推荐使用的曲线,而 x25519 被认为是最安全、最快速的曲线。

比起 RSA,ECC 在安全强度和性能上都有明显的优势。160 位的 ECC 相当于 1024 位的 RSA,而 224 位的 ECC 则相当于 2048 位的 RSA。因为密钥短,所以相应的计算量、消耗的内存和带宽也就少,加密解密的性能就上去了,对于现在的移动互联网非常有吸引力。

常见问题 #

  • 非对称加密为什么慢,非对称加密除了慢外还有什么缺点

非对称加密基于大数运算,比如大素数或者椭圆曲线,是复杂的数学难题,所以消耗计算量,运算速度慢。

除了慢,可能还有一个缺点就是需要更多的位数,相同强度的对称密钥要比非对称密钥短。

对称密钥一般都 128 位、256 位,而rsa 一般要 2048 位,不过椭圆曲线的会短一点。

2.7.6 混合加密 #

虽然非对称加密没有 “密钥交换” 的问题,但因为它们都是基于复杂的数学难题,运算速度很慢,即使是 ECC 也要比 AES 差上好几个数量级。如果仅用非对称加密,虽然保证了安全,但通信速度慢,实用性太差。

现在 TLS 里使用的混合加密方式。在通信刚开始的时候使用非对称算法,比如 RSA、ECDHE,首先解决密钥交换的问题。然后用随机数产生对称算法使用的 “会话密钥”(session key),再用公钥加密。因为会话密钥很短,通常只有 16 字节或 32 字节,所以慢一点也无所谓。对方拿到密文后用私钥解密,取出会话密钥。这样,双方就实现了对称密钥的安全交换,后续就不再使用非对称加密,全都使用对称加密。

简单来说就是:通信双方通过非对称加密协商出一个用于对称加密的密钥

利用混合加密就解决了对称加密算法的密钥交换问题,而且安全和性能兼顾。

2.7.7 摘要算法 #

摘要算法(Digest Algorithm),也就是常说的散列函数、哈希函数(Hash Function)。可以把摘要算法近似地理解成一种特殊的压缩算法,它能够把任意长度的数据 “压缩” 成固定长度、而且独一无二的 “摘要” 字符串,就好像是给这段数据生成了一个数字 “指纹”。被摘要算法 “加密” 后的数据无法解密,不能从摘要逆推出原文。

摘要算法实际上是把数据从一个 “大空间” 映射到了 “小空间”,所以就存在 “冲突”(collision,也叫碰撞)的可能性,可能会有两份不同的原文对应相同的摘要。好的摘要算法必须能够 “抵抗冲突”,让这种可能性尽量地小。

日常工作中的 MD5(Message-Digest 5)、SHA-1(Secure Hash Algorithm 1)就是最常用的两个摘要算法,能够生成 16 字节和 20 字节长度的数字摘要。但这两个算法的安全强度比较低,不够安全。

而 SHA-1 的后继者 SHA-2 实际上是一系列摘要算法的统称,总共有 6 种,常用的有 SHA224、SHA256、SHA384,分别能够生成 28 字节、32 字节、48 字节的摘要。

2.7.8 完整性 #

摘要算法保证了 “数字摘要” 和原文是完全等价的。所以,我们只要在原文后附上它的摘要,就能够保证数据的完整性。

比如,你发了条消息:“转账 1000 元”,然后再加上一个 SHA-2 的摘要。网站收到后也计算一下消息的摘要,把这两份 “指纹” 做个对比,如果一致,就说明消息是完整可信的,没有被修改。如果黑客在中间哪怕改动了一个标点符号,摘要也会完全不同,网站计算比对就会发现消息被窜改,是不可信的。

摘要算法不具有机密性,如果明文传输,那么黑客可以修改消息后把摘要也一起改了,网站还是鉴别不出完整性。

真正的完整性必须要建立在机密性之上,在混合加密系统里用会话密钥加密消息和摘要,这样黑客无法得知明文,也就没有办法动手脚了。这有个术语,叫哈希消息认证码(HMAC)。

2.7.9 数字签名 #

通信过程通过加密算法结合摘要算法可以说是比较安全了。但是还有漏洞,就是通信的两个端点(endpoint)。比如,黑客可以伪装成网站来窃取信息。而反过来,他也可以伪装成你,向网站发送支付、转账等消息,网站没有办法确认你的身份,钱可能就这么被偷走了。

在 TLS 中,解决身份验证的问题可以通过非对称加密里的 “私钥”,使用私钥再加上摘要算法,就能够实现 “数字签名”,同时实现 “身份认证” 和 “不可否认”。

数字签名的原理很简单,就是把公钥私钥的用法反过来,之前是公钥加密、私钥解密,现在是私钥加密、公钥解密。但又因为非对称加密效率太低,所以私钥只加密原文的摘要,这样运算量就小的多,而且得到的数字签名也很小,方便保管和传输。签名和公钥一样完全公开,任何人都可以获取。但这个签名只有用私钥对应的公钥才能解开,拿到摘要后,再比对原文验证完整性,就可以像签署文件一样证明消息确实是你发的。这两个行为也有专用术语,叫做 “签名” 和 “验签”。

只要你和网站互相交换公钥,就可以用 “签名” 和 “验签” 来确认消息的真实性,因为私钥保密,黑客不能伪造签名,就能够保证通信双方的身份。

比如,你用自己的私钥签名一个消息 “我是小明”。网站收到后用你的公钥验签,确认身份没问题,于是也用它的私钥签名消息 “我是吴彦祖”,你收到后再用它的公钥验一下,也没问题,这样你和网站就都知道对方不是假冒的,后面就可以用混合加密进行安全通信了。

数字签名是私钥对摘要的加密,可以由公钥解密后验证,实现身份认证和不可否认。

2.7.10 数字证书和 CA #

因为谁都可以发布公钥,所以,怎么来判断这个公钥就是你或者某宝的公钥呢?这就需要 CA(Certificate Authority,证书认证机构),它就像网络世界里的公安局、教育部、公证中心,具有极高的可信度,由它来给各个公钥签名,用自身的信誉来保证公钥无法伪造,是可信的。

CA 对公钥的签名认证有特定的格式,包含序列号、用途、颁发者、有效时间等等,把这些打成一个包再签名,完整地证明公钥关联的各种信息,形成 “数字证书”(Certificate)。知名的 CA 有 DigiCert、VeriSign、Entrust、Let’s Encrypt 等,它们签发的证书分 DV、OV、EV 三种,区别在于可信程度

DV 是最低的,只是域名级别的可信,背后是谁不知道。EV 是最高的,经过了法律和审计的严格核查,可以证明网站拥有者的身份(在浏览器地址栏会显示出公司的名字,例如 Apple、GitHub 的网站)。小一点的 CA 可以让大 CA 签名认证,但链条的最后,也就是 Root CA,就只能自己证明自己了,这个就叫 “自签名证书”(Self-Signed Certificate)或者 “根证书”(Root Certificate)。你必须相信,否则整个证书信任链就走不下去了。

操作系统和浏览器都内置了各大 CA 的根证书,上网的时候只要服务器发过来它的证书,就可以验证证书里的签名,顺着证书链(Certificate Chain)一层层地验证,直到找到根证书,就能够确定证书是可信的,从而里面的公钥也是可信的。

常见问题 #

  1. 假设有一个三级的证书体系(Root CA=> 一级 CA=> 二级 CA),你能详细解释一下证书信任链的验证过程吗?注服务器的证书由二级 CA 签发。

TLS 协商阶段,在交换完 Client Hello/Server Hello 消息后,发送方【通常是服务器】,发送 Certificate 消息,把证书链,包括自己的证书,二级CA证书,一级CA证书,一次性发送给接收方【通常是浏览器】。

每个传递过来的证书包括 4 部分

  • signedCertificate 签名的证书,即浏览器点击小锁头直观可以看到的证书;

  • algorithmIdentifier 算法标记,包括了签名证书用到的摘要和签名算法,如 sha256WithRSAEncryption;

  • Padding 填充字符;

  • encrpted 加密摘要,加密摘要不包含在 signedCertificate 中,所以浏览器中点击小锁头看不到加密摘要。

当前接收方只有内置的 Root Ca 根证书,无法直接信任接收方的证书。接收方将通过证书链中包含的签发者信息,逐层向上查找直到 Root Ca 根证书,并从根证书开始,逐级向下做验签。

首先,用根证书对一级证书做验签。具体过程是,对一级 CA 证书【signedCertificate】用传递过来的摘要算法【algorithmIdentifier】做摘要得到摘要1;用 Root Ca 根证书的公钥解密一级 CA 证书的数字签名【encrpted】,得到发送过来的摘要2,二者比较,如一致,则认为一级CA证书是真实有效的。类似的,继续用一级 CA 证书对二级 CA 证书做验签,二级 CA 证书对发送方证书做验签,如果发送方证书验证通过,则随之 TLS 协商进入 Server key exchange 阶段。

参考 #