HTTPS(二):HTTPS原理-内容加密

96
XDLee
0.8 2016.04.29 02:23* 字数 858

HTTPS是如何做到『内容加密』、『身份认证』、『内容完整性』的?

下面从原理角度看一下

HTTPS 原理介绍之内容加密

一、加密算法简介

1> 加密算法一般分为两种: '对称加密' 和 '非对称加密'。
2> '对称加密': 也叫'密钥加密',就是指加密和解密使用的是相同的密钥。
3> '非对称加密': 也叫'公钥加密',就是指加密和解密使用的是不同的密钥。
对称加密.png
非对称加密.png

对称加密特点

1> 对称加密强度非常高,一般破解不了。
2> 但存在一个很大的问题就是'无法安全地生成和保管密钥'。
3> 假如客户端和服务器之间每次会话都使用固定的、相同的密钥加密和解密,肯定存在很大的安全隐患。
4> 如果有人从客户端获取到了对称密钥,整个内容就不存在安全性了,而且管理海量的客户端密钥也是一件很复杂的事情。

非对称加密特点

1> 非对称加密主要用于密钥交换(也叫密钥协商),能够很好地解决这个问题。
2> 客户端和服务器每次新建会话时都使用非对称密钥交换算法协商出对称密钥,使用这些对称密钥完成应用数据的加解密和验证,整个会话过程中的密钥只在内存中生成和保存,而且每个会话的对称密钥都不相同(除非会话复用),中间者无法窃取。
3> 非对称密钥交换很安全,但同时也是 HTTPS 性能和速度严重降低的“罪魁祸首”。想要知道 HTTPS 为什么影响速度,为什么消耗资源,就一定要理解非对称密钥交换的整个过程。

下面重点介绍一下非对称密钥交换的数学原理以及在 TLS 握手过程中的应用。

二、 非对称密钥交换

简介
1> 在非对称密钥交换算法出现以前,对称加密一个很大的问题就是不知道如何安全生成和保管密钥。
2> 非对称密钥交换过程主要就是为了解决这个问题,使得对称密钥的生成和使用更加安全。
3> 密钥交换算法本身非常复杂,密钥交换过程涉及到随机数生成,模指数运算,空白补齐,加密,签名等操作。

常见的密钥交换算法有: RSA、ECDHE、DH、DHE等算法,它们的特性如下:

RSA
1> 算法实现简单,诞生与1977年,历史悠久,经过了长时间的破解测试,安全性高。
2> 缺点就是需要比较大的素数(目前常用的是2048位)来保证安全强度,很消耗CPU运算资源。
3> RSA是目前'唯一一个'既能用于'密钥交换'又能用于'证书签名'的算法。
DH
Diffie-Hellman 密钥交换算法,诞生事件比较早(1977年),但是1999年才公开,缺点是比较消耗CPU性能。
Diffie-Hellman 表示的是两位美国计算机学家: Whitfield Diffie 和 Martin Hellman。
ECDHE
1> 使用椭圆曲线(ECC)的DH算法。
2> 优点: 能用较小的素数(256位)实现RSA相同的安全等级。
3> 缺点: 算法实现复杂,用于密钥交换的历史不长,没有经过长时间的安全攻击测试。
ECDH
不支持PFS,安全性低,同时无法实现 false start
'PFS'(perfect forward secrecy),中文可叫做'完全前向保密'。
1> 要求一个密钥只能访问由它所保护的数据;
2> 用来产生密钥的元素一次一换,不能再产生其它的密钥;
3> 一个密钥被破解,并不影响其他密钥的安全性。
TLS False Start 的功能
用来对 HTTPS 网站进行加速的,它是通过 '减少' 客户端和服务器之间的'通信往返RT(Round Trip)'来实现的。
DHE
不支持ECC。非常消耗CPU资源。

建议优先支持 RSA 和 ECDHE_RSA 密钥交换算法。原因如下:

