加密通信 — 网络通信那些事
系列文章
文章目录
今天我们一起来聊一聊关于计算机安全的重中之重,
加密
。牢记一个核心原则:永远不要使用自己实现的加密算法 (我们老师说的原话是“Don’t roll your own crypto!!!”),而是去使用现有的库。因为需要用到加密的地方都涉及到敏感信息,而自己写的加密算法很可能存在bug导致敏感信息的泄露,相比较而言现有加密算法库,都经过无数人无数次的验证和测试,可靠性会高很多。
Hash (哈希,散列) 介绍
说到加密,肯定绕不过Hash的概念,所以这里单独把Hash拎出来说一下。
首先什么是Hash?Hash是把
任意长度的输入
(又叫做预映射pre-image)通过散列算法变换成
固定长度的输出
,该输出就是
散列值
。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数(摘自百度百科)。
那么怎么样的函数才能算作是一个好的Hash函数呢?其必须具备如下特点:
- 可以应用到任意大小的输入
- 产生固定大小的输出(比如MD5的输出为32字节)
-
对于任意的输入x,
H(
x
)
H(x)
H
(
x
)
都比较容易计算 -
输入改变一点点,输出改变很多
- 拿MD5算法举例(一个曾经应用很广泛的Hash算法),计算hello和hellp两个单词的Hash值
- “hello” — b1946ac92492d2347c6235b4d2611184
- “hellp” — 7bd75e0741818d4e6020b0250e52dd46
- 可以看到输入只相差一个字母,但是输出至少有一半以上的位不同
-
单向性 (one-way or pre-image resistant)
-
已知Hash值h,找到对应的输入x使得
H(
x
)
=
h
H(x) = h
H
(
x
)
=
h
是
计算上不可行
的
-
已知Hash值h,找到对应的输入x使得
-
已知一个输入x和其对应的Hash值
H(
x
)
H(x)
H
(
x
)
,找到一个不同的输入y使得
H(
x
)
=
H
(
y
)
H(x) = H(y)
H
(
x
)
=
H
(
y
)
是
计算上不可行
的 -
不容易冲突 (collision resistant)
-
找到一对输入
(x
,
y
)
(x, y)
(
x
,
y
)
,满足
x≠
y
并
且
H
(
x
)
=
H
(
y
)
x \neq y 并且 H(x) = H(y)
x
=
y
并
且
H
(
x
)
=
H
(
y
)
是
计算上不可行
的
-
找到一对输入
计算上不可行:计算找到符合条件的值所需时间不现实,如需要几百年
举个例子,假如我们现在写一个自己的hash函数,规定输入都是ASCII字符,然后输出固定长度为1个字节(最大0xffff)。最简单的方式,我们可以把输入的每一个字符对应的ASCII编码值相加,最终得到一个结果取余0xffff即可。该方法至少满足了Hash函数的定义(任意输入长度,固定输出长度),但是明显不是一个“好”的Hash函数(至少不符合6,7两点),明显”abc”和”bac”就会得到同样的Hash值(因为该方法并没有考虑到顺序问题)。
Hash的应用十分广泛,并不仅局限于加密方面,比如:
-
文件签名
- 这个最直接的一个应用场景就是”云盘秒传”,在上传资源的时候,云盘会先拿上传文件的Hash值去搜索储存空间,看看是否存在相同文件,假如存在就无需重复上传。这就是为什么一些热门资源上传速度非常快甚至可以实现秒传
-
HashMap的实现
- 每次读写HashMap只需要计算key的Hash值并到对应的地方进行数据的读写,不需要搜索整个底层数据结构
-
加密
-
由于Hash函数的不可逆性,我们可以储存密码的Hash值而不是原文,从而别人即使拿到数据,也无法得到真正的密码(对密码保存的,详见
加密储存 — 密码保存那些事
)
-
由于Hash函数的不可逆性,我们可以储存密码的Hash值而不是原文,从而别人即使拿到数据,也无法得到真正的密码(对密码保存的,详见
- 等等…
加密通信
介绍完Hash就来介绍一下今天的主角,
加密
。其中加密又分为两种,一种是实时的(比如加密通信),一种是非实时的(比如加密保存密码)。
- 相同 — 都需要对信息进行加密
-
不同
-
单向性
- 加密通信要能加解密
- 加密保存可能不需要解密
-
时效性
- 加密通信对速度有要求
- 保存对速度没有太高要求
-
单向性
本篇文章就来讨论一下加密通信常用的一些方法,每种方法都各有优劣,所以实际使用过程中一般是多种方法一起使用。下面介绍过程中会引入两个虚拟人物,Bob和Alice,假设两人之间互相通信。
对称加密 (Symmetric cryptography)
对称加密指的是双方使用
同一个密钥
进行加解密,发送方将“原文本” (plain text)加密后得到“密文” (cipher text),然后发送“密文”;接收方收到“密文”后,使用同一个密钥,解密得到“原文本”。基本公式如下:
-
c=
E
s
(
p
,
k
)
c = E_s(p,k)
c
=
E
s
(
p
,
k
)
— c (cipher text), p (plain text), k (secret key,密钥), E (encryption,加密算法) -
p=
D
s
(
c
,
k
)
p = D_s(c,k)
p
=
D
s
(
c
,
k
)
— D (decryption, 解密算法)
优劣:对称加密的好处是
计算速度快
,不会给通信带来明显的时延;但是不足之处在于
密钥的分发问题
,可以看到对称加密的整个安全性都取决于密钥的私密性,只要密钥只有接收和发送双方知道,那么即使第三方截获了信息也无法解密得到信息内容,但是一旦密钥泄露,那么就毫无安全性可言,任何一个知道密钥的人都可以解密得到信息内容。
这里可能会有一个误区就是,虽然密钥泄露了,但是只要加密算法不泄露,信息一样是安全的。注意加密的一个原则就是,“保持密钥的私密性但不保持算法的私密性”,这里有几个原因:1)常用的加密算法就那么几种,攻击方可以全都试一遍;2)我们应该使用经过无数人验证测试的可靠加密算法(亦即是公开的),而不是自己发明一个新的加密算法(尽管可能没人知道具体算法,但是可能会存在安全漏洞)。
常见加密算法
:
- DES (Data Encryption Standard)
- Triple DES:用2-3个不同的密钥,计算3次DES
- AES (Advanced Encryption Standard)
DES是1999年之前最常见的一个加密算法,但随着计算机算力的不断提高,DES开始变得不够用了;Triple DES只是一个过渡产物,通过多次运算来提高安全性;AES是后来提出的一个更安全的加密算法。这里就不详述算法的细节了,有兴趣的同学可以去自行百度。
拓展知识,如何定义一个加密算法是“计算安全 (computationally secure)”的?这个问题并没有一个统一的答案,不同场景下会有不同的结论,主要有两个核心衡量标准:
- 破解算法的代价超过了信息的价值;比如我有一段加密信息,内容是我的名字,那么其实使用什么加密算法都无所谓了,因为这个信息的价值很低;
- 破解算法所需要的时间超过了信息的有效时间;比如我有一个文件保存了我每个网站使用的密码(信息价值很高),但是我有一个习惯就是每7天就会更新所有的密码,那么我选取的加密算法只要能保证攻击者无法在7天内破解我的信息即可(7天后信息就过时了,也就意味着没了价值);
常见加密模式
:
上面描述的几种加密算法针对的都是固定大小的输入(64bit or 128bit),但是我们要求加密要能应付任意大小的输入,所以这里就涉及一个具体的操作模式。大体来说有两种模式,基于块的 (block cipher) 和基于流的 (stream cipher),由于基于块的更加常见,下面列举的也都是基于块的模式:
-
电码本模式 (ECB, Electronic codebook):最简单的方式,将输入划分为若干相等的数据块,使用同一个密钥对每一个数据块进行加密;
- 这也就意味着对于同样的输入数据块,输出总是相同的,这样就会给攻击者提供一些额外的信息,一个简单的例子,加密一幅图片,左边是未加密,右边是使用ECB模式加密后的结果
- 可以看到,加密后尽管图片变得“模糊”了,但是仍保留了大致的“轮廓”,提供了很多额外信息
- 密码分组链接模式 (CBC, Cipher Block Chaining):将原文分为若干项等的数据块,每一个数据块的初始plaintext与前一个块输出的ciphertext进行异或之后再将结果进行加密
- 密文反馈模式 (CFB, Cipher Feedback)
- 输出反馈模式 (OFB, Output Feedback)
- 计数模式 (CTR, Counter):设定一个计数器,每处理一个数据块计数器自增,然后将计数器的值加密后再与该块的原始plaintext进行异或操作得到最终结果
非对称加密 (Asymmetric cryptography)
相比较于对称加密,非对称加密使用的是
两个密钥(公钥 & 私钥)
,其中这两个密钥是一同产生的并且数学上相关的。发送方使用
公钥
将进行加密,接收方使用对应的
私钥
进行解密。基本公式如下:
-
c=
E
a
(
p
,
k
p
u
b
)
c = E_a(p, k_{pub})
c
=
E
a
(
p
,
k
p
u
b
)
— c (cipher text), p (plain text),
kp
u
b
k_{pub}
k
p
u
b
(public key), E (encryption) -
p=
D
a
(
c
,
k
p
r
i
v
)
p = D_a(c, k_{priv})
p
=
D
a
(
c
,
k
p
r
i
v
)
—
kp
r
i
v
k_{priv}
k
p
r
i
v
(private key)
优劣:相比较与对称加密,非对称加密的优点在于不需要考虑密钥的分发问题,因为只需要发送公钥,而公钥并不需要私密性(只能用于加密无法用于解密),所以不存在密钥泄露的问题(私钥本地保存,不要发送到网络上)。但是非对称加密也有个缺点就是
速度较慢
,假如用于实时通信会有比较明显的时延。
应用:
HTTPS就是这两种加密方式的一个典型应用(相比较于HTTP全部明文传输,HTTPS传输的是加密后的密文),为了将二者取长补短,HTTPS的基本工作流程是,首先客户端和服务器端利用
非对称加密
进行通信,二者商定一个共同的密钥,然后转为
对称加密
,利用刚刚商定好的密钥进行通信。这样大部分的通信使用的是对称加密,不会影响通信速度,同时又利用非对称加密解决了对称加密的密钥分发问题。
消息认证码 (Message Authentication Codes, MAC)
计算机安全领域除了加密 (encryption)之外还有一个重要概念,验证 (authentication)。即是说Bob可以验证该消息是Alice发送的,并且中间没有被人修改过。加密和验证有时候是同时出现的,有时候也会单独使用其中的某一种。比如:接收方要同时接收大量的数据,没有时间进行解密,所以采用明文发送(但是附带MAC进行消息验证)。
消息验证码的具体工作流程如下图:Alice先对要发送的所有信息(明文)做一次Hash,再将得到的值加密(对称 or 非对称)后得到MAC,然后添加到发送消息的结尾。Bob收到消息后,将单纯的信息(不包含MAC)同样做一次Hash并将得到的值加密,然后比较计算出来的
M
A
C
c
MAC_c
M
A
C
c
和接收到的
M
A
C
r
MAC_r
M
A
C
r
是否一致,当其一致的时候Bob就可以认为该消息是Alice发送的并且没有被人修改过。
上图中的MAC algorithm指的就是hash+encryption,K指的就是密钥(可以是对称加密里面的密钥,也可以是非对称加密里面的私钥)。注意这里消息内容是明文发送的,所以只有authentication没有encryption,当然我们也可以把消息加密后再发送,那样就同时拥有authentication和encryption了。
注意MAC的整个安全基础和加密一样,都在于
密钥的私密性
,无论是对称加密中的密钥还是非对称加密中的私钥,都需要保证该密钥只有发送方(和接收方)知道,不能有第三方知道。
数字签名 (Digital signatures)
数字签名本质上和MAC是一个东西,唯一的区别在于,数字签名必须使用非对称加密,和表面含义一样,数字签名不仅有authentication的功能,同时还具有
不可抵赖性
(犹如实际生活中我们的签名)。因为非对称加密的特性,决定了只有Alice拥有其自己的私钥,所以我们可以认为,假如一个我们可以用Alice的公钥去验证一条接收到的消息,那么这条消息一定是发自于Alice。