版权声明:本文为博主原创文章,若是用于商业用途只需要注明出处即可。
本文主要讲述DES算法的具体实现过程,不再重复说明DES的原理了。
到最后会完成结果如下:
输入string:ilovedes
输入key:1234abcd
显示输入序列(2进制):01101001 01101100 01101111 01110110 01100101 01100100 01100101 01110011
显示加密序列(2进制):10001001 10111110 00101001 11111001 00111010 01101000 10110111 00101010
显示解密序列(2进制):01101001 01101100 01101111 01110110 01100101 01100100 01100101 01110011
如果 输入序列 等于 解密序列 则加解密成功。
[注意] 二进制的序列比较可以直观看出加解密的结果,也可以转化为char*类型,文末提供额外函数以供参考。
一、大概过程
DES加解密的大概过程如下:
(1)IP置换
(2)16轮Feistel运算
(3)64bit数据前32位和后32位互换
(4)逆IP置换
输入64bit数据,经过上述过程,处理得到的密文64bit。
二、准备表
1.初始置换表–IP
int IP_TABLE[64] = { //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 };
2.扩展置换表–E
int 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 };
3.S盒(共8个)
int S_BOX[8][4][16] =
{
// S1
{ 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
},
{
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,
},
{
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,
},
{
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,
},
{
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,
},
{
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,
},
{
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,
},
{
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
}
};
4.P置换表
int P_TABLE[32] = { // P 盒
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 };
5.PC_1表
int 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 };
6.PC_2表
int 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 };
7.移位次数关系表–M
int M_Table[16] = {
1,1,2,2,
2,2,2,2,
1,2,2,2,
2,2,2,1
};
8.逆初始置换表–IP_N
int IP_N_Table[64] = { //逆IP置换矩阵
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 };
三、核心代码
按照实现的步骤进行即可。
1、设置头文件等
如果含有全局变量,全局变量都会在后面的步骤中说明,是为了用于可以随时debug相关信息,可以用于C++类的组成。如果不需要,可以在main()函数中另外定义变量存储。相关调试函数不会对程序稳定造成影响!
#include<iostream>
using namespace std;
2、扩展KEY
在进行正式的加密运算之前,我们先来生成KEY数组。
(注意:在之后的步骤中,小写key代表< 密钥字符串转化成的bool [64bit] >,大写KEY代表< 扩展的密钥数组bool [16][48] >)
(1)PC_1置换
输入64bit密钥,输出28bit和28bit 。
void PC_1(bool key[64],bool l[28],bool r[28]) {
for (int i = 0; i < 28; i++) {
l[i] = key[PC1_TABLE[i] - 1];//PC1_TABLE[i]是从1开始的位置,在数组中坐标需要减1(数组从0开始)
r[i] = key[PC1_TABLE[i+28] - 1];
}
}
(2)循环左移
输入input (bool数组)、 bitArrayLen (数组长度)、moveNum (左移距离)。输出 output(bool数组)。
void MoveLeft(bool *input,bool *output,int bitArrayLen,int moveNum) {
for (int i = 0; i < bitArrayLen; i++)
output[i] = input[(i + moveNum) % bitArrayLen];
}
(3)PC_2置换
输入28bit和28bit,输出48bit子密钥 。
void PC_2(bool l[28],bool r[28],bool key[48]){
bool temp[56] = { 0 };
int i = 0;
//合并成56bit
for (i = 0; i < 28; i++) {
temp[i] = l[i];
temp[i + 28] = r[i];
}
//压缩成key
for (int i = 0; i < 48; i++)
key[i] = temp[PC2_TABLE[i] - 1];
}
(4)生成密钥数组
每轮子密钥获取。输入l[28],r[28],当前轮数round 。输出子密钥c_key。(c_key命名由来:child_key)
void getKeyRound(bool L[28], bool R[28], bool c_key[48], int round) {
//l与r是函数内操作的32bit,可以看做缓存
bool l[28] = { 0 };
bool r[28] = { 0 };
int moveNum = M_Table[round];//左移位数
//循环左移
MoveLeft(R, r, 28, moveNum);
MoveLeft(L, l, 28, moveNum);
//PC2置换
PC_2(l,r, c_key);
//L与R替换成循环左移后的数据
for (int i = 0; i < 28; i++) {
R[i] = r[i];
L[i] = l[i];
}
}
生成密钥数组。输入key[64],输出KEY[16][48] 。(均为bool数组)
void getKey(bool key[64], bool KEY[16][48]) {
bool L[28] = { 0 };
bool R[28] = { 0 };
PC_1(key, L, R);//置换 PC-1
for (int i = 0; i < 16; i++) {
getKeyRound(L, R, KEY[i],i);//每轮获取子密钥
}
}
3、加密部分
(1)初始IP置换
输入64bit,输出64bit
void IP(bool input[64],bool output[64]) {
for (int i = 0; i < 64; i++)
output[i] = input[IP_TABLE[i] - 1];
}
(2)Feistel运算
初始置换后会将64bit数据分为L与R(各32bit)。
以下是每轮的Feistel运算,总共16轮。
第1步,R与子密钥c_key通过F()输出r。
第2步,L与r异或输出l。
第3步,L=R,R=l。
1)先完成F函数。
F函数使用了E函数、XOR函数(异或)、S盒置换、P函数。
代码(E函数):
void E(bool R[32], bool output[48]) {
for (int i = 0; i < 48; i++)
output[i] = R[E_TABLE[i] - 1];
}
代码(XOR函数):
void XOR(bool *input1,bool *input2,bool*output,int len) {
for (int i = 0; i < len; i++)
output[i] = input1[i] ^ input2[i];
}
代码(S函数)以及相关工具函数:(工具函数是为了代码的可阅读性。)
int Mi(int num, int n) {//返回num的n次幂
if (n <= 0)
return 1;
else
return Mi(num, n - 1)*num;
}
int BitToInt(bool *bit, int bitArrayLen) {//输入2进制数组,返回整数
int num = 0;
int i = bitArrayLen - 1;
while (i >= 0) {
if (bit[i] == 1) {
num += Mi(2, bitArrayLen - 1 - i);
}
i--;
}
return num;
}
void IntToBit4(int num, bool bit[4]) {//整数转化为4bit大小的bool数组
int i = 3;
while (num != 0 && i >= 0) {
if (num % 2 == 1)
bit[i--] = 1;
else
bit[i--] = 0;
num /= 2;
}
while (i >= 0) {
bit[i--] = 0;
}
}
void S(bool input[48], bool output[32]) {//输入48bit,输出32bit
int i = 0;
int j = 0;
int t = 0;
for (i = 0; i < 8; i++) {//8个S盒,每个盒子输入6bit,输出4bit
//t是每次输入的起始坐标
t = 6 * i;
//每个循环内单独定义,为了不必重新初始化。
bool line[2] = { input[t],input[t + 5] };
bool row[4] = { input[t + 1] ,input[t + 2] ,input[t + 3] ,input[t + 4] };
int num = S_BOX[i][BitToInt(line, 2)][BitToInt(row, 4)];
bool bit4[4] = { 0 };
IntToBit4(num, bit4);
for (j = 0; j < 4; j++)//将输出的4bit数据复制到output的对应位置
output[4 * i + j] = bit4[j];
}
}
代码(P函数):
void P(bool input[32], bool output[32]) {
for (int i = 0; i < 32; i++)
output[i] = input[P_TABLE[i] - 1];
}
代码(F函数):
void F(bool input[32], bool key[48], bool output[32]) {
bool e48[48] = { 0 };//缓存
E(input, e48);
bool xor48[48] = { 0 };//缓存
XOR(e48, key, xor48, 48);
bool s32[32] = { 0 };//缓存
S(xor48, s32);
P(s32, output);
}
2)单轮Feistel运算。
void FeistelRound(bool L[32], bool R[32], bool c_key[48]) {//输入L,R,c_key
bool f32[48] = { 0 };
bool r[32] = { 0 };//函数内的缓存
bool l[32] = { 0 };//函数内的缓存
int i = 0;
//r保存R的数据
for (i = 0; i < 32; i++) {
r[i] = R[i];
}
F(R, c_key, f32);
//使用l保存L与f32异或后的数据
XOR(f32, L, l, 32);
//当前轮次运算完毕,交换L与R的值
for (int i = 0; i < 32; i++) {
L[i] = r[i];
R[i] = l[i];
}
}
3)Feistel运算。
void Feistel(bool input[64],bool output[64],bool KEY[16][48],bool ifEnCrypt) {
bool L[32] = { 0 };
bool R[32] = { 0 };
int i = 0;
//L、R赋值
for (i = 0; i < 32;i++) {
L[i] = input[i];
R[i] = input[i + 32];
}
for (i = 0; i < 16; i++) {
if (ifEnCrypt == true)//加密
FeistelRound(L, R, KEY[i]);
else//解密
FeistelRound(L, R, KEY[15-i]);//i的范围0-15
}
for (i = 0; i < 32; i++) {//最后交换L、R
output[i] = R[i];
output[ i + 32 ] = L[i];
}
}
(3)逆IP置换
void IP_N(bool input[64], bool output[64]) {
for (int i = 0; i < 64; i++)
output[i] = input[IP_N_Table[i] - 1];
}
(4)加解密模块
加密
void EnCrypt(bool input[64],bool KEY[16][48],bool output[64]) {
bool b64[64] = { 0 };
IP(input, b64);
bool o64[64] = { 0 };
Feistel( b64, o64, KEY, true);
IP_N(o64, output);
}
解密。
void DeCrypt(bool input[64], bool KEY[16][48], bool output[64]) {
bool b64[64] = { 0 };
IP(input, b64);
bool o64[64] = { 0 };
Feistel(b64, o64, KEY, false);
IP_N(o64, output);
}
4、测试
在前面我们已经完成了DES的核心加密功能了。为了能够方便以后扩展其他功能,如:文件加密、长字符串加密、分组密码工作模式、3DES等,可以扩写其他函数。
扩写其他函数的标准:函数功能尽量支持重复使用、命名可读性、命名合理性等。
(1)字符串与bool数组之间的转换
为什么本程序多使用bool数组?
我认为,bool类型只有1或0,比较贴合比特的属性,也节省了一些内存空间。
因为输入是char * 类型的,如果直接在程序中转换,会造成代码难以阅读,因此另外编写函数来输出以及转换。
1)一个字节转为8个bit
void CharToBit(char input,bool output[8]) {//1个字符转为8bit
int i = 7;
if(input==0){//如果输入为空,则bit数组置0
for(i=0;i<8;i++){
output[i]=0;
}
}
while (input != 0 && i >= 0) {
if (input % 2 == 1)
output[i--] = 1;
else
output[i--] = 0;
input /= 2;
}
while (i >= 0) {
output[i--] = 0;
}
}
2)n个字节转为8n个bit
void CharToBit(char *input, bool *output,int n) {//n个字符转为8n个bit
for (int j = 0; j < n; j++)
CharToBit(input[j], output + j * 8);
}
(2)显示bit序列
打印长度为n的bool数组
void PrintBit(bool *bit,int n) {
for (int i = 0; i < n; i++) {
if ((i + 1) % 8 == 0)//每8个bit空一格输出,方便观察
cout << bit[i] << " ";
else
cout << bit[i];
}
cout << endl;
}
(3)在main函数中进行测试
void main() {
char str[10] = { 0 };
char str_key[10] = { 0 };
bool b_key[64] = { 0 };
bool b_KEY[16][48] = { 0 };
bool b_str[64] = { 0 };
bool b_encrypt[64] = { 0 };
bool b_decrypt[64] = { 0 };
cout << "输入string:"; cin >> str;
cout << "输入key:"; cin>>str_key;
CharToBit(str, b_str, 8);//str转化为b_str
CharToBit(str_key, b_key, 8);//str_key转化为b_key
getKey(b_key,b_KEY);//b_key转化为b_KEY
EnCrypt(b_str,b_KEY,b_encrypt);//加密
DeCrypt(b_encrypt, b_KEY, b_decrypt);//解密
cout << "显示输入序列(2进制):"; PrintBit(b_str, 64);
cout << "显示加密序列(2进制):"; PrintBit(b_encrypt, 64);
cout << "显示解密序列(2进制):"; PrintBit(b_decrypt, 64);
system("pause");
}
(4)测试结果

