上应用密码学,讲到DES算法,要求用C++实现,作为一个初学者,各种找资料,终于搞定了,写篇文章以备后用。本文介绍了 DES 算法加密的大致步骤和整体流程。
数据加密标准(Data Encryption Standard,DES)是在美国IBM公司的研究人员Horst Feistel 和Walter Tuchman于20世纪70年代中期提出的一个密码算法(LUCIFER算法(金星算法))的基础之上发展而来的,并于1977年1月15日由美国国家标准局(NBS)正式公布实施,是第一公开的商用密码标准,并得到了ISO的认可。
DES算法是一种典型的Feistel结构的分组密码算法,它的明文分组长度为64bit,密钥长度为64bit,其中密钥有8bit是奇偶校验,因此有效密钥长度为56bit.DES算法的加密和解密采用同一过程,它的安全性依赖于有效密钥。DES算法首先把需要加密的明文划分为每64bit的数据块,用56bit有效密钥对每64bit数据块进行加密。每次加密对输入的64bit的明文数据进行16轮的代替和置换后,输出完全不同于明文的64bit密文。DES算法仅使用最大为64bit的标准算数和逻辑运算,运算速度快,子密钥生产容易,适合于大多数计算机上采用软件方法实现,同时也适合于在专用芯片上实现。
DES算法的加密过程首先对明文分组进行操作,需要加密的明文分为固定大小64bit。
下面是DES加密算法的流程示意图:
每一个64bit的明文的分组加密处理过程
DES的16个子密钥的生成过程
从图可以看出,DES加密算法过程大体可分为三大部分:
16轮迭代采用Feistel密码结构对明文和密钥进行混淆和扩散
下面以输入明文“computer”和密钥“01234567”为例,分别讲一下各个部分的大致思路。
这部分很简单。初始置换表是一个8x8的矩阵
/*IP初始化置换表*/
int IP[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
};
表中的数值是输入的64bit明文块下标1到64。置换过程是输入64bit明文块根据表中下标位置进行置换。即把输入明文块第58位数据放到第一位,50位放到第二位......以此类推,初始置换以后得到的是一个 64 位的输出。
这部分需要用到的表 PC-1 和表 PC-2 如下:
/*置换选择PC-1*/
/*64bit密钥变为56bit*/
int PC_1[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
};
/*置换选择PC-2*/
/*56bit密钥压缩成48bit子密钥*/
int PC_2[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
};
/*循环左移位*/
int shiftBits[16] = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };
F函数变换过程如下图所示
密码函数F(R,k)接受两个输入:32 位的数据和 48 位的子密钥。然后:
这部分用到了E盒扩展置换表,8个S盒置换表以及P盒置换表,如下:
/*E盒扩展置换*/
/*将32bit扩展为48bit*/
int E[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
};
/*S盒代替压缩*/
/*每个S盒是16x4的置换表*/
/*48bit压缩为32bit*/
int S_BOX[8][4][16] = {
{
{ 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 }
}
};
/*P盒置换*/
/*类似于IP初始化*/
int P[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
};
合并 L16 和 R16 得到一个 64 位的数据,再经过尾置换后得到的就是 64 位的密文。注意:要将 L16和 R16 合并成 R16L16(即左右互换)。尾置换表IP-1如下:
/*逆初始化置换表*/
int IP_1[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
};
简单介绍一下要用到的bitset类
通过bitset类可以方便的处理二进制数据。
OK!现在我们可以回到本文的开头,去看看 DES 算法的整体流程图,思路就已经很清楚了。
/*************************************************************************
> File Name: DES
> Author: baofengxiong
************************************************************************/
#pragma once
#include
#include
using namespace std;
class DES
{
public:
bitset<32> F(bitset<32> R, bitset<48> k); //F函数
bitset<28> leftshift(bitset<28> k, int ); //左移函数
void generateKeys(); //生成16个48bit的子密钥
bitset<64> char_to_bit(const char s[8]); //将char字符转换为2进制
bitset<64> change(bitset<64> temp);
bitset<64> DES_encryp(bitset<64> &plain); //DES加密
bitset<64> DES_decrypt(bitset<64> &cipher);//DES解密
void get_s(string str) { s = str; } //获得明文
void get_key(string key_t) { k = key_t; } //获得密钥
void show_encryp(void);
void show_decrypt(void);
DES();
~DES();
private:
string s; //明文
string k; //密钥
bitset<64> key; //64位密钥
bitset<48> subkey[16];//每一轮子密钥
/*IP初始化置换表*/
int IP[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
};
/*逆初始化置换表*/
int IP_1[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
};
/****************密码函数F所用表*******************/
/*E盒扩展置换*/
/*将32bit扩展为48bit*/
int E[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
};
/*S盒代替压缩*/
/*每个S盒是16x4的置换表*/
/*48bit压缩为32bit*/
int S_BOX[8][4][16] = {
{
{ 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 }
}
};
/*P盒置换*/
/*类似于IP初始化*/
int P[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
};
/******************生成密钥所用表*******************/
/*置换选择PC-1*/
/*64bit密钥变为56bit*/
int PC_1[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
};
/*置换选择PC-2*/
/*56bit密钥压缩成48bit子密钥*/
int PC_2[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
};
/*循环左移位*/
int shiftBits[16] = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };
};
#include "DES.h"
#include
#include
using namespace std;
DES::DES()
{
}
DES::~DES()
{
}
bitset<32> DES::F(bitset<32> R, bitset<48> k) {
//E盒扩展
bitset<48> expandR;
for (int i = 0; i < 48; i++)
expandR[47 - i] = R[32 - E[i]]; //expandR[i] = R[E[i] - 1];
//异或
expandR = expandR ^ k;
//S盒代替
bitset<32> output;
int x = 0;
for (int i = 0; i < 48; i = i + 6)
{
int row = expandR[i] * 2 + expandR[i + 5];
int col = expandR[i + 1] * 8 + expandR[i + 2] * 4 + expandR[i + 3] * 2 + expandR[i + 4];
int num = S_BOX[i / 6][row][col];
bitset<4> temp(num);
output[x + 3] = temp[0];
output[x + 2] = temp[1];
output[x + 1] = temp[2];
output[x] = temp[3];
x += 4;
}
//P盒置换
bitset<32> tmp = output;
for (int i = 0; i < 32; i++)
output[i] = tmp[P[i] - 1];
return output;
}
//左移函数
bitset<28> DES::leftshift(bitset<28> k, int shift) {
bitset<28> temp = k;
if (shift == 1)
{
for (int i = 0; i < 27; i++)
{
if (i - shift < 0)
k[i - shift + 28] = temp[i];
else
k[i] = temp[i + shift];
}
}
if (shift == 2)
{
for (int i = 0; i < 26; i++)
{
if (i - shift < 0)
k[i - shift + 28] = temp[i];
else
k[i] = temp[i + shift];
}
}
return k;
}
void DES::generateKeys() {
bitset<56> real_key;
bitset<28> left;
bitset<28> right;
bitset<48> compressKey;
//首先经过选择置换PC-1,将初始密钥的8bit奇偶校验位去掉
//并重新编排
for (int i = 0; i < 56; i++)
real_key[i] = key[PC_1[i] - 1];
for (int round = 0; round < 16; round++)
{
for (int i = 0; i < 28; i++)
left[i] = real_key[i];
for (int i = 28; i < 56; i++)
right[i - 28] = real_key[i];
//左移
left = leftshift(left, shiftBits[round]);
right = leftshift(right, shiftBits[round]);
//连接,置换选择PC-2做重排,进行压缩
for (int i=0; i < 28; i++)
real_key[i] = left[i];
for (int i = 28; i < 56; i++)
real_key[i] = right[i - 28];
for (int i = 0; i < 48; i++)
{
int m = PC_2[i];
compressKey[i] = real_key[m - 1];
}
subkey[round] = compressKey;
}
}
// 工具函数:将char字符数组转为二进制
bitset<64> DES::char_to_bit(const char s[8]) {
bitset<64> bits;
int x = 0;
for (int i = 0; i < 8; i++)
{
int num = int(s[i]);
bitset<8> temp(num);
for (int j = 7; j >= 0; j--)
{
bits[x + j] = temp[7 - j];
}
x += 8;
}
/*for (int i = 0; i<8; ++i)
for (int j = 0; j<8; ++j)
bits[i * 8 + j] = ((s[i] >> j) & 1);
*/
return bits;
}
//工具函数:进行二进制逆向转换
bitset<64> DES::change(bitset<64> temp) {
bitset<64> bits;
bitset<8> n;
for (int i = 0; i < 64; i = i + 8)
{
for (int j = 0; j < 8; j++)
{
bits[i + j] = temp[i + 7 - j];
}
}
return bits;
}
bitset<64> DES::DES_encryp(bitset<64> &plain) {
bitset<64> cipher;
bitset<64> currentBits;
bitset<32> left;
bitset<32> right;
bitset<32> newLeft;
//初始置换IP
for (int i = 0; i < 64; i++)
currentBits[i] = plain[IP[i] - 1];//
for (int i = 0; i < 32; i++)
left[i] = currentBits[i];
for (int i = 32; i < 64; i++)
right[i - 32] = currentBits[i];
//进入16轮轮变换
for (int round = 0; round < 16; round++)
{
newLeft = right;
right = left ^ F(right, subkey[round]);
left = newLeft;
}
//合并
for (int i = 0; i < 32; i++)
cipher[i] = right[i];
for (int i = 32; i < 64; i++)
cipher[i] = left[i - 32];
//逆初始化置换
currentBits = cipher;
for (int i = 0; i < 64; i++)
cipher[i] = currentBits[IP_1[i] - 1];
return cipher;
}
bitset<64> DES::DES_decrypt(bitset<64> & cipher) {
bitset<64> plain;
bitset<64> currentBits;
bitset<32> left;
bitset<32> right;
bitset<32> newLeft;
//置换IP
for (int i = 0; i < 64; i++)
currentBits[i] = cipher[IP[i] - 1];
for (int i = 0; i < 32; i++)
left[i] = currentBits[i];
for (int i = 32; i < 64; i++)
right[i - 32] = currentBits[i];
//进入16轮迭代(子密钥逆序应用)
for (int round = 0; round < 16; round++)
{
newLeft = right;
right = left ^ F(right, subkey[15 - round]);
left = newLeft;
}
//合并
for (int i = 0; i < 32; i++)
plain[i] = right[i];
for (int i = 32; i < 64; i++)
plain[i] = left[i - 32];
//逆初始化置换
currentBits = plain;
for (int i = 0; i < 64; i++)
plain[i] = currentBits[IP_1[i] - 1];
return plain;
}
void DES::show_encryp() {
bitset<64> plain = char_to_bit(s.c_str());
key = char_to_bit(k.c_str());
// 生成16个子密钥
generateKeys();
// 密文写入 a.txt
bitset<64> cipher = DES_encryp(plain);
fstream file1;
file1.open("D://a.txt", ios::binary | ios::out);
file1.write((char*)&cipher, sizeof(cipher));
file1.close();
}
void DES::show_decrypt() {
// 读文件 a.txt
bitset<64> temp;
fstream file2;
file2.open("D://a.txt", ios::binary | ios::in);
file2.read((char*)&temp, sizeof(temp));
file2.close();
// 解密,并写入文件 b.txt
bitset<64> temp_plain = DES_decrypt(temp);
bitset<64> temp_1 = change(temp_plain);
file2.open("D://b.txt", ios::binary | ios::out);
file2.write((char*)&temp_1, sizeof(temp_1));
file2.close();
}
DES是典型的Feistel密码结构,因此解密过程与加密过程完全一样,只是输入不一样。注意子密钥逆序输入。
#include
#include
#include
#include"DES.h"
using namespace std;
int main() {
DES C;
string s = "computer";
string k = "01234567";
C.get_s(s);
C.get_key(k);
cout << "Start encryo..." << endl;
clock_t start = clock();
C.show_encryp(); //加密,生成密文并写入a.txt
clock_t finish = clock();
cout << "finish" << endl;
cout << "using " << (double)(finish - start) / CLOCKS_PER_SEC << "s." << endl;
cout << "\n";
cout << "start decrypt..." << endl;
start = clock();
C.show_decrypt(); //读取a.txt中的密文,解密,生成明文并写入b.txt
finish = clock();
cout << "finish" << endl;
cout << "using " << (double)(finish - start) / CLOCKS_PER_SEC << "s." << endl;
cin.get();
return 0;
}
源代码下载
https://download.csdn.net/download/weixin_42023723/11168167
(全文完)