1> ECDHE 支持 ECC 加速,计算速度更快。支持 PFS,更加安全。支持 false start,用户访问速度更快。
2> 目前还有至少 20% 以上的客户端不支持 ECDHE,我们推荐使用 RSA 而不是 DH 或者 DHE,因为 DH 系列算法非常消耗 CPU(相当于要做两次 RSA 计算)。
查看ECDHE_RSA用在哪里.png

需要注意:
通常所说的 ECDHE 密钥交换默认都是指 ECDHE_RSA,使用 ECDHE 生成 DH 算法所需的公私钥,然后使用 RSA 算法进行签名,最后再计算得出对称密钥。

非对称加密相比对称加密更加安全,但也存在两个明显缺点
1> CPU 计算资源消耗非常大。一次完全 TLS 握手,密钥交换时的非对称解密计算量占整个握手过程的 90% 以上。而对称加密的计算量只相当于非对称加密的 0.1%,如果应用层数据也使用非对称加解密,性能开销太大,无法承受。
2> 非对称加密算法对加密内容的长度有限制,不能超过公钥长度。比如现在常用的公钥长度是 2048 位,意味着待加密内容不能超过 256 个字节。
3> 所以公钥加密目前只能用来作'密钥交换'或者'内容签名'。
4> '不适合用来做应用层传输内容的加解密'。
5> 非对称密钥交换算法是整个 HTTPS 得以安全的基石,充分理解非对称密钥交换算法是理解 HTTPS 协议和功能的关键。
2.1 RSA 密钥协商
2.1.1 RSA 算法介绍
1. RSA 算法的安全性是建立在乘法不可逆或者大数因子很难分解的基础上。
2. RSA 的推导和实现涉及到了'欧拉函数'和'费马定理'及'模反元素'的概念,有兴趣可以自行百度。
3. RSA 算法是'统治世界'的最重要算法之一,而且从目前来看,RSA 也是 'HTTPS 体系中最重要的算法',没有之一。
数学知识
1. 互质关系
  1> 如果两个正整数,除了1之外,没有其它公因子,我们就称这两个数是'互质关系'。
  2> 比如: 11 和 30 没有除1之外的公因子,所以它们就是互质关系。这也说明,不是质数也可以构成互质关系。
  3> 如果一个正整数,它的因子除了1就是它本身,这个正整数就是'质数'。
2. 欧拉函数:
  1> 任意给定一个正整数 n,请问在小于等于 n 的正整数之中,有多少个数与 n 构成'互质'关系?
  2> 计算这个值的方法就叫'欧拉函数',以 φ(n) 表示。
  3> 例如: φ(8) ,在1到8之中,与8构成互质关系的是1、3、5、7,所以 φ(8)  = 4.
3. 
2.2 RSA 的密钥生成步骤如下:
1. 随机挑选两个不相等的质数 p 和 q,假设 p = 13, q = 19。 
   (实际应用中,这两个质数越大,就越难破解)
2. 计算 p 和 q 的乘积 n
   1> n = p * q = 13 * 19 = 247;
   2> n 的长度就是密钥长度。 247对应的二进制是 11110111,一共8位,所以这个密钥就是8位。
   3> 实际应用中,RSA密钥一般是1024位,重要场合则为2048位。
3. 计算 n 的欧拉函数 φ(n)。
   1> φ(n) 表示与整数 n 互质数的个数。如果 n 等于两个质数的积,则φ(n) = (p-1)(q-1) 。
   2> φ(247) = (13-1)(19-1) = 216。
4. 随机选择一个整数数 e,满足 1< e <φ(n) ,并且 e 与φ(n)互质,假设 e = 17。
   (实际应用中,常常选择65537)
5. 计算 e 对于 φ(n) 的模反元素d
   1> 所谓『模反元素』就是指有一个整数d,可以使得 ed 被 φ(n)除的余数为1。
   2> ed = 1 (mod φ(n)) 等价于 ed - 1 = kφ(n)
   3> 于是,找到模反元素d,实质上就是对下面这个二元一次方程求解:
     ex + φ(n)y = 1
     已知 e = 17, φ(n) = 216,则 17x + 216y = 1
     这个方程可以用『扩展欧几里得算法』求解,此处省略具体过程。总之,可以算出一组整数解为(x, y) = (89, -7),即 d = 89。
   至此,所有计算完成。
