DES算法

DES算法简介

DES(Data Encryption Standard)是目前最为流行的加密算法之一。DES是对称的,也就是说它使用同一个密钥来加密和解密数据。

DES还是一种分组加密算法,该算法每次处理固定长度的数据段,称之为分组。DES分组的大小是64位,如果加密的数据长度不是64位的倍数,可以按照某种具体的规则来填充位。

从本质上来说,DES的安全性依赖于虚假表象,从密码学的术语来讲就是依赖于“混乱和扩散”的原则。混乱的目的是为隐藏任何明文同密文、或者密钥之间的关系,而扩散的目的是使明文中的有效位和密钥一起组成尽可能多的密文。两者结合到一起就使得安全性变得相对较高。

DES算法具体通过对明文进行一系列的排列和替换操作来将其加密。过程的关键就是从给定的初始密钥中得到16个子密钥的函数。要加密一组明文,每个子密钥按照顺序(1-16)以一系列的位操作施加于数据上,每个子密钥一次,一共重复16次。每一次迭代称之为一轮。要对密文进行解密可以采用同样的步骤,只是子密钥是按照逆向的顺序(16-1)对密文进行处理。

计算16个子密钥

上面提到DES算法的第一步就是从初始密钥中计算得出16个子密钥。图示1展示了这个过程。DES使用一个56位的初始密钥,但是这里提供的是一个64位的值,这是因为在硬件实现中每8位可以用于奇偶校验,在软件实现中多出的位只是简单的忽略掉。要获得一个56位的密钥,可以执照表1的方式执行密钥转换。解释一下表1,按照从左往右从上往下的方式看,表格中每个位置P包含初始密钥中位在转换后的密钥中所占的位置。比如,初始密钥中的第57位就是转换后的密钥中的第1位,而初始密钥中的第49位则变成转换后的密钥中的第2位,以此类推…。(数据位的计数顺序按照从左到右从1开始的)

表1:DES中密钥的转换表(DesTransform[56])

img

将密钥转换为56位后,接下来计算子密钥。首先,将56位的密钥分为两个28位的组。然后,针对每个子密钥,根据子密钥的序列值(也就是16个子密钥中的第几个)旋转这两组值(旋转的位数见表2),然后重新合并。之后,再按照表3所示对重组后的密钥进行置换,使56位的子密钥缩小为48位(注意表3只有48位,丢弃了8位)这个排列过程就称为置换选择

针对16个子密钥,每个子密钥重复一次该过程。这里的目的是保证将初始密钥中的不同位在每一轮排列后应用于加密的数据上。

表2:针对DES子密钥每一轮的旋转次数(Round轮,Rotations旋转次数)(DesRotations)

img

表3:DES子密钥的置换选择(Despermuted[48])

img

图1:在DES中计算子密钥的过程

img

加密和解密数据块

经过上述过程,我们已经准备好了子密钥。接着就可以加密和解密数据块了。图2展示了这个过程。

图2:DES中加密和解密数据块

img

从表4所示的方式置换64位的数据块开始,该置换过程称为初始置换。该过程并不会增加DES的安全性,但这种做法在16位和32位的总线出现之前将使得数据更容易加载到DES芯片中。虽然这种处理已经不合时宜,但该置换过程仍然保留以满足DES标准。

表4:DES中数据的初始置换(DesInitial[64])

img

经过初始置换后,64位的数据块分为两个32位的组,L0和R0。

完成初始置换后,数据块将重复执行16轮一系列的操作。每一轮操作(i)的目的是计算出Li和Ri ,这些结果将用在下一轮操作中直到最终得到数据R16和L16。

每一轮以Li-1和Ri-1开始,然后根据表5所示进行扩展置换,将Ri-1从32位扩展到48位。该置换的主要目的是在加密数据的过程中制造一些雪崩效应,使用数据块中的1位将在下一步操作中影响更多位,从而产生扩散效果

