DES 算法实现
一、DES算法原理概述
DES算法是一种对称加密算法,这种特性使得,利用明文和密钥,我们加密一次能得到密文,然后利用密文和密钥再次经过DES加密,得到原文。
简要介绍对称加密算法。一个对称加密由明文(原始信息或数据,作为算法的输入)、 加密算法(对明文进行各种替换和转换)、 密钥(算法的另一个输入,决定算法进行的具体替换和转换)、 密文(已被打乱的消息输出)、 解密算法(加密算法的反向执行)5部分组成。
算法首先需要两样数据,一个是64位bits的明文Plain_Text,另一个是64位bits的密钥Key_64。在选定密钥的情况下,我们就能通过DES算法加密明文了。下面详细介绍DES的加密步骤。
1.第一步:IP置换。我们已有一个公开确定的IP矩阵,它是一个大小为64,里面的数据分别是1到64的矩阵,用来初步混淆明文。原理是让明文的每一位对应上IP矩阵的每一位,取其中的数据作为新的下标,重新排列得到一串64位长的数据M0。这里提醒,整个算法过程用到的矩阵的里面存的下标都是从一开始的,并不是我们熟悉的从0开始。
2.第二步:T迭代。这是整套算法里最关键的部分,里面会有多种处理手法。首先将上一步得到的M0断开成两个32位长的串L和R。然后将L和R经过16轮的迭代。其中每轮的L会重新赋值为上一轮的R,而R则会赋值为上一轮的L 异或上 Feistel函数返回值的值。
3.接下来顺势详细谈谈Feistel函数。它接收两个参数,一个是上一轮迭代而来的32位长的R,一个是后面会提到的48位长的子密钥Key_Sub。先要扩展32位的R到48位的E_R,用于与Key_Sub异或得到一个命名为result的48位长变量。然后result每连续6位就经过一次S_Box映射,每次都从6位压缩成4位。这样的映射会循环8次,那么最后映射结果连起来,又得到一个32位长的串F_S。将F_S经过一次P置换得到F_P。最后它会返回32位长的F_P用于赋值给下一轮的R。
4.上面提到的扩展32位的R到48位的E_R,用的是大小为48的矩阵E,里面存着对应R的下标,类似前面的置换过程。这也是一个公开确定的矩阵。
5.上面提到的S_Box映射,用的是8个给定的大小为4*16的矩阵S_Box,里面存的每一个元素都是能用4位二进制表示的数。这是一系列公开确定的矩阵,这也是整个算法里最让人摸不清的地方。但是我们先这样用吧。我们将上面的result每连续6位取出,分别表示为b1b2b3b4b5b6,然后令行号row=b1b6,列号col=b2b3b4b5,这样刚好能从一个S_Box中确定出一个4位长的数据。这样的过程会循环8次,分别从不同S_Box中得到一个4位数据,连接起来就得到了32位长的F_S。
6.上面提到的P置换,用的是大小为32的矩阵P,同样存着新的下标,同样类似前面的置换过程。这也是一个公开确定的矩阵。
7.T迭代的最后,我们再将最终的32位的L和R换个位置,存进一个64位长的变量RL里并返回RL,才结束这一步。
8.第三步,也就是加密过程的最后一步,IP逆置换。同样,我们给定了IP的逆矩阵,然后模仿第一步再做一次一样的映射,我们就得到密文Cipher_Text了。这一步看似简陋不必要,但其实是为了维护DES算法的对称性。只有保持了对称性,我们才能利用密文和密钥经过又一轮加密从而解密得到原文。
9.然后就要谈谈16个48位长的Key_Sub的产生了。64位密钥的先要经过一轮PC_1置换,得到56位的Key_56,然后将其分成左右各28位的C和D。对C和D按规则进行16轮的循环左移,每次得到的结果拼接回Key_56,然后进行PC_2置换得到48位的串,即一个Key_Sub。这16个Key_Sub在加密过程中分别用于上述的16次的T迭代,而在解密过程中,顺序需要反过来才能正确解密。
10.上面提到的PC_1置换和PC_2置换也是类似上面的映射,只不过PC_1置换是64位压缩为56位,PC_2置换是56位再映射成48位。PC_1矩阵和PC_2矩阵也已给出。
二、总体结构
下面开始介绍我对这个算法的实现。实现用的是C++,原因有二。一是我打算用面向对象将DES算法封装成一个类,用一个密钥创建一个DES对象,并只对外提供加密和解密两个函数接口。其余的实现细节(如用到的矩阵、T迭代、Feistel函数和子密钥生成等)对用户隐藏,较好地保证安全性。二是C++有bitset这个标准库,能直观地操作一个个bit,比C语言方便实现。
类DES是这次需要实现的类,我需要实现的功能很简单,给定一串64位的密钥,用密钥创建一个DES对象,提供64位的明文串,加密返回一串64位的密文,然后解密密文,使得解密结果和原文完全一致。
三、模块分解
类DES的声明放在文件”DES.h”中,并包含了静态常量的初始化。
实现的函数则放在文件”DES.cpp”中,测试用的主函数在”main.cpp”中。
首先DES的构造函数需要传入64位的bitset类型数据,赋值给私有变量Key_64,即我们后来用到的密钥。
然后DES的加密函数Encrypt和解密函数Decrypt也需要传入64位的bitset类型数据,这是分别我们需要加密的明文和需要解密的密文。
IP_Substitute函数是加密第一步的IP置换。传入的参数是明文,返回初步混淆后的64位密文。
IP_Inv_Substitute函数是加密最后一步的IP逆置换。传入T迭代后的密文,返回最终的64位密文。
T_Iteration函数是加密第一步的T迭代,传入第一步IP置换后的密文以及子密钥的引用,返回迭代后的64位密文。
Feistel函数是T迭代里用到的Feistel函数,用于将密文和密钥混合加密。
E_Expand函数是将32位的R扩展成48位的E_R。
P_Substitute函数是将32位的F_S映射为32位的F_P。
Key_64_To_56和Key_56_To_48以及Key_28_Shift_Left都是对上面所说的密钥处理拆分的过程,而函数Key_Gen调用这些函数从而得到16个48位的子密钥。
Key_Inv函数反转子密钥序列,用在解密函数当中。
四、数据结构
bitset是主要用到的数据结构,用来表示给定位数的bit串。过程中用到的矩阵如S_BOX,IP, PC_1等,用数组储存,作为DES类的静态私有常量。另外在测试用的主函数里,用到string类型的字符串来初始化bitset类型数据如明文和密钥。
类DES中我尽量不留变量,这样的话只靠函数计算并返回数值就比较安全,不容易泄露对象内的信息。但是初始化所需的密钥不能没有,所以用一个64位的bitset存着。另外子密钥序列的生成和解密所需的反转顺序的子密钥序列都能通过函数参数传引用的方法而避免用私有变量保存。故只需有一个私有变量即可。
五、C++算法过程
DES.h
#include <bitset>
using namespace std;
class DES {
public:
DES(bitset<64> Key);
bitset<64> Encrypt(bitset<64> Text);
bitset<64> Decrypt(bitset<64> Text);
private:
static int IP[];
static int IP_Inv[];
static int E[];
static int S_BOX[8][4][16];
static int P[];
static int PC_1[];
static int PC_2[];
bitset<64> IP_Substitute(bitset<64> M);
bitset<64> IP_Inv_Substitute(bitset<64> RL);
bitset<32> Feistel(bitset<32> R, bitset<48> ki);
bitset<48> E_Expand(bitset<32> R);
bitset<32> P_Substitute(bitset<32> F_S);
bitset<64> T_Iteration(bitset<64> M0, bitset<48>* &Key_Sub);
bitset<56> Key_64_To_56(bitset<64> Key);
bitset<28> Key_28_Shift_Left(bitset<28> Key_56_Half, int num);
bitset<48> Key_56_To_48(bitset<56> Key_56);
void Key_Gen(bitset<64> Key_64, bitset<48>* &Key_Sub);
void Key_Inv(bitset<48>* &Key_Sub);
bitset<64> Key_64;
};
int DES::IP[] = {
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
};
int DES::IP_Inv[] = {
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,