【声明】

(1) 本文源码

大部分源码来自:DES算法代码。在此基础上,利用Qt编程进行了改写,实现了DES加解密算法,并添加了文件加解密功能。在此对署名为bengold1979的网友表示感谢!本文是对DES算法代码一文代码的具体描述。该源码仅供学习交流,请勿用于商业目的。

(2) 图片及描述

图片及部分解析来自

http://zh.wikipedia.org/wiki/%E8%B3%87%E6%96%99%E5%8A%A0%E5%AF%86%E6%A8%99%E6%BA%96

【简介】

DES(Data Encryption Standard,资料加密标准),是一种使用密钥加密的块密码,1976年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),随后在国际上广泛流传开来。它基于使用56位密钥的对称算法。与其它块密码相似,DES自身并不是加密的实用手段,而必须以某种工作模式进行实际操作。FIPS-81确定了DES使用的几种模式[32] 。FIPS-74包括了更多关于DES使用的讨论。

【算法描述】

DES是一种典型的块密码—一种将固定长度的平文通过一系列复杂的操作变成同样长度的密文的算法。对DES而言,块长度为64位。同时,DES使用密钥来自定义变换过程,因此算法认为只有持有加密所用的密钥的用户才能解密密文。密钥表面上是64位的,然而只有其中的56位被实际用于算法,其余8位可以被用于奇偶校验,并在算法中被丢弃。因此,DES的有效密钥长度为56位,通常称DES的密钥长度为56位。
DES算法整体结构图,如图1所示:

图1 DES整体结构图

有16个相同的处理过程,称为“回次”(round),并在首位各有一次置换,称为IP与FP(或称IP-1,FP为IP的反函数吗,即IP“撤销”FP的操作,反之亦然)。IP和FP几乎没有密码学上的重要性,为了在1970年代中期的硬件上简化输入输出数据库的过程而被显式的包括在标准中。
在主处理回次前,数据块被分成两个32位的半块,并被分别处理;这种交叉的方式被称为费斯妥结构。费斯妥结构保证了加密和解密过程足够相似—唯一的区别在于子密钥在解密时是以反向的顺序应用的,而剩余部分均相同。这样的设计大大简化了算法的实现,尤其是硬件实现,因为没有区分加密和解密算法的需要。
图中的⊕符号代表异或(XOR)操作。“F函数”将数据半块与某个子密钥进行处理。然后,一个F函数的输出与另一个半块异或之后,再与原本的半块组合并交换顺序,进入下一个回次的处理。在最后一个回次完成时,两个半块不必交换顺序,这是费斯妥结构的一个特点,以保证加解密的过程相似。

【算法解析】

1 常量定义
下述定义,实际上是一个矢量,而非矩阵。详细请参考:

http://zh.wikipedia.org/wiki/DES%E8%A1%A5%E5%85%85%E6%9D%90%E6%96%99#.E9.80.89.E6.8B.A9.E7.BD.AE.E6.8D.A22.28PC-2.29

IP表

  1. const int DES::IP_Table[] =
  2. {
  3. 58, 50, 42, 34, 26, 18, 10, 2,  60, 52, 44, 36, 28, 20, 12, 4,
  4. 62, 54, 46, 38, 30, 22, 14, 6,  64, 56, 48, 40, 32, 24, 16, 8,
  5. 57, 49, 41, 33, 25, 17, 9,  1,  59, 51, 43, 35, 27, 19, 11, 3,
  6. 61, 53, 45, 37, 29, 21, 13, 5,  63, 55, 47, 39, 31, 23, 15, 7,
  7. };
  8. // final permutation IP^-1
  9. const int DES::IPR_Table[] =
  10. {
  11. 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31,
  12. 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
  13. 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
  14. 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25
  15. };

Expand Array

  1. const int DES::E_Table[48] =
  2. {
  3. 32,1,2,3,4,5,
  4. 4,5,6,7,8,9,
  5. 8,9,10,11,12,13,
  6. 12,13,14,15,16,17,
  7. 16,17,18,19,20,21,
  8. 20,21,22,23,24,25,
  9. 24,25,26,27,28,29,
  10. 28,29,30,31,32,1
  11. };