6. 将 n 和 e 封装成公钥,n 和 d 封装成私钥。
   本例中: n = 247, e = 17, d = 89,所以公钥就是(247, 17), 私钥就是(247, 89)。
实际应用中,公钥和私钥数据都采用 ASN.1(Abstract Syntax Notation One 抽象语法标记) 格式表达。
2.3 RSA算法的可靠性
回顾上面的密钥生成步骤,一共出现了6个数字:
 p、q、n、φ(n)、e、d
1. 这6个数字之中,公钥用到了两个(n和e),其余四个数字都是不公开的。
2. 最关键的是d,因为 n 和 d 组成了私钥,一旦d泄露,就等于私钥泄露。
有无可能在已知 n 和 e 的情况下,推导出 d?
1> ed ≡ 1 (mod φ(n))。只有知道e和φ(n),才能算出d。
2> φ(n) = (p-1)(q-1)。只有知道p和q,才能算出φ(n)。
3> n = pq。只有将n因数分解,才能算出p和q。
   结论: 如果 n 可以被因数分解,d就是算出,也就意味着私钥被破解。
4> 但是当 n 大到一定程度时(比如接近 2^2048),即使现在最快的 CPU 也无法进行这个因式分解,即无法知道 n 是由哪个数 p 和 q 乘出来的。
   大整数的因数分解,是一件非常困难的事情。目前,除了暴力破解,还没有发现别的有效方法。
5> 所以就算知道了公钥,整个加解密过程还是非常安全的。

维基百科这样写:
"对极大整数做因数分解的难度决定了RSA算法的可靠性。
   换言之,对一极大整数做因数分解愈困难,RSA算法愈可靠。
   假如有人找到一种快速因数分解的算法,那么RSA的可靠性就会极度下降。但找到这样的算法的可能性是非常小的。
   今天只有短的RSA密钥才可能被暴力破解。到2008年为止,世界上还没有任何可靠的攻击RSA算法的方式。
  只要密钥长度足够长,用RSA加密的信息实际上是不能被解破的。"
举例: 你没法对下面这个整数进行因数分解
   12301866845301177551304949
  58384962720772853569595334
  79219732245215172640050726
  36575187452021997864693899
  56474942774063845925192557
  32630345373154826850791702
  61221429134616704292143116
  02221240479274737794080665
  351419597459856902143413
等于
   33478071698956898786044169
  84821269081770479498371376
  85689124313889828837938780
  02287614711652531743087737
  814467999489
    ×
  36746043666799590428244633
  79962795263227915816434308
  76426760322838157396665112
  79233373417143396810270092
  798736308917
事实上,这大概是人类已经分解的最大整数(232个十进制位,768个二进制位)。
比它更大的因数分解,还没有被报道过,因此目前被破解的最长RSA密钥就是768位。
2.4 加密和解密

有了公钥和私钥,就可以进行加密解密了。

2.4.1 加密要用公钥(n, e)

假设客户端要想服务器发送加密信息 m,它就要用服务器的公钥(n, e)对 m 进行加密。这里需要注意,m必须是整数(字符串可以取ASCII或Unicode值),并且 m 必须小于 n。

所谓『加密』,就是算出下式的c:
m^e ≡ c (mod n)

客户端的公钥是(247, 17),m值假设是 135,那么可以算出下面的等式:

135^17 = 200(mod 247)

于是 c 等于200,客户端就把 200 发送给了服务器。

2.4.2 解密要用私钥(n, d)

服务器拿到客户端发来的 200 以后,就用自己的私钥(247, 89)进行解密。可以证明,下面的等式一定成立:

c^d = m (mod n)
也就是说,c的d次方除以n的余数为m,那么,c等于200,私钥是(247, 89),那么服务器算出
200^89 = 135 (mod 247)

因此,服务器知道了客户端加密前的原文就是 135。
至此,"加密-解密"的整个过程全部完成。


RSA加密示例.png

可以看到,如果不知道 d,就没有办法从c求出m。要知道d就必须分解n,这是极难做到的,所以RSA算法保证了通信安全。

