点击获取完整源代码
一、算法简介
DES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国国家标准局(NBS)确定为联邦信息处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。
DES是基于56位密钥的对称算法,由于它使用的56位密钥过短,目前已经不被视为一种安全的加密算法。当然你也可以使用DES的派生算法3DES来进行加密,提高破解的难度。
2001年,DES被高级加密标准(AES)所取代。另外,DES已经不作为NIST的标准。
二、算法原理概述
1. 算法基本概念
明文以字节为基本单位,将明文分成每8字节一组,即64位,如果明文最后的分组不够64位,按照PKCS#5规范进行填充,如最后一组只有2字节,那么在末尾补上6个字节,每个字节取值均为06,如果刚刚分组完全,则在末尾添加8个字节,字节取值均为08。
密钥K以位为基本单位,密钥长64位,与明文分组一样长,但密钥因为存在8位奇偶校验位,实际起作用的只有56位,密钥需要加密解密双方均知道才可以进行加密解密。
2. 算法总体结构
以下明文、密文长均为64位,加密或解密后需手动组装起来
- 加密过程
- C = DES(M) =
I
P
−
1
{IP}^{-1}
IP−1 * W *
T
16
T_{16}
T16 *
T
15
T_{15}
T15 * … *
T
1
T_{1}
T1 *
I
P
IP
IP (M)
- M为64位明文,C为64位密文
- IP为初始置换
- I P − 1 IP^{-1} IP−1 为IP的逆置换
- T 16 T_{16} T16 * $T_{15} $ * … * T 1 T_1 T1 为16轮迭代T,由密钥得出
- W为前后置换,将64位的前32位与后32位交换位置
- C = DES(M) =
I
P
−
1
{IP}^{-1}
IP−1 * W *
T
16
T_{16}
T16 *
T
15
T_{15}
T15 * … *
T
1
T_{1}
T1 *
I
P
IP
IP (M)
- 解密过程
- M = DeDES(C)= I P − 1 {IP}^{-1} IP−1 * W * T 1 T_1 T1 * … * T 15 T_{15} T15* T 16 T_{16} T16 * I P IP IP (C)
3. 初始置换IP
IP是64位的置换表,代码中表现为64位数组。其中第0位上的数为58,表示为新得到的64位串的第0位是原来的第57位(注意是57位而不是58位,因为数组上的数只有1-64,并不是0-63,所以要相对应减1,后面的所有置换表均如此),新得到的64位串表示位 L 0 L_0 L0 R 0 R_0 R0,分别代表前32位(即低32位)和后32位(即高32位)。置换表在代码中给出。
4. 逆置换 I P − 1 IP^{-1} IP−1
同上,置换表也在代码中给出。
5. 16轮T迭代
这是最复杂的一个环节
(1)第k轮迭代
L i L_i Li = R i − 1 R_{i-1} Ri−1 , R i R_i Ri = L i − 1 L_{i-1} Li−1 ⨁ \bigoplus ⨁ f f f( R i − 1 R_{i-1} Ri−1, k i k_i ki), i i i = 1, 2, … , 16
注:解密过程是反过来的,即i是递减使用的
- f f f是输出32位的Feistel轮函数
- k i k_i ki长48位,是由密钥K生成的字密钥,共16个
(2)字密钥 k i k_i ki生成
- 对K的56位非校验位进行PC-1置换,PC-1置换表共56位,得到 C 0 C_0 C0 D 0 D_0 D0,分别为前28位和后28位, i i i = 1
- 计算
C
i
C_i
Ci = L
S
i
S_i
Si(
C
i
−
1
C_{i-1}
Ci−1)和
D
i
D_i
Di = L
S
i
S_i
Si(
D
i
−
1
D_{i-1}
Di−1)
- 当i为1,2,9,16时,L S i S_i Si(A)表示将二进制串A左移一位,否则,左移两位
- 对56位的 C i C_i Ci D i D_i Di进行PC-2置换,得到48位的 k i k_i ki.PC-2置换表共48位,置换表在代码中给出
- 循环得到所有字密钥
(3)Feistel轮函数 f f f( R i − 1 R_{i-1} Ri−1, k i k_i ki)
- 将长度位32位的串 R i − 1 R_{i-1} Ri−1进行E-扩展,成为48位的串E( R i − 1 R_{i-1} Ri−1),E-扩展所需要的置换表在代码中给出
- 将E( R i − 1 R_{i-1} Ri−1)和长度位48位的字密钥 k i k_i ki做二进制的异或运算, k i k_i ki前面已算出
- 将上一步得到的48位结果分成8个分组,每个分组长度为6位
- 将8个分组分别经过8个不同的S-盒( S 1 S_1 S1 S 2 S_2 S2… S 8 S_8 S8)进行6-4转换,得到8个长度为4位的分组,分组位越低使用的S-盒下标也小,此处是一一对应的,S-盒的转换规则稍后给出
- 将上一步得到的结果按顺序连接在一起得到长度为32位的串
- 将上一步的结果经过P-置换,得到新的32位串,作为 f f f( R i − 1 R_{i-1} Ri−1, k i k_i ki) 的返回值。P置换表共32位,代码中给出
(4)S-盒转换
每个S-盒均为4行16列的矩阵,矩阵内数据为0-15,可变为二进制串0000-1111。输入的数有6bit,经过转化之后返回4bit。
例:S1为刚刚48位串的最低6位,为“1111001”,自然得使用 S 1 S_1 S1盒。令row = ( 11 ) 10 (11)_{10} (11)10 = 3,其中,高位1为 S 1 S_1 S1的最高位,低位1为 S 1 S_1 S1的最低位。令col = ( 1100 ) 10 (1100)_{10} (1100)10 = 12,这4个位为中间剩下的4个位。查找 S 1 S_1 S1盒, [ S 1 ] 3 , 12 {[S_1]_{3,12}} [S1]3,12则为输出的四位。注意:3代表的是第4行,因为矩阵下标从0开始,同理12代表13列
8个S-盒的矩阵在代码中给出。
三、代码实现模块及结构
此代码支持中英文的加密解密,具体代码请看代码文件:data.hpp存放各个置换表,des.cpp是具体实现文件
(1)自定义结构体代表二进制
typedef struct data64
{
bitset<64> bits;
}datas_64;
typedef struct data56
{
bitset<56> bits;
}datas_56;
//还有其他不同位数的结构体
(2)声明函数模块
void init_secret_key(); //初始化密钥
void init_K_i(datas_64 secret_key);//初始化子密钥ki
string RKCS_fill(string str); //RKCS#5补齐8个字节
vector<datas_64> string_to_binary(string str); //将字符串转化为二进制数
string binary_to_string(vector<datas_64> v); //将二进制数转化为字符串
vector<datas_64> encryption(vector<datas_64> v); //所有数据的加密算法,第二个参数为密钥
vector<datas_64> Decrypt(vector<datas_64> v); //所有数据的解密算法,第二个参数为密钥
datas_64 byte_encryption(datas_64 v); //64位的加密算法
datas_64 byte_Decrypt(datas_64 v); //64位的解密算法
datas_64 IP(datas_64 v); //IP置换
datas_64 IP_inverse(datas_64 v); //IP的逆置换
datas_64 W(datas_64 v); //W操作
datas_64 T_iteration(datas_64 v, datas_48* K); //16次T迭代,注意,算法最后的左右交换输出就是W操作,在这里不写,不要重复!!
datas_64 T_iteration_inverse(datas_64 v, datas_48* K); //16次T的逆迭代,算法最后的左右交换输出就是W操作,在这里不写,不要重复!!
datas_32 Feistel(datas_32 R, datas_48 K); //Feistel轮函数
datas_48 E_extend(datas_32 v); //E-扩展
datas_32 S_box_change(datas_48 v); //s盒6-4转化,48位转位32位
//6-4转化,输入一个6位,输出一个4位,index位第index个S-BOX
datas_4 find_in_Sbox(int index, datas_6 v);
datas_32 P(datas_32 v); //Feistel轮函数中的P置换
(3)main函数
int main(){
init_secret_key();
init_K_i(secret_key);
string Plaintext = "";
char c;
cout << "输入明文: ";
while((c = cin.get()) != '\n'){
Plaintext += c;
}
Plaintext = RKCS_fill(Plaintext);
vector<datas_64> v = string_to_binary(Plaintext);
vector<datas_64> data1 = encryption(v);
cout << "密文: " << binary_to_string(data1) << endl;
vector<datas_64> data2 = Decrypt(data1);
cout << "解密后明文: " << binary_to_string(data2) << endl;
return 0;
}
四、实验结果
-
输入随机中文
-
输入随机英文
-
中英混合