一旦扩展置换完成,计算出48位的结果值与这一轮子密钥Ki的异或值(XOR,符号计为⊕)。这将产生48位的中间值,记为Rint

如果将E计为扩展置换的结果,则本轮到目前为止的操作可以表示为:

Rint = E(Ri-1) ⊕ K****i

表5:DES中数据块的扩展置换(DesExpansion[48])

img

下一步,Rint 需要通过8个单独的S盒执行8次替换操作。每个S盒(j)从Rint的6j 到 6j+6 的位置取出6位,并为其在表6中查出1个4位的值,将该值写到缓冲区的4j位置处(如图3)。

图3:DES中的8个S盒

img

读表6,查找S盒(j)。通过前面取出的6位值,根据第1位和最后1位组成的2位值找到表6中的行号,而根据中间剩下的4位来确定表6中的列号。比如,在图3中,Rint中的第3个6位组是101011。因此,在表6中查找到的第3个S盒是9。因为行号等于112 = 3,列号等于01012 = 5(查表时从索引0开始计数)。S盒为数据增加了不确定性,除了给DES带来安全性外,没什么特别的。

表6:DES中数据块的S盒替换

img

img

一旦完成了S盒替换,得到的结果又变为一个32位的值。接下来再通过P盒来置换。如下表7所示。

表7:DES中数据块的P盒置换

img

到目前为止,我们把这一轮的操作想象为一个函数,一般记为f。如果 bj 代表Rint中的第j个6位组,Sj 代表第j个S盒,而P代表P盒置换,则该函数可以定义为

f = P(S1(b1),S2(b2),…,S8(b8))

每一轮的最后一个操作是计算 f 的32位结果值与传入本轮操作的原始数据的左分组Li-1之间的异或值。

一旦完成,将左右两个分组交换然后开始下一轮

在最后一轮中,不用交换左右分组。

把所有的步骤连起来,在每一轮中计算Li和Ri的步骤可以精确表示为:

Li = Ri-1

Ri = Li-1 *⊕* f(Ri-1,Ki)

当全部的16轮操作都结束后,将最后的右分组R16和最后剩下的左分组L16连接起来,组成一个64位的分组R16L16。(回顾一下,在最后一轮中左右分组并没有交换。最后的右分组在左边而最后的左分组在右边。)

最后一步是将R16L16按照表8所示的置换进行置换。简而言之,就是撤消之前的初始置换。

加密数据时,最终结果就是一个64位的密文,而当解密数据时,最终结果就是64位的明文了。

表8:DES中数据块的最终置换

img

DES的接口定义

des_encipher


void des_encipher(const unsigned char *plaintext, unsigned char *ciphertext, unsigned char *key);

返回值:无

描述采用DES算法,将明文plaintext的一个64位的明文组加密。在key中指定64位的密钥(最后8位将被忽略掉,实际得到56位的密钥)。ciphertext是返回的64位的密文组。

由调用者负责管理ciphertext所需要的空间。要加密一段较大的数据,可以按照分组加密模式调用des_encipher。为了得到较高的效率,des_encipher可以重用之前的调用中计算出来的子密钥,这可以通过在之后的调用中将NULL传给key,以此来开启这种功能。

复杂度:O(1)

des_decipher


void des_decipher(const unsigned char *ciphertext, unsigned char *plaintext, unsigned char *key);

返回值:无

描述采用DES算法将密文ciphertext的一个64位分组解密。该函数假设ciphertext包含的是通过des_encipher加密过的密文。在key中指定64位的密钥(最后8位将被忽略掉,实际得到56位的密钥)。plaintext是返回的64位的明文组。由调用者负责管理plaintext所需要的空间。要解密一大段的数据,可以按照分组加密模式调用des_decipher。为了获得较高的效率,des_decipher可以重用之前调用中计算出来的子密钥。可以在随后的调用中将NULL传给key,以此来开启这种功能。

复杂度:O(1)

DES算法的实现