P转换表

  1. const int DES::P_Table[32] =
  2. {
  3. 16,7,20,21,
  4. 29,12,28,17,
  5. 1,15,23,26,
  6. 5,18,31,10,
  7. 2,8,24,14,
  8. 32,27,3,9,
  9. 19,13,30,6,
  10. 22,11,4,25
  11. };

PC1表

  1. const int DES::PC1_Table[56] =
  2. {
  3. 57,49,41,33,25,17,9,
  4. 1,58,50,42,34,26,18,
  5. 10,2,59,51,43,35,27,
  6. 19,11,3,60,52,44,36,
  7. 63,55,47,39,31,33,15,
  8. 7,62,54,46,38,30,22,
  9. 14,6,61,53,45,37,29,
  10. 21,13,5,28,20,12,4
  11. };

PC2表

  1. const int DES::PC2_Table[48] =
  2. {
  3. 14,17,11,24,1,5,
  4. 3,28,15,6,21,10,
  5. 23,19,12,4,26,8,
  6. 16,7,27,20,13,2,
  7. 41,52,31,37,47,55,
  8. 30,40,51,45,33,48,
  9. 44,49,39,56,34,53,
  10. 46,42,50,36,29,32
  11. };

循环移位表

  1. const int DES::LOOP_Table[16] =
  2. {
  3. 1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1
  4. };

S-Box

  1. const int DES::S_Box[8][4][16] =
  2. {
  3. {
  4. {14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7},
  5. {0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8},
  6. {4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0},
  7. {15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13}
  8. },
  9. {
  10. {15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10},
  11. {3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5},
  12. {0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15},
  13. {13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9}
  14. },
  15. {
  16. {10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8},
  17. {13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1},
  18. {13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7},
  19. {1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12}
  20. },
  21. {
  22. {7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15},
  23. {13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9},
  24. {10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4},
  25. {3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14}
  26. },
  27. {
  28. {2,12,4,1,7,10,11,6,8,5,3,15,13,0,14,9},
  29. {14,11,2,12,4,7,13,1,5,0,15,10,3,9,8,6},
  30. {4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14},
  31. {11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3}
  32. },
  33. {
  34. {12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11},
  35. {10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8},
  36. {9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6},
  37. {4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13}
  38. },
  39. {
  40. {4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1},
  41. {13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6},
  42. {1,4,11,13,12,3,7,14,10,15,6,8,0,5,9,2},
  43. {6,11,13,8,1,4,10,7,9,5,0,15,14,2,3,12}
  44. },
  45. {
  46. {13,2,8,4,6,15,11,1,10,9,3,14,5,0,12,7},
  47. {1,15,13,8,10,3,7,4,12,5,6,11,0,14,9,2},
  48. {7,11,4,1,9,12,14,2,0,6,10,13,15,3,5,8},
  49. {2,1,14,7,4,10,8,13,15,12,9,0,3,5,6,11}
  50. }
  51. };

2 费斯妥函数(F函数)
图2中显示了费斯妥函数(F函数)的过程。其每次对半块(32位)进行操作,并包括四个步骤:

图2 费斯妥函数

(1) 扩张—用扩张置换(图中的E)将32位的半块扩展到48位,其输出包括8个6位的块,每块包含4位对应的输入位,加上两个邻接的块中紧邻的位。

  1. void DES::Transform(Bit *Out, Bit *In, const int *Table, int len)
  2. {
  3. bool Tmp[256];
  4. for(int i=0; i<len; ++i)
  5. Tmp[i] = In[ Table[i]-1 ];
  6. memcpy(Out, Tmp, len);
  7. }
  8. Transform(MR, In, E_Table, 48);

(2) 与密钥混合—用异或操作将扩张的结果和一个子密钥进行混合。16个48位的子密钥—每个用于一个回次的F变换—是利用密钥调度从主密钥生成的(见下文)。

  1. void DES::Xor(Bit *InA, const Bit *InB, int len)
  2. {
  3. for(int i = 0; i < len; ++i)
  4. InA[i] ^= InB[i];
  5. }

