□宣克祥
【摘要】数据加密标准(Data Encryption Standard,DES)自诞生以来,为维护信息安全发挥了十分重要的作用。DES算法是典型的分组对称式私钥密码机制。本文对DES算法的具体实现过程进行深入探讨,并对DES算法的安全性问题进行详细分析。
【关键词】DES算法;加密标准;信息安全
【作者单位】宣克祥,解放军国际关系学院
数据加密标准(Data Encryption Standard,DES)是由IBM 公司研制的一种加密算法,美国国家标准局于1977年将它作为正式数据加密标准予以颁布。几十年来,在银行ATM 机、商用POS机、加油站、高速公路收费站、金融交易数据包的MAC校验等领域,DES加密算法被广泛应用,以实现数据保密和安全。
一、DES算法加密过程
DES算法是一种典型的分组对称式私钥密码机制。DES 算法每次以64位数据块为加密对象,进行16轮加密编码,是一个分组加密算法;同时,DES算法加密和解密所用密钥相同,加密与解密操作互逆;DES算法密钥需要保护,数据的安全性依赖于密钥的保密性。DES算法加密过程分为三个阶段:第一阶段将8字节明文转换为64比特数据串,对数据串进行初始置换,并分为左右32位;第二阶段分别对左右32位数据进行16轮编码以加密;第三阶段对加密后的数据进行最后置换,并转换成8字节密文输出。 (一)初始置换(Initial Permutation)。先通过以下函数将8字节明文数据转换为64比特数据串:
//字节转化为位
void ByteToBit(bool*Out,const char*In,int bits)
{
for(int i=0;i<bits;++i)
Out[i]=(In[i>>3]>>(i&7))&1;
}
然后按照下表顺序,将64位明文数据串重新排列:
const static char IP_Table[64]=
{
58,50,42,34,26,18,10,2,60,52,44,36,28,20,12,4,
62,54,46,38,30,22,14,6,64,56,48,40,32,24,16,8,
57,49,41,33,25,17,9,1,59,51,43,35,27,19,11,3,
61,53,45,37,29,21,13,5,63,55,47,39,31,23,15,7
};
数据转换函数为:
void Transform(bool*Out,bool*In,const char*Ta-ble,int len)
{
/
/len是输出数组元素的数量
for(int i=0;i<len;++i)
//Table[i]实际上顺序编号,最小为1,减1后即成为输入数组的下标
Tmp[i]=In[Table[i]-1];
//转换后一次性拷入(输出)
memcpy(Out,Tmp,len);
}
按照上表,新串的第0位为原串的第57位,新串的第1位为原串的第49位,新串的第2位为原串的第41位,如此类推,新串的第63位为原串的第6位,形成新的64比特数据串。
(二)16轮编码阶段。具体过程如图1所示
:
图116轮编码具体过程
由图1可见,
16轮编码可以分为6个步骤:①经置换后的64比特数据串分成左右32位后,先将右32位备份至tmp ,memcpy (tmp ,Ri ,32),再依据下表将右32位扩充成48位。
static const char E_Table [48]={
32,1,2,3,4,5,4,5,6,7,8,9,
8,9,10,11,12,13,12,13,14,15,16,17,16,17,18,19,20,21,20,21,22,23,24,25,24,25,26,27,28,29,28,29,30,31,32,1};
扩充所用函数为Transform (MR ,In ,E_Table ,48),操作
原理同前,
48位新串中的第0位为原32比特串中的第31位,新串的第1位为原串的第0位,如此类推,新串的第47位
为原串的第0位。
②将扩充后的右48位与一组48位子密钥进行异或运算,运算结果保存在InA 中。DES 算法使用8字节64位密钥串,经过变换成为16组48位密钥串,每轮使用一组48位子密钥串。异或运算实现代码如下:
//位异或运算:位值只能是0或1,可表示为bool 类型void Xor (bool *InA ,const bool *InB ,int len ){
//len 为总位数
for (int i =0;i <len ;++i )//各位计算结果保存在InA [i ]中InA [i ]^=InB [i ];}
③将右48位数据转换为32位数据。将右48位数据分成8组,
每6位作为一组输入,其中第1位和第6位用于确定行索引,第2-5位用于确定列索引,根据组、行和列三维索
引,顺序从下表中选取一个数字,输出一个4位比特串,
8组共输出32位。
const static char S_Box [8][4][16]={//模式1
14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7,0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8,4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0,15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13,//模式2
15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10,3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5,0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15,13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9,//模式3
10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8,13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1,13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7,1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12,//模式4
7,
13,14,3,0,6,9,10,1,2,8,5,11,12,4,15,13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9,10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4,3,15,0,
6,10,1,13,8,9,4,5,11,12,7,2,14,//模式5
2,12,4,1,7,10,11,6,8,5,3,15,13,0,14,9,14,11,2,12,4,7,13,1,5,0,15,10,3,9,8,6,4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14,11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3,//模式6
12,
1,10,15,9,2,6,8,0,13,3,4,14,7,5,11,10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8,9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6,4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13,//模式7
4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1,13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6,1,4,11,13,12,3,7,14,10,15,6,8,0,5,9,2,6,11,13,8,1,4,10,7,9,5,0,15,14,2,3,12,//模式8
13,2,8,4,6,15,11,1,10,9,3,14,5,0,12,7,1,15,13,8,10,3,7,4,12,5,6,11,0,14,9,2,7,11,4,1,9,12,14,2,0,6,10,13,15,3,5,8,2,1,14,7,4,10,8,13,15,12,9,0,3,5,6,11};
右48位输入替换为32位输出具体操作函数如下:
void S_func (bool Out [32],const bool In [48]){
//8轮替换,每轮6位输入,4位输出for (char i =0,j ,k ;i <8;++i ,In +=6,Out +=4){
//第1和第6位作为行号j =(In [0]<<1)+In [5];
//第2-5位作为列号k =(In [1]<<3)+(In [2]<<2)+(In [3]<<1)+In [4];
//依据i 、行、列三维,从表中选取一个0-15之间的十进制数
//转换成4位二进制数输出ByteToBit (Out ,&S_Box [i ][j ][k ],4);}}
④依据下表中元素的顺序,用函数Transform (In ,In ,P_Table ,32)将右32比特串重新排序,操作原理同前。
const static char P_Table [32]={
16,7,20,21,29,12,28,17,1,15,23,26,5,18,31,10,
2,8,24,14,32,27,3,9,19,13,30,6,22,11,4,25};
⑤再将排序后的新右32位与原左32位进行异或运算,生成新右32位。具体操作函数为Xor (Ri ,Li ,32),原理与
步骤②相同,运算结果保存在右32位Ri 中。
⑥把先前备份的右32位(tmp )直接变成新的左32位,memcpy (Li ,tmp ,32)。这样,本轮编码就结束了。将所得到的64位编码经过16轮同样的循环,整个第二阶段编码即全部结束。(三)最后置换(Final Permutation )。依据下表,对第二阶段得到的64位数据串进行置换,生成的比特串就是密文,具体操作为Transform (M ,M ,IPR_Table ,
64)。const static char IPR_Table [64]=
{
40,8,48,16,56,24,64,32,39,7,47,15,55,23,63,31,
38,6,46,14,54,22,62,30,37,5,45,13,53,21,61,29,
36,4,44,12,52,20,60,28,35,3,43,11,51,19,59,27,
34,2,42,10,50,18,58,26,33,1,41,9,49,17,57,25
};
最后用函数BitToByte (Out ,M ,64)将64比特串密文转换为8字节输出。
//位转化为字节
void BitToByte (char *Out ,const bool *In ,int bits ){
memset (Out ,0,bits >>3);for (int i =0;i <bits ;++i )Out [i >>3]|=In [i ]<<(i&7);}
二、
DES 算法解密过程DES 算法对数据的解密是加密的逆过程,也可以分为三个阶段:初始置换阶段、
16轮编码阶段和最后置换阶段。(一)初始置换。解密时的初始置换实际上是对加密时最后置换的还原。值得注意的是,表IP_Table [64]与表IPR_
Table [64]是互逆的。例如:在加密时的最后置换阶段,新串的第0位为原串的第39位,新串的第1位为原串的第7位,新串的第2位为原串的第47位,如此类推,新串的第63位为原串的第24位;在解密时的初始置换阶段,新串的第39位为原串的第0位,新串的第7位为原串的第1位,新串的第47位为原串的第2位,新串的第24位为原串的第63位,与加密时最后置换对应类推。
(二)进行16轮编码。解密就是要恢复原左32位和原右32位数据。从加密过程可以看出,新左32位即是原右32位,新右32位是原右32位(即新左32位)经过一系运算(①②③④)后与原左32位进行异或运算(⑤)得到的,要恢复原左32位,
必须首先得到原右32位经过一系运算后的结果即第④之后的结果,把第④之后的结果与新右32位进行一次异或运算即可还原原左32位数据。这样经过16轮解码,加上最后置换,原64位数据就全部恢复了。解密过程如下图所示:
解密操作的具体6个步骤如下:
①将新左32位(即原右32位)备份至tmp ,
memcpy (tmp ,Li ,32),再依据E_Table 将其扩充成48位。
②将扩充后的48位与48位子密钥进行异或运算。解密所使用的密钥与加密相同,但是,密匙使用的次序相反,即最后一轮用于加密的密钥串用于第一轮解密,如果各轮加密密匙分别是K 0,K 1,K 2……K 15,那么各轮解密密匙就分别是K 15,K 14,K 13……K 0
。
③下面进入替换操作:将异或运算后的48位数据分成8组,每6位作为一组输入,其中第1位和第6位用于确定行索引,第2-5位用于确定列索引,根据组、行和列三维索引,
顺序从S_Box [8][4][16]表中选取一个数字,输出一个4位(4位ˑ8种=32位)的比特串,8组共输出32位。
④依据表P_Table [32]中的顺序,将得到的32比特串重
新排序。
⑤再将排序后的32位数据串与新右32位进行异或运算,
生成原左32位。与加密时相反,此时函数Xor (Li ,Ri ,32)运算结果存入左32位Li 中,这是关键解密步骤。
⑥把先前备份的新左32位(tmp )直接变成原右32位,memcpy (Ri ,tmp ,32)。在这一阶段的16轮编码过程中,第②步使用的密码顺序与加密时相反,第⑤步是加密时异或运算的逆运算,其它步骤进行的操作与加密时基本相同。
(三)最后置换。解密时的最后置换实际上是对加密时初始置换的还原。IP 表和IPR 表不是随意生成的,
两表中的数据排列方式是经过精心设计的,两表密切关联,具有将各位数据进行互逆置换的功能。例如:新串的第57为原串的第0位,新串的第49位为原串的第1位,新串的第41为原串的第2位,新串的第6位为原串的第63位,与加密时初始置换对应类推。
三、
DES 算法密钥的生成DES 算法对密钥的处理过程为:首先将64位密钥转换成56位密钥,再通过置换和移位,将密钥转换成48位,共有16组,然后再分别与明文或密文数据进行异或运算以加密或
解密。因此,
DES 算法实际使用的是16组经过变换的48位密钥。以下是密钥的生成过程示意图:
64比特的密钥K ,经过PC_1块(Permuted Choice 1,交
换选择1)转换后,生成56比特的密钥串。密钥串各比特由函数Transform (bool *Out ,bool *In ,const char *Table ,int len )按照表PC1_Table [56]从64位密钥中选取:
void Transform (bool *Out ,bool *In ,const char *Ta-ble ,int len )
{
for (int i =0;i <len ;++i )Tmp [i ]=In [Table [i ]-1];memcpy (Out ,Tmp ,len );}
//56位密钥转换表(permuted choice table (key ))
const static char PC1_Table [56]={
57,49,41,33,25,17,9,1,58,50,42,34,26,18,10,2,59,51,43,35,27,19,11,3,60,52,44,36,63,55,47,39,31,23,15,7,62,54,46,38,30,22,14,6,61,53,45,37,29,21,13,5,28,20,12,4};
将56位密钥串分成两个长度为28比特的串L0和R0,然后L0和R0分别向左移位。每组密钥向左移位多少是不同的,具体由函数RotateL (bool *In ,int len ,int loop )按照表LOOP_Table [16]操作:
void RotateL (bool *In ,int len ,int loop ){
//将左loop 位数据暂时存入Tmp memcpy (Tmp ,In ,loop );
//将从左第In +loop 位起的数据存入In memcpy (In ,In +loop ,len -loop );
//将Tmp 中的loop 位数据存入In +len -loop 起的位置memcpy (In +len -loop ,Tmp ,loop );}
//16轮密钥左移位数表(number left rotations of pc1)
const static char LOOP_Table [16]={
1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1};
将得到的L1和R1合并起来,经过PC_2块(Permuted Choice 2,交换选择2)变换后,生成第1组48比特的子密钥K0。密钥串各比特由函数Transform ((*pSubKey )[i ],K ,PC2_Table ,48)按照表PC2_Table [48]从56位密钥中选取:
//48位密钥转换表(permuted choice key (table ))
const static char PC2_Table [48]={
14,17,11,24,1,5,3,28,15,6,21,10,23,19,12,4,26,8,16,7,27,20,13,2,41,52,31,37,47,55,30,40,51,45,33,48,44,49,39,56,34,53,46,42,50,36,29,32};
之后,
L1、R1分别循环左移后合并产生另一个56比特串,经过PC_2变换,再生成第2组48比特的子密钥K1……依次类推,直至生成16组48位的子密钥K15。以下是子密钥生成程序:
void SetSubKey (PSubKey pSubKey ,const char Key [8]){
//KL 指向左28位的第1位,KR 指向右28位的第1位static bool K [64],*KL =&K [0],*KR =&K [28];//将密钥由字节转变成64位数据K ByteToBit (K ,Key ,64);
//依据PC1_Table ,将64位数据K 转变成56位数据K Transform (K ,K ,PC1_Table ,56);//16轮变换:每轮中KL 指向左28位,KR 指向右28位,合并为48位并存入K
//pSubKey [i ]二维数组指针指向各轮变换后形成的16个48位密钥,备用
for (int i =0;i <16;++i )
{
RotateL (KL ,28,LOOP_Table [i ]);RotateL (KR ,28,LOOP_Table [i ]);
//依据PC2_Table ,从56比特串中选取48比特组成密钥
串Transform ((*pSubKey )[
i ],K ,PC2_Table ,48);}
}
最后,将由PC_2块得到的16组48位密钥串存入(*
PSubKey )[16][48]数组中,在加密和解密时使用。
四、
DES 算法安全性分析DES 算法最重要的特征之一就是输入与输出之间的非线性映射。DES 通过一系列操作使输出成为输入的非线性函数,换位扩展了输出对输入的多位依赖性。DES 算法是对数据在位层次上进行操作,其首尾的数据置换、中间的数据扩充和替换等过程设计十分精妙,加密功能相当强
大,能够有效地保障数据的安全性。DES 加密算法的安全性主要通过以下特征表现出来:
(一)DES 算法在16轮编码阶段,每轮加密和解密所用的密钥都不相同。我们知道,在经典加密法中,密钥自始自终是不变的,而现代加密法的密钥是变化的,这是现代加密法与经典加密法一个很大的区别。DES 一共产生16组48位密钥,每轮都用一组不同的48位密钥对明文进行异或运算,
再对运算结果进行替换操作。尽管所有64位数据块都使用同样的16组48位密钥,但对于同一个64位明文数据块而言,由于密钥不断变化,与明文运算的结果也会变得更加难以预测,由明文推测密钥的可能性就会大大降低。(二)按照索引对数据进行替换操作。在按照8种模式
对数据进行替换阶段,
DES 算法把明文数据转换为索引,然后依据索引从8种模式中选取0-15之间的一个十进制数,
并把它转换成4比特的二进制数输出。这里输出的已经不是经过变换的明文数据本身了,而是8种模式中按照特定方
式排列的0-15之间的一个十进制数,这就是“替换”的真正涵义所在。
(三)各种模式的行与列构成一种非线性映射关系。非线性特征表明,
不存在如下形式的线性函数:y =ax +b 。其中a 和b 是常量,x 为列号,y 为输出值。如模式1的第1行,给定
一个x 值的集合,是很难预计所输出值y 的。以第1种模式第一行为例,各列与其对应值的非线性映射示意图如下
:
各列与其对应值之间的变化不构成一条直线或平滑的曲
线,表明其对应关系是非线性的,不存在规律可循,这使得要根据行和列去预测可能出现的值成为不能。只有待选数据的排列达到了较高的随机性要求,数据的安全性才有可靠保障。
(四)输出与输入之间保持较高的无规律性。如果改变一个输入位,
至少会改变两个输出位。输入发生小的改变,输出将产生很大的改变,这对维护加密法的安全性是十分重
要的。假如在模式1时的输入为010010,第1位和第6位为0和0,运算后为0,表明为行0;第2、3、4、5位分别是1、0、0、1,运算后为1001,表明为列9。以此取值应为10,二进制为1010。如果将输入改变为110010,那么输出位于行2列9,取
值应为12,
二进制为1100。可以发现,两次输出中间两位发生了改变。仔细观察一下8种模式中的各项数据,左右数据相差小于、
等于1的两个数据基本不会排列在一起,上下数据相同或相差小于、等于1的两个数据也基本不会排列在一
起,紧挨着的两个数据一般差值都比较大。这样,当行或列索引增减1时,替换到的数据就会出现很大的不同。输入与输出之间反差越大,寻输入与输出规律的难度就越大,对算法的具体分析就越困难,攻击与破解的难度也就越大。
DES 算法诞生后近二十年里,很多人尝试着破解DES 密钥,攻击取得了不同程度的进展,但并没有真正取得成功。直到1997年7月,借助因特网上14000多台计算机,花了90天时间才破解了DES 密钥。6个月后,破解DES 密钥的时间减少到了39天。1998年7月,一台具有特殊构造的计算机
(Deep Crack )只用了56个小时就破解了一个DES 算法的密
钥。尽管如此,要破解DES 密钥或明文,要求提供大量的参考数据,包括大量的已知明文-密文对,而这些条件在通常情况下是不具备的。当然,由于某些密钥不受循环移位的影响,对其循环移位只能得到相同的子密钥,所以,由64位0或1组成
的密钥显然是脆弱的。此外,
因为密钥要被分为两个部分,两部分都独立进行移位,所以每个部分都不应当全为0或1。
为了提高DES 算法的安全性,可以通过执行三次DES 算法的方法来增加加密强度,这就是三重数据加密算法(Triple Data Encryption Algorithm ,简称TDEA )。TDEA 密钥由三个64比特密钥K a 、K
b 、K c 组成,称为密钥束。TDEA 加密步骤如下:
①使用密钥K a 对明文进行DES 加密;
②使用密钥K b 对加密结果进行DES 解密;③使用密钥K c 对解密结果再进行DES 加密。TDEA 解密步骤如下:
①使用密钥K c 进行DES 解密;
②使用密钥K b 对解密结果进行DES 加密;③使用密钥K a 对加密结果再进行DES 解密。TDEA 算法这种加密-解密-加密的模式可简写为EDE (Encrypt -Decrypt -Encrypt ),具体实现代码为:
if (!Is3DES )//单次DES
SDES (Out ,In ,&SubKey [0],Type );Else //三重DES
{
//加密:加(key0)-解(key1)-加(key0)//解密:解(key0)-加(key1)-解(key0)
//因此,若密钥为1234567812345678,也就是SubKey [0]与SubKey [1]相同
//则等于是用12345678进行的单次DES 加密和解密SDES (Out ,In ,&SubKey [0],Type );
SDES (Out ,Out ,&SubKey [1],Type );SDES (Out ,Out ,&SubKey [0],Type );}
可以看出,TDEA 第1和第3组密钥是相同的,第2组不同,实际上只有两组密钥。随着密码技术的发展,三重数据加密算法也变得不安全了。1999年,美国国家标准与技术局
发出通告,
要求开发新的加密标准。从提交的多种算法中,比利时密码学家Joan Daemen 和Vincent Rijmen 设计的Rijn-dael 被评为获胜者,这就是将要逐步取代DES 算法的高级加
密标准(Advanced Encryption Standard ,
AES )。本文中所有VC ++程序均已经过作者调试验证,完整
的源程序可以提供给对DES 算法研究感兴趣的读者。【参考文献】1.叶远健,曹英,张长富译.[美]Richard Spillman.经典密码
学与现代密码学[
M ].北京:清华大学出版社,20052.蔡建,梁志敏译.[美]David Salomon.数据保密与安全
[M ].北京:清华大学出版社,20053.邱仲潘译.[印]Atul Kahate.密码学与网络安全[M ].北京:清华大学出版社,
2005