考虑到DES算法中涉及的位操作很多,因此DES算法通常都是在硬件中实现。DES算法中的图表和术语(通过线、框画的流程图,以及诸如S盒、P盒这样的术语)使其更倾向于在硬件中实现,当然,软件实现也有它的价值所在。在软件开发中,通过几种基本的指令操作来帮助实现DES中的各种置换、转换以及替换操作都是很有效的。

des_encipher

函数des_encipher将明文的一个64位的明文分组通过DES算法加密。

由于DES的一个很好的特点是同样的过程既能用来加密数据也能用来解密数据,因此des_encipher只需要简单的调用des_main,而des_decipher同样也只需要调用des_main即可。

函数des_main通过使用其参数direction来确定到参数source提供的数据是明文还是密文。direction参数只是简单地修改子密钥的顺序。

在des_encipher中,direction设置为encipher

函数des_main()首先检测参数key是否为NULL。这将允许des_encipher的调用者重用上一次调用时计算出来的子密钥。将子密钥数组subkeys声明为static类型。如果key不为NULL,将计算子密钥。

要计算子密钥,可以按照前面介绍过的步骤(计算16个子密钥)来执行。key的转换是通过函数permute来实现的这里就是根据一个特定的表在一个缓冲区中置换位。假设表中的每个位置(i)上都存在一个值p,函数permute通过将位置p的位移动到位置(i)上来完成对传入的buffer的置换。

要置换密钥,将表Des_Transform(同表1)传给函数permute。必要的旋转操作可以通过调用位操作bit_rot_left来实现。该操作将buffer按照指定的位数向左旋转。每一轮要正确旋转28位的子密钥分组,将表Des_Rotations(同表2)中合适的元素传给bit_rot_left。通过调用permute,并把传入表Des_permuted(同表3),来对每一个子密钥做置换选择。

要加密一个数据块,首先要执行初始置换。为实现这一步,首先调用函数permute并将表Des_Initial(同表4)传入。然后,将数据分为两个32位的分组:lblk以及rblk。回顾一下,加密数据的大部分工作都是将一系列的操作重复执行16轮。每一轮的主要工作都花在计算函数(f)的值上,将值保存在fblk中。

每一轮操作开始时,将对rblk执行一个扩展置换。为了实现这个步骤,将调用函数permute,并把表Des_Expansion(同表5)传入。然后,通过调用位操作bit_xor来计算扩展后的rblk和某个适当的子密钥的异或值。无论是加密还是解密数据,与本轮操作相关的子密钥都需要参与执行。一旦完成了异或计算,将对结果执行一系列的S盒替换操作。Des_Sbox(同表6)定义了8个用于DES算法中的S盒。对于当前fblk中的每个6位分组,第1位和最后1位联合起来确定Des_Sbox中的行号,而中间的4位用来确定列号。最后执行P盒置换来完成函数f的计算。通过调用permute函数并传入表Des_Pbox(同表7)来实现这个步骤。计算出lblk与函数f的值的异或结果,并交换lblk和rblk来结束一轮的操作。

将上述过程重复16次,每轮一次。当全部16轮操作完成后,将rblk拷贝到target的前32位中,将lblk拷贝到之后的32位中(按照要求,最后一轮不交换lblk和rblk)。最终,通过调用permute并把Des_Final(同表8)传入来完成最后的置换操作。

des_encipher的时间复杂度是O(1),因为加密数据块的所有步骤都能在恒定的时间内完成。

des_decipher

函数des_decipher将一个64位的密文分组通过DES算法进行解密。同des_encipher一样,des_decipher实际通过调用des_main来完成解密任务,但这里direction需要设置为decipher。因此,des_decipher的工作方式同des_encipher一样,只是这里的子密钥需要以逆序的方式参与。具体来说,就是在des_main中,针对每一轮i(从0开始计数),参与计算的子密钥为subkeys数组中下标为(15-i)的元素。

des_decipher的时间复杂度为O(1),因为解密数据块中的所有步骤都可以在恒定的时间内完成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值