(3) S盒—在与子密钥混合之后,块被分成8个6位的块,然后使用“S盒”,或称“置换盒”进行处理。8个S盒的每一个都使用以查找表方式提供的非线性的变换将它的6个输入位变成4个输出位。S盒提供了DES的核心安全性—如果没有S盒,密码会是线性的,很容易破解。

  1. void DES::S_func(Bit Out[], const Bit In[])
  2. {
  3. for(int i=0,j,k; i<8; ++i,In+=6,Out+=4)
  4. {
  5. j = (In[0]<<1) + In[5];
  6. k = (In[1]<<3) + (In[2]<<2) + (In[3]<<1) + In[4]; //组织SID下标
  7. for(int l=0; l<4; ++l)                               //把相应4bit赋值
  8. Out[l] = (S_Box[i][j][k]>>(3 - l)) & 1;
  9. }
  10. }

(4) 置换—最后,S盒的32个输出位利用固定的置换,“P置换”进行重组。这个设计是为了将每个S盒的4位输出在下一回次的扩张后,使用4个不同的S盒进行处理。S盒,P置换和E扩张各自满足了克劳德•香农在1940年代提出的实用密码所需的必要条件,“混淆和扩散”。

  1. void DES::F_func(Bit In[32], const Bit Ki[48])
  2. {
  3. bool MR[48];
  4. Transform(MR, In, E_Table, 48);
  5. Xor(MR, Ki, 48);
  6. S_func(In, MR);
  7. Transform(In, In, P_Table, 32);
  8. }

3 密钥调度
如图3所示,首先,使用选择置换1(PC-1)从64位输入密钥中选出56位的密钥—剩下的8位要么直接丢弃,要么作为奇偶校验位。然后,56位分成两个28位的半密钥;每个半密钥接下来都被分别处理。在接下来的回次中,两个半密钥都被左移1或2位(由回次数决定),然后通过选择置换2(PC-2)产生48位的子密钥—每个半密钥24位。移位(图中由<<标示)表明每个子密钥中使用了不同的位,每个位大致在16个子密钥中的14个出现。

图3 密钥调度

  1. void DES::ByteToBit(Bit *Out, const unsigned char *In, int bits)
  2. {
  3. for(int i = 0; i < bits; ++i)
  4. Out[i] = (In[i>>3]>>(7 - (i&7))) & 1;
  5. }
  6. void DES::RotateL(Bit *In, int len, int loop)
  7. {
  8. Bit Tmp[256];
  9. memcpy(Tmp, In, loop);
  10. memcpy(In, In+loop, len-loop);
  11. memcpy(In+len-loop, Tmp, loop);
  12. }
  13. void DES::SetSubKey(PSubKey pSubKey, const unsigned char Key[])
  14. {
  15. bool K[64], *KL=&K[0], *KR=&K[28];
  16. ByteToBit(K, Key, 64);
  17. Transform(K, K, PC1_Table, 56);
  18. for(int i=0; i<16; ++i) {
  19. RotateL(KL, 28, LOOP_Table[i]);
  20. RotateL(KR, 28, LOOP_Table[i]);
  21. Transform((*pSubKey)[i], K, PC2_Table, 48);
  22. }
  23. }

4 明文填充
 DES加解密以8位为单位进行,如果输入明文不是8位,则利用填充函数进行填充为8的整数倍。

  1. bool DES::RunPad(int nType, const unsigned char *In, unsigned datalen, unsigned char *Out, unsigned &padlen)
  2. {
  3. if (nType < PAD_ISO_1 || nType > PAD_PKCS_7)
  4. return false;
  5. if (In == NULL || datalen < 0 || Out == NULL)
  6. return false;
  7. int res = (datalen & 0x00000007);
  8. if (res == 0)
  9. {
  10. padlen = datalen;
  11. memcpy(Out, In, datalen);
  12. return true;
  13. }
  14. padlen  =   (datalen+8-res);
  15. memcpy(Out,In,datalen);
  16. if(nType    ==  PAD_ISO_1)
  17. {
  18. memset(Out+datalen,0x00,8-res);
  19. }
  20. else if(nType   ==  PAD_ISO_2)
  21. {
  22. memset(Out+datalen,0x80,1);
  23. memset(Out+datalen,0x00,7-res);
  24. }
  25. else if(nType   ==  PAD_PKCS_7)
  26. {
  27. memset(Out+datalen,0x00,8-res);
  28. }
  29. else
  30. {
  31. return false;
  32. }
  33. return true;
  34. }