那么,公钥(n, e)只能加密小于n的整数,如果要加密大于n的整数,怎么办?
有两种解决方法
1> 把长信息分割成若干短信息,每段分别加密
2> 先选择一种对称加密算法(比如DES),用这种算法的密钥加密信息,再用RSA公钥加密DES密钥。
1> 实际应用中, (n, e) 组成了'公钥对',(n, d)组成了'私钥对',其中 n 和 d都是一个接近2048的大数。
即使现在性能很强的CPU,想要计算 m≡c^d mod(n),也需要消耗比较大的计算资源和时间。
2> 公钥对 (n, e) 一般都注册到了证书里,任何人都能直接查看,比如百度证书的公钥对如下图,

百度HTTPS证书公钥.png
e 取值比较小的好处有两个:
1. 由 c = m^e mod(n) 可知,e 较小,客户端 CPU 计算消耗的资源较少。
2. 加大 server 端的破解难度。e 比较小,私钥对中的 d 必然会非常大。所以 d 的取值空间也就非常大,增加了破解难度。

2.5 TLS握手过程中的 RSA 密钥协商

介绍玩 RSA的原理,那最终会话所需要的对称密钥是如何生成的呢?跟RSA有什么关系?
以 TLS1.2为例简单描述以下,省略跟密钥交换无关的握手消息。过程如下:

1. 客户端发送 client_hello, 包含一个随机数 random1。
2. 服务器回复 server_hello,包含一个随机数 random2,同时回复 certificate,携带了证书公钥P。
3. 客户端收到random2之后,就能够生成 premaster_secrect 以及 master_secrect。
  其中,premaster_secrect 长度为48个字节,前两个字节是协议版本号,剩下的46个字节填充一个随机数。结构如下:
  Struct {byte Version[2]; bute random[46];} 
** master_secrect 的生成算法简述如下
  Master_key = PRF(premaster_secrect, "master secrect", 随机数1+随机数2) 
  其中,PRF是一个随机函数,定义如下:
  PRF(secrect, label, seed) = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed)
而 master secrect 包含了6部分内容,分别是用于校验内容一致性的密钥,用于对称内容加解密的密钥,
以及初始化向量(用于CBC模式),客户端和服务器各一份,从上式可以看出,把premaster_key 赋值给 secrect, 
master key 赋值给 label,客户端和服务器的两个随机数做种子就能确定地求出一个 48位长的随机数。
4. 客户端使用证书公钥P将 premaster_secrect加密后发送给服务器。
5. 服务器使用私钥解密得到 premaster_secrect,又由于服务端之前就接收了随机数1,所以服务器根据相同的生成算法,在相同的输入参数下,求出了相同了 master secrect。
RSA密钥协商过程.png

可以看出,密钥协商过程需要 2 个 RT(Route Trip),这也是 HTTPS 慢的一个重要原因。而 RSA 发挥的关键作用就是对 premaster_secrect 进行了加密和解密。中间者不可能破解 RSA 算法,也就不可能知道 premaster_secrect,从而保证了密钥协商过程的安全性。


三、对称内容加密

1> 非对称密钥交换过程结束之后就得出了本次会话需要使用的对称密钥。
2> 对称加密又分为两种模式:流式加密和分组加密。
3> 流式加密现在常用的就是 RC4,不过 RC4 已经不再安全,微软也建议网站尽量不要使用 RC4 流式加密。
4> 一种新的替代 RC4 的流式加密算法叫 ChaCha20,它是 google 推出的速度更快,更安全的加密算法。
   目前已经被 android 和 chrome 采用,也编译进了 google 的开源 openssl 分支 —boring ssl,并且nginx 1.7.4 也支持编译 boringssl。

5> 分组加密以前常用的模式是 AES-CBC,但是 CBC 已经被证明容易遭受BEAST和LUCKY13 攻击。
   目前建议使用的分组加密模式是 AES-GCM,不过它的缺点是计算量大,性能和电量消耗都比较高,不适用于移动电话和平板电脑。
网络安全
Web note ad 1