分组密码的模式(分组密码是如何迭代的?)
密码算法可以分为分组密码和流密码两种。
分组密码是每次只能处理特定长度的一块数据的一类密码算法,这里的“一块”就称为分组。此外,一个分组的比特数就称为分组长度。
例如,DES和三重DES的分组长度都是64比特。这些密码算法一次只能加密64比特的明文,并生成64比特的密文。AES的分组长度为128比特,因此AES一次可加密128比特的明文,并生成128比特的密文。
流密码是对数据流进行连续处理的一类密码算法。流密码中一般以1比特、8比特或32比特等为单位进行加密和解密。
分组密码处理完一个分组就结束了,因此不需要通过内部状态来记录加密的进度;相对地,流密码是对一串数据流进行连续处理,因此需要保持内部状态。
在“对称密码”中所介绍的算法中,只有一次性密码本属于流密码,而DES、三重DES、AES(Rijndael)等大多数对称密码算法都属于分组密码。
什么是模式?
分组密码算法一次只能加密固定长度的分组,但是我们需要加密的明文长度可能会超过分组密码的分组长度,这时就需要对分组密码算法进行迭代,以便将一段很长的明文全部加密。而迭代的方法就称为分组密码的模式。
话说到这里,很多人可能会说:“如果明文很长的话,将明文分割成若干个分组再逐个加密不就好了吗?”
事实上可没有那么简单。将明文分割成多个分组并逐个加密的方法称为ECB模式,这种模式具有很大的弱点(稍后讲解)。对密码不是很了解的程序员在编写加密软件时经常会使用ECB模式,但这样做会在不经意间产生安全漏洞,因此大家要记住千万不能使用ECB模式。
模式有很多种类,分组密码的主要模式有以下5种。
ECB模式:Electronic CodeBook mode(电子密码本模式)
CBC模式:Cipher Block Chaining mode(密码分组链接模式)
CFB模式:Cipher FeedBack mode(密文反馈模式)
OFB模式:Output FeedBack mode(输出反馈模式)
CTR模式:CounTeR mode(计数器模式)
本节中会出现一个新的概念一主动攻击者(小攻)。窃听者只能被动地进行窃听,而主动攻击者则可以主动介入发送者和接收者之间的通信过程,进行阻碍通信或者是篡改密文等活动。
什么是ECB模式?
在ECB模式中,将明文分组加密之后的结果将直接成为密文分组。
大致步骤如下:
第一步:明文分组1 => 加密 => 密文分组1
第二步:明文分组2 => 加密 => 密文分组2
第三步:明文分组3 => 加密 => 密文分组3
使用ECB模式加密时,相同的明文分组会被转换为相同的密文分组,也就是说,我们可以将其理解为是一个巨大的“明文分组→密文分组”的对应表,因此ECB模式也称为电子密码本模式。当最后一个明文分组的内容小于分组长度时,需要用一些特定的数据进行填充。
ECB模式是所有模式中最简单的一种。ECB模式中,明文分组与密文分组是一一对应的关系,因此,如果明文中存在多个相同的明文分组,则这些明文分组最终都将被转换为相同的密文分组。这样一来,只要观察一下密文,就可以知道明文中存在怎样的重复组合,并可以以此为线索来破译密码,因此ECB模式是存在一定风险的。
ECB模式中,每个明文分组都各自独立地进行加密和解密,但这其实是一个很大的弱点。
假如小攻,他能够改变密文分组的顺序。当接收者对密文进行解密时,由于密文分组的顺序被改变了,因此相应的明文分组的顺序也会被改变。也就是说,小攻无需破译密码就能够操纵明文。在这个场景中,他只要知道哪个分组记录了什么样的数据(即电文的格式)就可以了。
我们来看一个简单的例子。假设某银行的转账请求数据由以下3个分组构成。
分组1=付款人的银行账号
分组2=收款人的银行账号
分组3=转账金额
银行在收到转账请求数据后,就会将数据中指定的金额从付款人的账户转移到收款人的账户中。接下来,小攻将密文分组1和2的内容进行对调,并没有试图破译密码。而银行对上述信息解密后,就会变的完全相反。
通过这个例子我们可以看出,ECB模式的一大弱点,就是可以在不破译密文的情况下操纵明文。
在ECB模式中,只要对任意密文分组进行替换,相应的明文分组也会被替换。此外,小攻所能做的还不仅限于替换,例如,如果将密文分组删除,则相应的明文分组也会被删除,如果对密文分组进行复制,则相应的明文分组也会被复制。
如果使用除ECB之外的其他模式,那么上述攻击从一开始就是不可能实现的。
什么是CBC模式?
CBC模式是密文分组链接模式,之所以叫这个名字,是因为密文分组是像链条一样相互连接在一起的。在CBC模式中,首先将明文分组与前一个密文分组进行异或运算,然后再进行加密
当加密第一个明文分组时,由于不存在“前一个密文分组”,因此需要事先准备一个长度为一个分组的比特序列来代替“前一个密文分组”,这个比特序列称为初始化向量,通常缩写为IV。一般来说,每次加密时都会随机产生一个不同的比特序列来作为初始化向量。
大致步骤如下:
第一步:初始化向量 ⊕ 明文分组1 => 加密 => 密文分组1
第二步: 密文分组1 ⊕ 明文分组2 => 加密 => 密文分组2
第三步:密文分组2 ⊕ 明文分组3 => 加密 => 密文分组3
明文分组在加密之前一定会与“前一个密文分组”进行异或运算,因此即便明文分组1和2的值是相等的,密文分组1和2的值也不一定是相等的。这样一来,ECB模式的缺陷在CBC模式中就不存在了。
在CBC模式中,我们无法单独对一个中间的明文分组进行加密。例如,如果要生成密文分组3,则至少需要凑齐明文分组1、2、3才行。
解密的步骤如下:
第一步:密文分组1 => 解密 => ⊕ 初始化向量 => 明文分组1
第二步:密文分组2 => 解密 => ⊕ 密文分组1 => 明文分组2
第三步:密文分组3 => 解密 => ⊕ 密文分组2 => 明文分组3
现在假设CBC模式加密的密文分组中有一个分组损坏了(例如由于硬盘故障导致密文分组的值发生了改变等)。在这种情况下,只要密文分组的长度没有发生变化,则解密时最多只会有2个分组受到数据损坏的影响。
假设CBC模式的密文分组中有一些比特缺失了(例如由于通信错误导致没有收到某些比特等),那么此时即便只缺失了1比特,也会导致密文分组的长度发生变化,此后的分组发生错位,这样一来,缺失比特的位置之后的密文分组也就全部无法解密了。
假设小攻的目的是通过修改密文来操纵解密后的明文。如果小攻能够对初始化向量中的任意比特进行反转(即将1变为0,将0变为1),则明文分组(解密后得到的明文分组)中相应的比特也会被反转。这是因为在CBC模式的解密过程中,第一个明文分组会和初始化向量进行异或运算。
这样,小攻就可以对初始化向量进行攻击,但是想要对密文分组也进行同样的攻击就非常困难了。例如,如果将密文分组1中的某个比特进行了反转,则明文分组2中相应的比特也会被反转,那么明文分组3中相应的比特也会被反转,这1比特的变化却会对解密后的明文分组中的多个比特造成影响。也就是说,只让明文分组1中所期望的特定比特发生变化是很困难的。
填充提示攻击是一种利用分组密码中的填充部分来进行攻击的方法。在分组密码中,当明文长度不为分组长度的整数倍时,需要在最后一个分组中填充一些数据使其凑满一个分组长度。在填充提示攻击中,攻击者会反复发送一段密文,每次发送时都对填充的数据进行少许改变。由于接收者(服务器)在无法正确解密时会返回一个错误消息,攻击者通过这一错误消息就可以获得一部分与明文相关的信息。这一攻击方式并不仅限于CBC模式,而是适用于所有需要进行分组填充的模式。要防御这种攻击,需要对密文进行认证,确保这段密文的确是由合法的发送者在知道明文内容的前提下生成的。
什么是CFB模式?
CFB模式的全称是密文反馈模式。在CFB模式中,前一个密文分组会被送回到密码算法的输入端。所谓反馈,这里指的就是返回输入端的意思。与CBC模式不同的是,在CFB模式中,是首先将前一个密文分组进行加密,然后再与明文分组进行异或运算。
在生成第一个密文分组时,由于不存在前一个输出的数据,因此需要使用初始化向量来代替,这一点和CBC模式是相同的。
大致步骤如下:
第一步:初始化向量 => (加密) => ⊕ 明文分组1 => 密文分组1
第二步:密文分组1 => (加密) => ⊕ 明文分组2 => 密文分组2
第三步:密文分组2 => (加密) => ⊕ 明文分组3 => 密文分组3
在ECB模式和CBC模式中,明文分组都是通过密码算法进行加密的,然而,在CFB模式中,明文分组并没有通过密码算法来直接进行加密。是将前一个密文分组进行加密,然后与明文分组进行异或运算,明文分组和密文分组之间只有一个异或。
解密的步骤如下:
第一步:初始化向量 => (加密不是解密) => ⊕ 密文分组1 => 明文分组1
第二步:密文分组1 => (加密) => ⊕ 密文分组2 => 明文分组2
第三步:密文分组2 => (加密) => ⊕ 密文分组3 => 明文分组3
CFB模式解密时,需要注意的是分组密码算法依然执行加密操作,因为密钥流是通过加密操作来生成的。
其实CFB模式的结构与我们在介绍的一次性密码本是非常相似的。一次性密码本是通过将“明文”与“随机比特列”进行异或运算来生成“密文”的。
在CFB模式中,密码算法的输出相当于一次性密码本中的随机比特序列。由于密码算法的输出是通过计算得到的,并不是真正的随机数,因此CFB模式不可能像一次性密码本那样具备理论上不可破译的性质。
CFB模式中由密码算法所生成的比特序列称为密钥流。在CFB模式中,密码算法就相当于用来生成密钥流的伪随机数生成器,而初始化向量就相当于伪随机数生成器的“种子”。关于伪随机数生成器和种子,我们将在后面详细探讨。我们可以将CFB模式看作是一种使用分组密码来实现流密码的方式。
对CFB模式可以实施重放攻击。有一天,小发向小接发送了一条消息,这条消息由4个密文分组组成。小攻将该消息中的后3个密文分组保存了下来。转天,小发又向小接发送了内容不同的4个密文分组(我们假设小发使用了相同的密钥)。小攻用昨天保存下来的3个密文分组将今天发送的后3个密文分组进行了替换。
于是,当小接解密时,4个分组中就只有第1个可以解密成正确的明文分组,第2个会出错,而第3个和第4个则变成了被小攻替换的内容(也就是昨天发送的明文内容)。小攻没有破解密码,就成功地将以前的电文混入了新电文中。而第2个分组出错到底是通信错误呢,还是被人攻击所造成的呢?小接是无法做出判断的。要做出这样的判断,需要使用消息认证码。
什么是OFB模式
OFB模式的全称是输出反馈模式。在OFB模式中,密码算法的输出会反馈到密码算法的输入中。OFB模式并不是通过密码算法对明文直接进行加密的,而是通过将“明文分组”和“密码算法的输出”进行异或来产生“密文分组”的,在这一点上OFB模式和CFB模式非常相似。
OFB模式中也需要使用初始化向量。
大致步骤如下:
第一步:初始化向量 =>(加密) ⊕ 明文分组1 => 密文分组1
第二步:初始化向量 =>(加密*2) ⊕ 明文分组2 => 密文分组2
第三步:初始化向量 =>(加密*3) ⊕ 明文分组3 => 密文分组3
解密的步骤如下:
第一步:初始化向量 =>(加密不是解密) => ⊕ 密文分组1 => 明文分组1
第二步:初始化向量 => (加密*2) => ⊕ 密文分组2 => 明文分组2
第三步:初始化向量 => (加密*3) => ⊕ 密文分组3 => 明文分组3
OFB模式和CFB模式的区别仅仅在于密码算法的输入。
CFB模式中,密码算法的输入是前一个密文分组,也就是将密文分组反馈到密码算法中,因此就有了“密文反馈模式”这个名字。
相对地,OFB模式中,密码算法的输入则是密码算法的前一个输出,也就是将输出反馈给密码算法,因此就有了“输出反馈模式”这个名字。OFB模式中加密的一直是 初始化向量
由于CFB模式中是对密文分组进行反馈的,因此必须从第一个明文分组开始按顺序进行加密,也就是说无法跳过明文分组1而先对明文分组2进行加密。
相对地,在OFB模式中,异或所需要的比特序列(密钥流)可以事先通过密码算法生成,和明文分组无关。只要提前准备好所需的密钥流,则在实际从明文生成密文的过程中,就完全不需要动用密码算法了,只要将明文与密钥流进行异或就可以了。
和AES等密码算法相比,异或运算的速度是非常快的。这就意味着只要提前准备好密钥流就可以快速完成加密。
什么是CTR模式?
CTR模式的全称是CounTeR模式(计数器模式)。CTR模式是一种通过逐次累加的计数器进行加密来生成密钥流的流密码。
CTR模式中,每个分组对应一个逐次累加的计数器,并通过对计数器进行加密来生成密钥流。也就是说,最终的密文分组是通过将计数器加密得到的比特序列,与明文分组进行异或而得到的。
大致步骤如下:
第一步:计数器CTR =>(加密) ⊕ 明文分组1 => 密文分组1
第二步:计数器CTR+1 =>(加密) ⊕ 明文分组2 => 密文分组2
第三步:计数器CTR+2 =>( 加密) ⊕ 明文分组3 => 密文分组3
解密的步骤如下:
第一步:计数器CTR =>(加密) ⊕ 密文分组1 => 明文分组1
第二步:计数器CTR+1 =>(加密) ⊕ 密文分组2 => 明文分组2
第三步:计数器CTR+2 =>( 加密) ⊕ 密文分组3 => 明文分组3
CTR模式中可以以任意顺序对分组进行加密和解密,因为计数器与分组序号是可以关联起来的。这一性质是OFB模式所不具备的。能够以任意顺序处理分组,就意味着能够实现并行计算。在支持并行计算的系统中,CTR模式的速度是非常快的。
在错误与机密性方面,CTR模式也具备和OFB模式差不多的性质。假设CTR模式的密文分组中有一个比特被反转了,则解密后明文分组中仅有与之对应的比特会被反转,这一错误不会放大。
换言之,在CTR模式中,主动攻击者可以通过反转密文分组中的某些比特,引起解密后明文中的相应比特也发生反转。这一弱点和OFB模式是相同的。
不过CTR模式具备一个比OFB模式要好的性质。在OFB模式中,如果对密钥流的一个分组进行加密后其结果碰巧和加密前是相同的,那么这一分组之后的密钥流就会变成同一值的不断反复。在CTR模式中就不存在这一问题。
我们已经介绍了ECB、CBC、CFB、OFB和CTR等模式,首先,希望大家搞清楚每种模式的3个字母到底是什么的缩写。如果能够记住每个模式的名称,就能够在头脑中想象出相应的结构图,也就能够搞清楚每个模式的特点了。
最后推荐大家使用除了 ECB 模式之外的其他模式。