在更进一步探讨密码技术之前,先考虑一个问题,加密的本质是什么?
炒鸡蛋这个例子挺生动。把鸡蛋打到锅里炒匀,炒好后原来的蛋黄和蛋清就混合在一起,再也看不出来哪个部分是蛋黄,哪个部分是蛋清。加密的本质,和炒鸡蛋异曲同工。为了使明文不被推测出来,也需要一个“炒”的过程,即把明文的二进制序列打乱,成为密文。好的加密算法,就在于无法通过密文还原成明文。
当然,加密和炒鸡蛋还是有巨大差异的,炒鸡蛋再也无法恢复原样,加密则需要通过计算恢复成原来的明文,否则加密也就没有实际意义了。
上一篇《密码学的基本概念》中,提到有两种密码算法,一种是对称密码算法,一种是非对称密码算法,它们的本质区别在于加解密的过程是否使用相同的密钥。这篇笔记,就是关于对称密码。
基本概念一:二进制编码
计算机将一切信息都转化成二进制,每个信息都是0和1组成的比特序列。将信息映射成比特序列的操作称为 编码(encoding)。当然,编码规则多种多样,比如常见的 ASCII、GB2312 等,以ASCII编码为例:
字母 | ASCII编码 |
---|---|
m | 01101101 |
i | 01101001 |
d | 01100100 |
n | 01101110 |
i | 01101001 |
g | 01100111 |
h | 01101000 |
t | 01100100 |
需要注意的是,编码并不是加密,编码仅仅是将现实世界的事物用0和1进行规则的排列,而加密则是将这些有规律的二进制序列打乱。
基本概念二:异或(XOR)
XOR运算是密码领域的关键概念。XOR 运算规则是:
- 0 XOR 0 = 0
- 0 XOR 1 = 1
- 1 XOR 0 = 1
- 1 XOR 1 = 0
也就是说:
- 两个相同的数字进行 XOR 的结果是0,不同的数字进行 XOR 的结果是1
- 与0异或,数值不变;与1异或,数值反转
于是,我们有一个重要的发现:A 与 B 异或后再与 B 异或,结果将变回 A。
比如 A = 01001100,B = 10101010,那么 (A XOR B) XOR B = A
01001100 A
XOR 10101010 B
----------------
11100110
XOR 10101010 B
----------------
01001100 A
上面的计算过程,其实就是对称加密算法的基本原理。A 代表明文,B 代表密钥,XOR 则代表加解密算法。密钥在其中的作用就是打乱与恢复二进制序列。
你有没有发现,即使一个简单的 XOR 运算,如果密钥的二进制序列 足够随机,一般人是很难通过密文猜出明文的。
而基于 XOR 这个简单的运算,能带来一种绝对不会被破译的加密方法 -- 一次性密码本。
基本概念三:一次性密码本
一次性密码本有这样几个特点:
- 算法极其简单
- 绝不会被破译
- 没有实用价值
看完这三个特点,你是不是有一种匪夷所思的感觉?对的,我第一次接触这个概念,也是一脸懵逼。一种极其简单、绝对不会被破译的方法怎么会没有实用价值呢?
首先,一次性密码本的加密原理是 “将明文与一串与明文长度相同的随机的二进制序列进行 XOR 运算”,解密原理则是 “将运算结果与这串随机的二进制序列进行 XOR 运算”。
比如上文提到的 “midnight” 的 ASCII 编码是64位的二进制序列:“01101101 01101001 01100100 01101110 01101001 01100111 01101000 01100100”,用一个64位的随机二进制序列对其进行加密,将得到一个看似也是随机的64位加密结果。
其次,为什么如此简单的方式却无法被破译呢?我们假设采用暴力破解的方式,尝试用所有可能的64位二进制序列对密文进行异或运算,结果中即可能包含 “midnight”、“mistress”、“congress”、“abstract” 这样有意义的单词,也可能包含 “ABCDEFGH”、“BBBBBBBB”、“ZYXWVUTS” 这样规则字符串,也可能包含 “Hc(dfGHz”、“EC34jA9!” 这样毫无意义的字符串。
那么问题来了,究竟哪个字符串才是真正的明文呢?正是由于我们无法确认得到的是不是正确的明文,因此说一次性密码本是无法破译的。
更难以理解的是,大名鼎鼎的数学家与计算机科学家香农(C.E.Shannon)在1949年居然通过数学方法加以证明,一次性密码本是 无条件安全的,在理论上是无法破译的。数学好神奇。
通过上面的描述,我们确实看到,一次性密码本算法极其简单的,而且无法破译,那为什么没有实用价值呢?
最大的问题在于密钥的配送和保存。
你使用了一次性密码本对明文进行了加密,并将密文发送给接收者,然后通过某种秘密的途经将相同长度的密钥也发送给接收者。我们稍微深入的想一想:
- 如果密钥能够安全的传送,那么,相同长度的明文不也可以安全的传送么?
- 如果密钥能够安全的保存,那么,相同长度的明文不也可以安全的保存么?
而且,由于密钥长度与明文相同,在传送的过程中,任意一个比特出现翻转,解密的结果都将错误。明文有 100Mbit,密钥就需要 100Mbit。保证 100Mbit 的密钥不发生任何比特错误,也是极大的挑战。
因此,只有极其重要的数据和机密性场景,才会使用一次性密码本 ,密钥由专门的人力/通道负责传送。一次性密码本也就几乎没有任何实用价值。
读书的时候,看到一个很有意思的小问题,文章的最后将揭示答案:
虽然一次性密码本的密钥需要与明文一样长,那能不能通过压缩软件,对密钥进行压缩,这样密钥不久变短了了吗?这个想法正确吗?
上面提到了对称密码算法的一些背景知识,常见的对称密码算法有 DES、3DES、AES,以及国家商用密码 SM4 等。下面对 DES 和 AES 这两种著名对称密码算法的基本原理进行介绍。
1. DES 对称密码算法
DES(Data Encryption Standard)是1977年美国联邦信息处理标准(大名鼎鼎的 FIPS)中采用的一种对称密码,被广泛应用于各国政府和银行机构。这种算法目前已经不再安全,1999年的 DES Challenge III 大赛中仅仅用了22小时15分钟便被破解。在所有新的应用中,都不应该再使用 DES 算法,它也完成了自己的历史使命。然而,DES 作为第一个广泛使用的对称加密算法,很多设计思想和理念都为后续的对称加密算法提供了宝贵的借鉴。对 DES 基本原理一窥究竟,是挺有价值,挺有意思的的事情。
DES 是一种将64个比特的明文加密成64个比特密文的算法,其密钥长度是64比特,但每隔7个比特会设置一个校验字节,也就是说,实际密钥长度是56比特。
DES 以64比特为单位进行加密,而这64个比特的单位就成为 分组,以分组为单位进行处理的密码算法就成为 分组密码算法。对超过64比特的明文进行加密,需要先进行分组,然后进行迭代处理,这个迭代的具体方式就称为 模式(mode)。模式多种多样,也很有意思,后面会单独拿出一个章节来记录。
DES 基本结构是由 Horst Feistel 设计的,因此也叫 Feistel 结构。在 Feistel 结构中,核心的概念是 “轮(round)”,整个加密过程就是若干次 “轮” 的循环,DES 就是一种16轮循环的 Feistel 结构。每一轮的计算过程如下图所示:
从这张图能够看到 Feistel 结构鲜明特色的一点:每一轮加密,“右侧”的数据是没有被加密的,而是作为“混合源”,与子密钥一起,在轮函数的作用下,与“左侧”的数据进行异或操作,得到“加密后的左侧”。
这里有两个概念:
- 子密钥:指 本轮加密使用的密钥 。在 Feistel 结构中,每一轮都需要使用一个不同的子密钥。子密钥是由主密钥通过固定算法派生而成。
- 轮函数:轮函数是 Feistel 系统的核心,根据“右侧”与“子密钥”生成对“左侧”进行加密的比特序列,而将“左侧”与该比特序列进行异或运算,就得到“加密后的左侧”。
总结一下,每一轮的运算过程如下:
1. 将输入的64比特数据分成左右两份
2. 将输入的右侧直接发送到输出结果的右侧
3. 将输入的右侧发送到轮函数
4. 轮函数根据输入的右侧与子密钥计算一串比特序列
5. 将输入的左侧与这串比特序列进行 XOR 操作,作为加密后的左侧
而用同样的子密钥和轮函数对一轮的运算结果再运行一次,就完成了解密操作,好玩吧:
在实际应用中,由于每一轮的处理过程,“右侧”其实是没有被加密的,因此真正有实用价值的 Feistel 结构都是多轮处理的,在两轮之间,左右进行一次对调。如上文所说,DES 算法就是一个16轮 Feistel 结构。
Feistel 的这样一些性质:
- 轮数可以任意增加,无论多少轮,都不会出现无法解密的情况
- 轮函数可以任意设计,无论多简单,还是多复杂,都不会出现无法解密的情况
- 加密和解密可以用完全相同的结构来实现,这样非常利用基于硬件来实现加解密的逻辑
下图是一个3轮 Feistel 结构:
针对3轮 Feistel 解密时,只需要采用逆向过程,即 “子密钥3” - “子密钥2” - “子密钥1” 运行3轮 Feistel 网络对密文进行处理即可。也就是说,DES 的加密和解密过程,只是调整了子密钥的先后顺序,而实际进行的处理是相同的。
这就是基于 Feistel 结构的 DES 密码算法的基本原理了,很有意思。需要注意的是,Feistel 结构不仅仅被 DES 算法使用,很多分组加密算法都采用了 Feistel 结构。
AES 对称密码算法
AES(Advanced Encryption Standard)的目标就是取代不再推荐使用的 DES(包括 3DES)。美国国家标准技术研究所(NIST)最终在诸多加密算法中挑选了一个名为 Rijndael 的算法(由比利时密码学家 Daemen 和 Rijmen 提出),这个选拔过程挺有意思:
1. 算法必须可以无条件的免费供给全世界使用
2. 算法必须公开详细设计和代码细节
尤其第二点,是现代密码学的正确思想,数学算法,而非 “隐蔽式安全” 。算法与代码完全公开,直面全世界的密码学家与黑客的挑战,如果能未找到弱点,才能证明这种算法的强度。
AES 的 Rijndael 算法和 DES 的 Feistel 算法类似,都由多 “轮” 构成,每轮对固定长度的数据进行处理。和 DES 不同,AES 每轮对 “128比特/16字节” 进行处理,每一轮分为 “SubBytes”、“ShiftRows”、“MixColumns” 和 “AddRoundKey” 四个步骤构成。 下面对 Rijndael 处理过程进行简要的说明,该算法在 《The Design of Rijndael》中有详细的介绍。
Rijndael 加密过程
1. SubBytes 过程
使用包含256个值的替换表(S-Box)对每个字节进行逐一替换。
2. ShiftRows 过程
以4字节为单位的行(row)按照一定规则 向左平移,而且每一行平移的字节数是不同的。
3. MixColumns 过程
以4字节为单位的列(column)进行 矩阵运算,将其变换为另一个4字节的列。
4. AddRoundKey 过程
使用轮密钥,对每个字节进行 XOR 运算。
上述就是 Rijndael 处理过程,有两个优点:
1. 每一轮对每个比特均会进行加密操作,而 Feistel 每轮仅加密一半的比特,因此加密轮数可以更少
2. 各阶段以字节、行、列为单位进行运算,非常有利于并行处理,达到更优的性能
Rijndael 解密过程
Rijndael 每一轮加密过程为:SubBytes -> ShiftRows -> MixColumns -> AddRoundKey
Rijndael 每一轮解密就是加密的逆过程:AddRoundKey -> InvMixColumns -> InvShiftRows -> InvSubBytes
1. AddRoundKey 过程
使用轮密钥,对每个字节进行 XOR 运算。
2. MixColumns 过程
以4字节为单位的列(column)进行 逆矩阵运算,将其变换为另一个4字节的列。
3. ShiftRows 过程
以4字节为单位的行(row)按照一定规则 向右平移,而且每一行平移的字节数是不同的。
4. SubBytes 过程
使用包含256个值的替换表(S-Box)对每个字节进行逐一替换。
Rijndael 算法背后有严谨的数学结构,从明文到密文整个的计算过程全部可以用公式来表达。即便如此,截至目前,还没有出现针对 Rijndael 的有效攻击。AES 规格中,分组长度固定为16个字节,密钥长度则有128比特、192比特和256比特三种,分别记为 AES-128、AES-192 和 AES-256。
以上,就是对称密码算法的一些知识,下一篇笔记,将针对对称密码算法中另一个重要概念 -- 分组密码模式 -- 进行介绍。