5 DES运算单元(详见【算法描述】)

  1. void DES::ByteToBit(Bit *Out, const unsigned char *In, int bits)
  2. {
  3. for(int i = 0; i < bits; ++i)
  4. Out[i] = (In[i>>3]>>(7 - (i&7))) & 1;
  5. }
  6. void DES::BitToByte(unsigned char *Out, const Bit *In, int bits)
  7. {
  8. memset(Out, 0, bits>>3);
  9. for(int i = 0; i < bits; ++i)
  10. Out[i>>3] |= In[i]<<(7 - (i&7));
  11. }
  12. void DES::DES_Unit(unsigned char Out[], const unsigned char In[], const PSubKey pSubKey, bool Type)
  13. {
  14. bool M[64], tmp[32], *Li=&M[0], *Ri=&M[32];
  15. ByteToBit(M, In, 64);
  16. Transform(M, M, IP_Table, 64);
  17. if( Type == ENCRYPT )
  18. {
  19. for(int i=0; i<16; ++i)
  20. {
  21. memcpy(tmp, Ri, 32);        //Ri[i-1] 保存
  22. F_func(Ri, (*pSubKey)[i]);  //Ri[i-1]经过转化和SBox输出为P
  23. Xor(Ri, Li, 32);            //Ri[i] = P XOR Li[i-1]
  24. memcpy(Li, tmp, 32);        //Li[i] = Ri[i-1]
  25. }
  26. }
  27. else
  28. {
  29. for(int i=15; i>=0; --i)
  30. {
  31. memcpy(tmp, Ri, 32);        //Ri[i-1] 保存
  32. F_func(Ri, (*pSubKey)[i]);  //Ri[i-1]经过转化和SBox输出为P
  33. Xor(Ri, Li, 32);            //Ri[i] = P XOR Li[i-1]
  34. memcpy(Li, tmp, 32);        //Li[i] = Ri[i-1]
  35. }
  36. }
  37. RotateL(M,64,32);                   //Ri与Li换位重组M
  38. Transform(M, M, IPR_Table, 64);     //最后结果进行转化
  39. BitToByte(Out, M, 64);              //组织成字符
  40. }

6 DES加解密
(1) DES和3DES
 DES,在加解密时,只运行DES运算单元一次。3DES,在加解密时,运行DES运算单元3次。
(2) 工作模式
(a) 电子密码本模式(EBC)
最古老,最简单的模式,将加密的数据分成若干组,每组的大小跟加密密钥长度相同;然后每组都用相同的密钥加密, 比如DES算法, 如果最后一个分组长度不够64位,要补齐64位。
定义
          Enc(X,Y)是加密函数
          Dec(X,Y)是解密函数
          Key是加密密钥;
          Pi ( i = 0,1…n)是明文块,大小为64bit;
          Ci ( i = 0,1…n)是密文块,大小为64bit;
ECB加密算法可表示为:
           Ci = Enc(Key, Pi)
ECB解密算法可以表示为:
          Pi = Dec(Key,Ci)

算法特点
 每次Key、明文、密文的长度都必须是64位; 
 数据块重复排序不需要检测; 
 相同的明文块(使用相同的密钥)产生相同的密文块,容易遭受字典攻击; 
 一个错误仅仅会对一个密文块产生影响。