四、一些总结
(1)DES算法算是比较简单的一个算法了,逻辑部分很好实现,按照教程手敲过一遍基本会熟悉它的过程了。
(2)我的编程思想是:先把函数做好,再在主函数里实现逻辑就很方便了,想用某个功能时候立马就能想起就是这个函数。
(3)为什么输出对比时用的是二进制序列而不是字符串?我此处用的是char类型的,char范围-128到127。当bit数组为1000 0000时候转化为char时候会返回残缺数据,感兴趣的可以用BitToChar()函数单独尝试下。而且转化为字符数组显示并不能看出加密后的数据变化。
解决办法: 使用unsigned char或unsigned char * ;(本文没有使用因为只是为了加密比特,而不是需要输出。读者可以自行另外写函数输出。)
附件(完整代码)
#include<iostream>
using namespace std;
int IP_TABLE[64] = { //置换矩阵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 E_TABLE[48] = { //扩展矩阵E
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 };
int S_BOX[8][4][16] =
{
{ // S1
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
},
{// S2
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,
},
{// S3
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,
},
{// S4
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,
},
{// S5
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,
},
{// S6
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,
},
{// S7
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,
},
{// S8
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
}
};
int P_TABLE[32] = { // 表P
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 };
int PC1_TABLE[56] = { // PC_1
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 };
int PC2_TABLE[48] = { // PC_2
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 };
int M_Table[16] = {
1,1,2,2,
2,2,2,2,
1,2,2,2,
2,2,2,1
};
int IP_N_Table[64] = { //逆初始置换矩阵IP_N
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 };
void PC_1(bool key[64], bool l[28], bool r[28]) {
for (int i = 0; i < 28; i++) {
l[i] = key[PC1_TABLE[i] - 1];//PC1_TABLE[i]是从1开始的位置,在数组中坐标需要减1(数组从0开始)
r[i] = key[PC1_TABLE[i + 28] - 1];
}
}
void MoveLeft(bool *input, bool *output, int bitArrayLen, int moveNum) {
for (int i = 0; i < bitArrayLen; i++)
output[i] = input[(i + moveNum) % bitArrayLen];
}
void PC_2(bool l[28], bool r[28], bool key[48]) {
bool temp[56] = { 0 };
int i = 0;
//合并成56bit
for (i = 0; i < 28; i++) {
temp[i] = l[i];
temp[i + 28] = r[i];
}
//压缩成key
for (int i = 0; i < 48; i++)
key[i] = temp[PC2_TABLE[i] - 1];
}
void getKeyRound(bool L[28], bool R[28], bool c_key[48], int round) {
//l与r是函数内操作的32bit,可以看做缓存
bool l[28] = { 0 };
bool r[28] = { 0 };
int moveNum = M_Table[round];//左移位数
//循环左移
MoveLeft(R, r, 28, moveNum);
MoveLeft(L, l, 28, moveNum);
//PC2置换
PC_2(l, r, c_key);
//L与R替换成循环左移后的数据
for (int i = 0; i < 28; i++) {
R[i] = r[i];
L[i] = l[i];
}
}
void getKey(bool key[64], bool KEY[16][48]) {
bool L[28] = { 0 };
bool R[28] = { 0 };
PC_1(key, L, R);//置换 PC-1
for (int i = 0; i < 16; i++) {
getKeyRound(L, R, KEY[i], i);//每轮获取子密钥
}
}
void IP(bool input[64], bool output[64]) {
for (int i = 0; i < 64; i++)
output[i] = input[IP_TABLE[i] - 1];
}
void E(bool R[32], bool output[48]) {
for (int i = 0; i < 48; i++)
output[i] = R[E_TABLE[i] - 1];
}
void XOR(bool *input1, bool *input2, bool*output, int len) {
for (int i = 0; i < len; i++)
output[i] = input1[i] ^ input2[i];
}
int Mi(int num, int n) {//返回num的n次幂
if (n <= 0)
return 1;
else
return Mi(num, n - 1)*num;
}
int BitToInt(bool *bit, int bitArrayLen) {//输入2进制数组,返回整数
int num = 0;
int i = bitArrayLen - 1;
while (i >= 0) {
if (bit[i] == 1) {
num += Mi(2, bitArrayLen - 1 - i);
}
i--;
}
return num;
}
void IntToBit4(int num, bool bit[4]) {//整数转化为4bit大小的bool数组
int i = 3;
while (num != 0 && i >= 0) {
if (num % 2 == 1)
bit[i--] = 1;
else
bit[i--] = 0;
num /= 2;
}
while (i >= 0) {
bit[i--] = 0;
}
}
void S(bool input[48], bool output[32]) {//输入48bit,输出32bit
int i = 0;
int j = 0;
int t = 0;
for (i = 0; i < 8; i++) {//8个S盒,每个盒子输入6bit,输出4bit
//t是每次输入的起始坐标
t = 6 * i;
//每个循环内单独定义,为了不必重新初始化。
bool line[2] = { input[t],input[t + 5] };
bool row[4] = { input[t + 1] ,input[t + 2] ,input[t + 3] ,input[t + 4] };
int num = S_BOX[i][BitToInt(line, 2)][BitToInt(row, 4)];
bool bit4[4] = { 0 };
IntToBit4(num, bit4);
for (j = 0; j < 4; j++)//将输出的4bit数据复制到output的对应位置
output[4 * i + j] = bit4[j];
}
}
void P(bool input[32], bool output[32]) {
for (int i = 0; i < 32; i++)
output[i] = input[P_TABLE[i] - 1];
}
void F(bool input[32], bool key[48], bool output[32]) {
bool e48[48] = { 0 };//缓存
E(input, e48);
bool xor48[48] = { 0 };//缓存
XOR(e48, key, xor48, 48);
bool s32[32] = { 0 };//缓存
S(xor48, s32);
P(s32, output);
}
void FeistelRound(bool L[32], bool R[32], bool c_key[48]) {//输入L,R,c_key
bool f32[48] = { 0 };
bool r[32] = { 0 };//函数内的缓存
bool l[32] = { 0 };//函数内的缓存
int i = 0;
//r保存R的数据
for (i = 0; i < 32; i++) {
r[i] = R[i];
}
F(R, c_key, f32);
//使用l保存L与f32异或后的数据
XOR(f32, L, l, 32);
//当前轮次运算完毕,交换L与R的值
for (int i = 0; i < 32; i++) {
L[i] = r[i];
R[i] = l[i];
}
}
void Feistel(bool input[64], bool output[64], bool KEY[16][48], bool ifEnCrypt) {
bool L[32] = { 0 };
bool R[32] = { 0 };
int i = 0;
//L、R赋值
for (i = 0; i < 32; i++) {
L[i] = input[i];
R[i] = input[i + 32];
}
for (i = 0; i < 16; i++) {
if (ifEnCrypt == true)//加密
FeistelRound(L, R, KEY[i]);
else//解密
FeistelRound(L, R, KEY[15 - i]);//i的范围0-15
}
for (i = 0; i < 32; i++) {//最后交换L、R
output[i] = R[i];
output[i + 32] = L[i];
}
}
void IP_N(bool input[64], bool output[64]) {
for (int i = 0; i < 64; i++)
output[i] = input[IP_N_Table[i] - 1];
}
void EnCrypt(bool input[64], bool KEY[16][48], bool output[64]) {
bool b64[64] = { 0 };
IP(input, b64);
bool o64[64] = { 0 };
Feistel(b64, o64, KEY, true);
IP_N(o64, output);
}
void DeCrypt(bool input[64], bool KEY[16][48], bool output[64]) {
bool b64[64] = { 0 };
IP(input, b64);
bool o64[64] = { 0 };
Feistel(b64, o64, KEY, false);
IP_N(o64, output);
}
void CharToBit(char input, bool output[8]) {//1个字符转为8bit
int i = 7;
if(input==0){//如果输入为空,则bit数组置0
for(i=0;i<8;i++){
output[i]=0;
}
}
while (input != 0 && i >= 0) {
if (input % 2 == 1)
output[i--] = 1;
else
output[i--] = 0;
input /= 2;
}
while (i >= 0) {
output[i--] = 0;
}
}
void CharToBit(char *input, bool *output, int n) {//n个字符转为8n个bit
for (int j = 0; j < n; j++)
CharToBit(input[j], output + j * 8);
}
void PrintBit(bool *bit, int n) {
for (int i = 0; i < n; i++) {
if ((i + 1) % 8 == 0)//每8个bit空一格输出,方便观察
cout << bit[i] << " ";
else
cout << bit[i];
}
cout << endl;
}
void main() {
char str[10] = { 0 };
char str_key[10] = { 0 };
bool b_key[64] = { 0 };
bool b_KEY[16][48] = { 0 };
bool b_str[64] = { 0 };
bool b_encrypt[64] = { 0 };
bool b_decrypt[64] = { 0 };
cout << "输入string:"; cin >> str;
cout << "输入key:"; cin >> str_key;
CharToBit(str, b_str, 8);//str转化为b_str
CharToBit(str_key, b_key, 8);//str_key转化为b_key
getKey(b_key, b_KEY);//b_key转化为b_KEY
EnCrypt(b_str, b_KEY, b_encrypt);//加密
DeCrypt(b_encrypt, b_KEY, b_decrypt);//解密
cout << "显示输入序列(2进制):"; PrintBit(b_str, 64);
cout << "显示加密序列(2进制):"; PrintBit(b_encrypt, 64);
cout << "显示解密序列(2进制):"; PrintBit(b_decrypt, 64);
system("pause");
}
本文详细介绍DES算法的具体实现过程,包括核心代码与测试流程,适用于理解并实现DES加密解密算法。