(b) 加密块链模式(CBC)
与ECB模式最大的不同是加入了初始向量。
定义
           Enc(X,Y)是加密函数
           Dec(X,Y)是解密函数
           Key是加密密钥;
           Pi ( i = 0,1…n)是明文块,大小为64bit;
           Ci ( i = 0,1…n)是密文块,大小为64bit;
           XOR(X,Y)是异或运算;
           IV是初始向量(一般为64位);
    ECB加密算法可表示为:
          C0 = Enc(Key, XOR(IV, P0)
            Ci = Enc(Key, XOR(Ci-1, Pi)
    ECB解密算法可以表示为:
           P0 = XOR(IV, Dec(Key, C0))
            Pi = XOR(Ci-1, Dec(Key,Ci))
算法特点
 每次加密的密文长度为64位(8个字节);
 当相同的明文使用相同的密钥和初始向量的时候CBC模式总是产生相同的密文;
 密文块要依赖以前的操作结果,所以,密文块不能进行重新排列;
 可以使用不同的初始化向量来避免相同的明文产生相同的密文,一定程度上抵抗字典攻击;
 一个错误发生以后,当前和以后的密文都会被影响。

(c) 加密反馈模式(CFB)
加密反馈模式克服了需要等待8个字节才能加密的缺点,它采用了分组密码作为流密码的密钥流生成器。
定义
           Enc(X,Y)是加密函数
           Dec(X,Y)是解密函数
          Key是加密密钥;
           Pi ( i = 0,1…n)是明文块,大小为64bit;
           Ci ( i = 0,1…n)是密文块,大小为64bit;
            Si ( i = 0,1…n),大小为8bit,n个连续的Si组成加密位移寄存器,一般n=8;
           Oi = Enc(Key, Si);
           Lef(x) 为取数据x的最左8个bit位;
           A(x,y)为合并x左移8位,空位用y填充
     CFB加密算法可表示为:
           S0 = IV;
           Oi = Enc(Key, Si);
            Ci = XOR( Ci, Lef(Oi));
           Si = A(Si-1, Ci);
     CFB解密算法可表示为:
           S0 = IV;
           Oi = Enc(Key, Si);
           Ci = XOR( Ci, Lef(Oi));
           Si = A(Si-1, Ci);
     图示:

 
图4 CFB模式

算法特点
 每次加密的Pi和Ci不大于64位;
 加密算法和解密算法相同,不能适用于公钥算法;
 使用相同的密钥和初始向量的时候,相同明文使用CFB模式加密输出相同的密文;
 可以使用不同的初始化变量使相同的明文产生不同的密文,防止字典攻击;
 加密强度依赖于密钥长度;
 加密块长度过小时,会增加循环的数量,导致开销增加; 
 加密块长度应时8位的整数倍(即字节为单位);
 一旦某位数据出错,会影响目前和其后8个块的数据。

(d) 输出反馈模式(OFB)
与CFB模式不同之处在于, 加密位移寄存器与密文无关了,仅与加密key和加密算法有关;做法是不再把密文输入到加密移位寄存器,而是把输出的分组密文(Oi)输入到一位寄存器。
     定义
           Enc(X,Y)是加密函数
           Dec(X,Y)是解密函数
           Key是加密密钥;
           Pi ( i = 0,1…n)是明文块,大小为64bit;
           Ci ( i = 0,1…n)是密文块,大小为64bit;
           Si ( i = 0,1…n),大小为8bit,n个连续的Si组成加密位移寄存器,一般n=8;
           Oi = Enc(Key, Si);
           Lef(x) 为取数据x的最左8个bit位;
           A(x,y)为合并x左移8位,空位用y填充
    CFB加密算法可表示为:
           S0 = IV;
           Oi = Enc(Key, Si);
           Ci = XOR( Ci, Lef(Oi));
           Si = A(Si-1, Oi);          注意这里与CFB模式的不同
    CFB解密算法可表示为:
           S0 = IV;
           Oi = Enc(Key, Si);
           Ci = XOR( Ci, Lef(Oi));
           Si = A(Si-1, Oi);

算法特点
 与CFB类似,以下都是不同之处;
 因为密文没有参与链操作,所以使得OFB模式更容易受到攻击; 
 不会进行错误传播,某位密文发生错误,只会影响该位对应的明文,而不会影响别的位; 
 不是自同步的,如果加密和解密两个操作失去同步,那么系统需要重新初始化; 
 每次重新同步时,应使用不同的初始向量。可以避免产生相同的比特流,避免”已知明文”攻击。

  1. bool DES::RunDes(bool bType,bool bMode,const unsigned char *In,
  2. unsigned char *Out,unsigned datalen,const unsigned char *Key,unsigned keylen)
  3. {
  4. //判断输入合法性
  5. if(!(In && Out && Key && datalen && keylen>=8))
  6. return false;
  7. unsigned char inbuf[datalen + 8];
  8. memset(inbuf, 0x00, sizeof(inbuf));
  9. memcpy(inbuf, In, datalen);
  10. unsigned char* tempBuf;
  11. unsigned padlen = datalen;
  12. //只处理8的整数倍,不足长度自己填充
  13. if(datalen & 0x00000007)
  14. {
  15. if (!RunPad(PAD_PKCS_7, In, datalen, inbuf, padlen))
  16. return false;
  17. tempBuf = inbuf;
  18. }
  19. else
  20. {
  21. tempBuf = inbuf;
  22. }
  23. bool m_SubKey[3][16][48];       //秘钥
  24. //构造并生成SubKeys
  25. char nKey   =   (keylen>>3)>=3 ? 3: (keylen>>3);
  26. for(int i=0;i<nKey;i++)
  27. {
  28. SetSubKey(&m_SubKey[i],&Key[i<<3]);
  29. }
  30. if(bMode == ECB)    //ECB模式
  31. {
  32. if(nKey == 1)   //单Key
  33. {
  34. for(int i=0,j = padlen>>3;i<j;++i,Out+=8,tempBuf+=8)
  35. {
  36. DES_Unit(Out,tempBuf,&m_SubKey[0],bType);
  37. }
  38. }
  39. else
  40. if(nKey == 2)   //3DES 2Key
  41. {
  42. for(int i=0,j = padlen>>3;i<j;++i,Out+=8,tempBuf+=8)
  43. {
  44. DES_Unit(Out,tempBuf,&m_SubKey[0],bType);
  45. DES_Unit(Out,Out,&m_SubKey[1],!bType);
  46. DES_Unit(Out,Out,&m_SubKey[0],bType);
  47. }
  48. }
  49. else            //3DES 3Key
  50. {
  51. for(int i=0,j=padlen>>3;i<j;++i,Out+=8,tempBuf+=8)
  52. {
  53. DES_Unit(Out,tempBuf,&m_SubKey[bType? 2 : 0],bType);
  54. DES_Unit(Out,Out,&m_SubKey[1],!bType);
  55. DES_Unit(Out,Out,&m_SubKey[bType? 0 : 2],bType);
  56. }
  57. }
  58. }
  59. else                //CBC模式
  60. {
  61. unsigned char cvec[8] = ""; //扭转向量
  62. unsigned char cvin[8] = ""; //中间变量
  63. if(nKey == 1)   //单Key
  64. {
  65. for(int i=0,j=padlen>>3;i<j;++i,Out+=8,tempBuf+=8)
  66. {
  67. if(bType == ENCRYPT)
  68. {
  69. for(int j=0;j<8;++j)     //将输入与扭转变量异或
  70. {
  71. cvin[j] = tempBuf[j] ^ cvec[j];
  72. }
  73. }
  74. else
  75. {
  76. memcpy(cvin,tempBuf,8);
  77. }
  78. DES_Unit(Out,cvin,&m_SubKey[0],bType);
  79. if(bType == ENCRYPT)
  80. {
  81. memcpy(cvec,Out,8);         //将输出设定为扭转变量
  82. }
  83. else
  84. {
  85. for(int j=0;j<8;++j)     //将输出与扭转变量异或
  86. {
  87. Out[j]  =   Out[j] ^ cvec[j];
  88. }
  89. memcpy(cvec,cvin,8);            //将输入设定为扭转变量
  90. }
  91. }
  92. }
  93. else
  94. if(nKey == 2)   //3DES CBC 2Key
  95. {
  96. for(int i=0,j=padlen>>3;i<j;++i,Out+=8,tempBuf+=8)
  97. {
  98. if(bType == ENCRYPT)
  99. {
  100. for(int j=0;j<8;++j)     //将输入与扭转变量异或
  101. {
  102. cvin[j] =   tempBuf[j] ^ cvec[j];
  103. }
  104. }
  105. else
  106. {
  107. memcpy(cvin,tempBuf,8);
  108. }
  109. DES_Unit(Out,cvin,&m_SubKey[0],bType);
  110. DES_Unit(Out,Out,&m_SubKey[1],!bType);
  111. DES_Unit(Out,Out,&m_SubKey[0],bType);
  112. if(bType == ENCRYPT)
  113. {
  114. memcpy(cvec,Out,8);         //将输出设定为扭转变量
  115. }
  116. else
  117. {
  118. for(int j=0;j<8;++j)     //将输出与扭转变量异或
  119. {
  120. Out[j]  =   Out[j] ^ cvec[j];
  121. }
  122. memcpy(cvec,cvin,8);            //将输入设定为扭转变量
  123. }
  124. }
  125. }
  126. else            //3DES CBC 3Key
  127. {
  128. for(int i=0,j=padlen >>3;i<j;++i,Out+=8,tempBuf+=8)
  129. {
  130. if(bType == ENCRYPT)
  131. {
  132. for(int j=0;j<8;++j)     //将输入与扭转变量异或
  133. {
  134. cvin[j] = tempBuf[j] ^ cvec[j];
  135. }
  136. }
  137. else
  138. {
  139. memcpy(cvin,tempBuf,8);
  140. }
  141. DES_Unit(Out,cvin,&m_SubKey[bType ? 2 : 0],bType);
  142. DES_Unit(Out,Out,&m_SubKey[1],!bType);
  143. DES_Unit(Out,Out,&m_SubKey[bType ? 0 : 2],bType);
  144. if(bType == ENCRYPT)
  145. {
  146. memcpy(cvec,Out,8);         //将输出设定为扭转变量
  147. }
  148. else
  149. {
  150. for(int j=0;j<8;++j)     //将输出与扭转变量异或
  151. {
  152. Out[j] = Out[j] ^ cvec[j];
  153. }
  154. memcpy(cvec,cvin,8);            //将输入设定为扭转变量
  155. }
  156. }
  157. }
  158. }
  159. return true;
  160. }

【算法举例】

  1. bool DES::RunDesEncode(const unsigned char *In, unsigned char *Out, const unsigned char *Key,
  2. unsigned datalen, unsigned keylen, bool bMode)
  3. {
  4. return RunDes(ENCRYPT, bMode, In, Out, datalen, Key, keylen);
  5. }
  6. bool DES::RunDesDecode(const unsigned char* In, unsigned char* Out, const unsigned char* Key,
  7. unsigned datalen, unsigned keylen, bool bMode)
  8. {
  9. return RunDes(DECRYPT, bMode, In, Out, datalen, Key, keylen);
  10. }
  11. DES des;
  12. //-1-
  13. printf("-----------------------\n");
  14. unsigned char in1[]={'a','b','c','d','e','f','g','h'};
  15. unsigned char key1[24]={'q','w','r','t','y','u','0','b','v','s','a','z','l',';','y','5','?','3','n','m','3','0','a','q'};
  16. unsigned char out1[8];
  17. unsigned char result1[8];
  18. for(size_t i=0; i<sizeof(in1); i++)
  19. printf("%c", in1[i]);
  20. printf("\n");
  21. //des.RunDesEncode(in1, out1, key1, 8, 24, CBC);
  22. //des.RunDesEncode(in1, out1, key1, 8, 24);
  23. des.RunDesEncode(in1, out1, key1);
  24. for(size_t i=0; i<sizeof(out1); i++)
  25. printf("%c", out1[i]);
  26. printf("\n");
  27. //des.RunDesDecode(out1, result1, key1, 8, 24, CBC);
  28. //des.RunDesDecode(out1, result1, key1, 8, 24);
  29. des.RunDesDecode(out1, result1, key1);
  30. for(size_t i=0; i<sizeof(result1); i++)
  31. printf("%c", result1[i]);
  32. printf("\n");
  33. for(int i=0; i<8; i++)
  34. assert((in1[i] == result1[i]));

【文件加密】

本文仅分析DES算法本身,不就DES文件加密进行分析。源码中的文件加密功能仅供测试用。实际使用时,文件加密可使用多线程,要考虑加锁、明文字节填充等问题。

【源码下载】

http://download.csdn.net/detail/tandesir/4613526

【参考文献】

1http://zh.wikipedia.org/wiki/%E8%B3%87%E6%96%99%E5%8A%A0%E5%AF%86%E6%A8%99%E6%BA%96

http://blog.csdn.net/bengold1979/article/details/2208930

3  http://blog.csdn.net/iamfafa/article/details/6364369

DES加解密算法Qt实现的更多相关文章

  1. JavaScript与C#互通的DES加解密算法

    原文地址:传送门 本文提供了一个能使JavaScript与C#互通的DES加解密算法的实现,在前台页面中用JavaScript版本的DES算法将数据加密之后,传到服务器端,在服务器端可用C#版本的DE ...

  2. AES加解密算法Qt实现

    [声明] (1) 本文源码 在一位未署名网友源码基础上,利用Qt编程,实现了AES加解密算法,并添加了文件加解密功能.在此表示感谢!该源码仅供学习交流,请勿用于商业目的. (2) 图片及描述 除图1外 ...

  3. 实验一:C语言实现DES加解密算法

    计算程序执行10万次需要的时间: 总共需要175秒 加解密一次的时间小于:0.00175秒 纯计算加解密的时间会更短 去除IO操作后的时间 也就是说加解密一次的时间为0.07毫秒 /*-------- ...

  4. DES加解密算法(C语言实现)

    DES加密和解密算法的实现(C语言) 主要是做个记录,害怕以后代码丢了,先放到这里了. DES再不进行介绍了,可以看上一篇的 DES 的python实现 转载请注明出处:https://www.cnb ...

  5. 实现与JS相同的Des加解密算法【转】

    Java代码 import java.util.ArrayList; import java.util.List; /** * DES加密/解密 * * @Copyright Copyright (c ...

  6. Des加解密算法

    class DesHelper    {        /// <summary>        /// DES加密方法        /// </summary>       ...

  7. C#加解密算法

    先附上源码 加密解密算法目前已经应用到我们生活中的各个方面 加密用于达到以下目的: 保密性:帮助保护用户的标识或数据不被读取. 数据完整性:帮助保护数据不被更改. 身份验证:确保数据发自特定的一方. ...

  8. JavaScript与C#互通的DES加解密算法的实现(转)

    本文提供了一个能使JavaScript与C#互通的DES加解密算法的实现,在前台页面中用JavaScript版本的DES算法将数据加密之后,传到服务器端,在服务器端可用C#版本的DES解密算法将其解密 ...

  9. Node.js的DES加解密和MD5加密

    最基本的就是经常用的md5加密算法 代码如下 var  MD5=function (data) {        var _encrymd5 = require('crypto').createHas ...

随机推荐

  1. PHP输出中文乱码的问题(转)

    用echo输出的中文显示成乱码, 其实应该是各种服务器脚本都会遇到这个问题, 根本还是编码问题, 一般来说出于编码兼容考虑大多的页面都将页面字符集定义为utf-8 <meta http-equi ...

  2. 解析xml的几种方式

    http://blog.csdn.net/smcwwh/article/details/7183869

  3. 解决Cacti监控图像断断续续问题

    最近cacti的图像全都是断断续续.新加的设备,图像也是这样,查看cacti 的log发现大量下面类似的错误信息:04/12/2011 03:54:37 PM - SPINE: Poller[0] H ...

  4. 数据库和Doctrine(转载自http://www.111cn.net/phper/332/85987.htm)

    对于任何应用程序来说最为普遍最具挑战性的任务,就是从数据库中 读取和持久化数据信息.尽管symfony完整的框架没有默认集成ORM,但是symfony标准版,集成了很多程序,还自带集成了Doctrin ...

  5. Linux(CentOS)搭建SVN服务器全攻略

    虽然在windows上搭建SVN很简单,但是效能却不高,这当然是和linux相比了.然而在linux上搭建SVN却非常繁琐,所以今天这篇文章就来一步一步教您如何在Centos上搭建SVN 安装#yum ...

  6. php mkdir 创建多级目录实例代码

    先介绍一下 mkdir() 这个函数 mkdir($path,0777,true); 第一个参数:必须,代表要创建的多级目录的路径. 第二个参数:设定目录的权限,默认是 0777,意味着最大可能的访问 ...

  7. 【创建型】Prototype模式

    原型模式主要是用原型实例指定创建原型对象的种类,并且通过拷贝原型创建新对象.最简单的理解就是克隆.就如cocos2d-x中的 class Clonable::clone();该模式的主要目的是可以在运 ...

  8. MySQL触发器之审计功能(转)

    [导读] 最近ITPUB技术论坛特意组织网络性讨论活动,关于数据库审计的话题,分享各自公司如何实现数据库审计.个人经验和构想,以及数据库审计的技巧,刚好有网友发了一个典型的审计需求,要帮他分析,以及教 ...

  9. HashMap HashTable HashSet区别剖析

    HashMap.HashSet.HashTable之间的区别是Java程序员的一个常见面试题目,在此仅以此博客记录,并深入源代码进行分析: 在分析之前,先将其区别列于下面 1:HashSet底层采用的 ...

  10. android adb 常用指令

    转自:http://www.cnblogs.com/playing/archive/2010/09/19/1830799.html Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你 ...