MFC 简单实现 DES 算法
前言
徐旭东老师说过学者就应该对知识抱有敬畏之心,所以我的博客的标题总喜欢加上“简单”二字,就是为了提醒自己,自己所学知识只是皮毛,离真理还远矣。
DES 算法
DES算法是密码体制中的对称密码体制,明文按64位进行分组,密钥长64位(其中有8位是奇偶校验位,不参与 DES 运算),参数有三个:key、data、mode。即要加密或者解密的数据、密钥、加密还是解密。
考虑到算法注重的是性能,且不涉及面向对象的思维,所以一开始选择 C 语言开发,但是为了良好的交互界面,所以最终选择了 MFC。
基础知识
DES 算法是对二进制数进行各种操作从而达到加密解密的作用,所以了解编码、字符和二进制数的概念非常重要。这也算是这个作业的意外收获吧~
首先一个字节对应一个8位的二进制数。
| 编码 | 英文字符(单位:字节) | 中文字符(单位:字节) | 备注 |
|---|---|---|---|
| ASCII | 1(有时候7位二进制数) | 无 | 不能满足其他国家需求 |
| GB2312 | 1 | 2 | 为了显示中文制定(属于 ANSI 编码,其他国家皆有自己的标准) |
| GBK | 1 | 2 | 支持人名、古汉语等方面出现的罕用字,如:溙 |
| Big5 | 1 | 2 | 支持繁体字 |
| GB18030 | 1 | 2 | 支持藏文、蒙文、维吾尔文等主要的少数民族文字 |
| UTF-16(标准的 Unicode) | 2 | 2 | 为了适应全球化,超出范围的使用两个UTF-16即4字节 |
| UTF-8(Unicode 的一种,变长编码) | 1 | 2-4(大部分占3个字节) | 保留 ASCII 字符的编码,存英文时节省空间 |
加密流程
1、输入64位的明文,按照以下 IP 置换表对明文进行置换:
int IP_Table[64] = { //64位明文初始置换 表IP
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,
56, 48, 40, 32, 24, 16, 8, 0,
58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6
};
//IP置换
int IP_Substitute(Type data[64]){
Type temp[64];
for (int i = 0; i < 64; i++){
temp[i] = data[IP_Table[i]];
}
memcpy(data, temp, 64);
return 0;
}
得到置换后的64位数据分成两份各32位,即 L0 和 R0( R0 也是下一轮的L1)。
2、32位的 R0 通过以下 E 位选择表扩充置换成48位:
int E_Table[48] = { //32位R扩充置换成48位 表E
31, 0, 1, 2, 3, 4,
3, 4, 5, 6, 7, 8,
7, 8, 9, 10, 11, 12,
11, 12, 13, 14, 15, 16,
15, 16, 17, 18, 19, 20,
19, 20, 21, 22, 23, 24,
23, 24, 25, 26, 27, 28,
27, 28, 29, 30, 31, 0
};
//32位R扩展置换成48位
Int E_Substitute(Type data[48]){
Type temp[48];
for (int i = 0; i < 48; i++){
temp[i] = data[E_Table[i]];
}
memcpy(data, temp, 48);
return 0;
}
R0 由32位扩充置换成48位,等待和子密钥进行运算。
3、输入64位的密钥,通过以下 PC_1 置换选择表去除第8,16,24,32,48,56,64位,即去除奇偶校验位后置换:
int PC_1_Table[56] = { //64位密钥置换压缩成56位 表1
56, 48, 40, 32, 24, 16, 8,
0, 57, 49, 41, 33, 25, 17,
9, 1, 58, 50, 42, 34, 26,
18, 10, 2, 59, 51, 43, 35,
62, 54, 46, 38, 30, 22, 14,
6, 61, 53, 45, 37, 29, 21,
13, 5, 60, 52, 44, 36, 28,
20, 12, 4, 27, 19, 11, 3
};
//64位密钥置换压缩成56位
int PC1_Compress(Type key[64], Type temp[56]){
for (int i = 0; i < 56; i++){
temp[i] = key[PC_1_Table[i]];
}
return 0;
}
得到56位的密钥分成两份,各28位,即 C0 和 D0。
4、C0 和 D0 分别按照循环左移表进行左移:
//循环左移次数
int MOVE[16] = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };
//循环左移
int LEFT_Shift(Type data[56], int time){
Type temp[56];
//保存将要循环移动到右边的位
memcpy(temp, data, time);
memcpy(temp + time, data + 28, time);
//前28位移动
memcpy(data, data + time, 28 - time);
memcpy(data + 28 - time, temp, time);
//后28位移动
memcpy(data + 28, data + 28 + time, 28 - time);
memcpy(data + 56 - time, temp + time, time);
return 0;
}
即左移一次得到 C1 和 D1,然后 C1 和 D1 左移1次得到 C2 和 D2,然后 C2 和 D2 左移2次得到 C3 和 D3,依次类推最后得到16个56位的密钥。这16个密钥再按照以下 PC_2 表进行置换压缩:
int PC_2_Table[48] = { //56位子密钥置换压缩成48位 表2
13, 16, 10, 23, 0, 4, 2, 27,
14, 5, 20, 9, 22, 18, 11, 3,
25, 7, 15, 6, 26, 19, 12, 1,
40, 51, 30, 36, 46, 54, 29, 39,
50, 44, 32, 46, 43, 48, 38, 55,
33, 52, 45, 41, 49, 35, 28, 31
};
//56位的子密钥置换压缩成48位
int PC2_Compress(Type key[56], Type temp[48]){
for (int i = 0; i < 48; i++){
temp[i] = key[PC_2_Table[i]];
}
return 0;
}
//生成16个子密钥
int MAKE_SubKeys(Type key[64], Type subKeys[16][48]){
Type temp[56];
PC1_Compress(key, temp);//PC1置换
for (int i = 0; i < 16; i++){//16轮跌代,产生16个子密钥
LEFT_Shift(temp, MOVE[i]);//循环左移
PC2_Compress(temp, subKeys[i]);//PC2置换,产生子密钥
}
return 0;
}
最终得到16个48位的子密钥:K1~K16。

5、R0 和 K0 进行异或运算得到48位的新的 R0 (8行6列),然后通过 S1~S8
表进行压缩(每行分别对应一个 S 表),最终压缩成8行4列,即32位,例如第一行:011010,第1和第6列组成00(十进制中的0),则对应 S1 表中第一行;中间4列组成1101(十进制中的13),则对应第14列,也就是9,最终011010被替换成1001,所以很明显 S 表中的数最大不会超过15。
//count=48:R与子密钥异或运算、count=32:R与L异或运算
int XOR(Type R[48], Type L[48], int count){
for (int i = 0; i < count; i++){
R[i] ^= L[i];
}
return 0;
}
int S[8][4][16] =
{ //8行6列R压缩成8行4列 S盒
//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 }
}
};
压缩后得到新的32位的 R0。
//S盒置换
int SBOX(Type data[48]){
int line, row, output;
int cur1, cur2;
for (int i = 0; i < 8; i++){
cur1 = i * 6;
cur2 = i << 2;
//计算在S盒中的行与列
line = (data[cur1] << 1) + data[cur1 + 5];
row = (data[cur1 + 1] << 3) + (data[cur1 + 2] << 2)
+ (data[cur1 + 3] << 1) + data[cur1 + 4];
output = S[i][line][row];
//化为2进制
data[cur2] = (output & 0X08) >> 3;
data[cur2 + 1] = (output & 0X04) >> 2;
data[cur2 + 2] = (output & 0X02) >> 1;
data[cur2 + 3] = output & 0x01;
}
return 0;
}

6、R0 通过32位的置换表 P 置换:
int P_Table[32] = { //32位R置换函数 表P
15, 6, 19, 20, 28, 11, 27, 16,
0, 14, 22, 25, 4, 17, 30, 9,
1, 7, 23, 13, 31, 26, 2, 8,
18, 12, 29, 5, 21, 10, 3, 24
};
//32位的R进行P置换
int P_Substitute(Type data[32]){
Type temp[32];
for (int i = 0; i < 32; i++){
temp[i] = data[P_Table[i]];
}
memcpy(data, temp, 32);
return 0;
}
得到新的 R0 和 L0 进行异或运算(目前为止前面除了K2到K16,其他值都用上了)得到 R1(也就是下一轮的 L2 );将一开始的 R0 赋值给 L1。于是得到了 L1 和 R1 ,使用 K1 循环上面的运算,知道把子密钥全部使用完。最终的得到 L16 和 R16,合并得到64位的密文。
//count=48:R与子密钥异或运算、count=32:R与L异或运算
int XOR(Type R[48], Type L[48], int count){
for (int i = 0; i < count; i++){
R[i] ^= L[i];
}
return 0;
}
//交换
int Swap(Type left[32], Type right[32]){
Type temp[32];
memcpy(temp, left, 32);
memcpy(left, right, 32);
memcpy(right, temp, 32);
return 0;
}
7、得到的密文通过 IP_1 逆置换表置换:
int IP_1_Table[64] = { //64位密文逆初始置换 表IP^-1
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,
32, 0, 40, 8, 48, 16, 56, 24
};
//IP逆置换
int IP_1_Substitute(Type data[64]){
Type temp[64];
for (int i = 0; i < 64; i++){
temp[i] = data[IP_1_Table[i]];
}
memcpy(data, temp, 64);
return 0;
}
置换后得到最终的加密结果,64位的密文。

解密流程:
由于加密的算法过程几乎是对称的,所以解密过程和加密过程几乎一样,只是在 R 和16个子密钥进行与或运算时,应该是从K16到K1。
图形界面:
Visual Studio->文件->新建->项目->MFC 应用程序:

基于对话框:

布局(其中两个 radio 成组):

给 Button 添加响应函数:
void CDESDlg::OnBnClickedOk()
{
// TODO: 在此添加控件通知处理程序代码
CString Spath, Dpath, Kpath;
GetDlgItemText(Source_path, Spath);
GetDlgItemText(Destination_path, Dpath);
GetDlgItemText(Key_path, Kpath);
char *Cspath = NULL, *Cdpath = NULL, *Ckpath = NULL;
bool correct = true;
if (!Spath.GetLength() || !Dpath.GetLength() || !Kpath.GetLength()){
MessageBox(_T("请选择文件!"));
correct = false;
}
else{
Cspath = (LPSTR)(LPCTSTR)Spath;
Cdpath = (LPSTR)(LPCTSTR)Dpath;
Ckpath = (LPSTR)(LPCTSTR)Kpath;
}
if (correct){
int state;
state = IsDlgButtonChecked(Encrypt);
clock_t a, b;
CString time;
if (state) {
a = clock();
DES_Encrypt(Cspath, Ckpath, Cdpath);
b = clock();
time.Format(_T("加密成功,耗时%lu毫秒。"), b - a);
MessageBox(time);
}
else {
a = clock();
DES_Decrypt(Cspath, Ckpath, Cdpath);
b = clock();
time.Format(_T("解密成功,耗时%lu毫秒。"), b - a);
MessageBox(time);
}
}
}
运行结果:
新建4个文本文件:明文、密钥、加密结果、解密结果。
明文和密钥中输入以下内容:

选择文件,进行加密解密:


加密和解密结果:

修改解密密钥重新解密:

MFC 简单实现 DES 算法的更多相关文章
- 在IOS中使用DES算法对Sqlite数据库进行内容加密存储并读取解密
在IOS中使用DES算法对Sqlite 数据库进行内容加密存储并读取解密 涉及知识点: 1.DES加密算法: 2.OC对Sqlite数据库的读写: 3.IOS APP文件存储的两种方式及读取方式. 以 ...
- AES算法,DES算法,RSA算法JAVA实现
1 AES算法 1.1 算法描述 1.1.1 设计思想 Rijndael密码的设计力求满足以下3条标准: ① 抵抗所有已知的攻击. ② 在多个平台上速度快,编码紧凑. ③ 设计 ...
- DES算法原理完整版
1.所需参数 key:8个字节共64位的工作密钥 data:8个字节共64位的需要被加密或被解密的数据 mode:DES工作方式,加密或者解密 2.初始置换 DES算法使用64位的密钥key将64位的 ...
- Web安全学习笔记之DES算法实例详解
转自http://www.hankcs.com/security/des-algorithm-illustrated.html 译自J. Orlin Grabbe的名作<DES Algorith ...
- DES算法解析
DES算法 美国国家标准局1973年开始研究除国防部外的其它部门的计算机系统的数据加密标准,于1973年5月15日和1974年8月27日先后两次向公众发出了征求加密算法的公告. 1977年1月,美国 ...
- DES算法与四种加密模式的代码实现(C++语言)
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/Love_Irelia97/article/ ...
- IDAPython实战项目——DES算法识别
在CTF的逆向中我们需要的是找到加密的主函数,结合了yara的识别原理,通过对常量数组的引用的查找,一步步递归构建调用树.调用树根部就是可能的密码算法主函数. 由于这种办法需要常量分布于算法的各个步骤 ...
- 聊聊密码学中的DES算法
用心分享,共同成长 没有什么比你每天进步一点点更实在了 本文已经收录至我的github,欢迎大家踊跃star 和 issues. https://github.com/midou-tech/artic ...
- 理解DES算法
首先 了解对称密码加密技术:采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密.但是有时候密钥不用完全相同 只要相似也可以.因为用一个密钥可 ...
随机推荐
- CodeForces 135C C. Zero-One
题目 题意: 一个01串,AB两个人轮流删去一个字符,直到只剩两个,A先手.最后剩的两位组成一个二进制数,A要使其最小,B要使其最大. 有一些部分不知道原来是什么,用?表示,求所有的可能里,最后剩下的 ...
- python-面向对象(二)
面向对象总结 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使用(可以讲多函数中公用的变量封装到对象中) 对象,根据模板创建的实例( ...
- 在Prefetcher中取消robots.txt的限制
Robots.txt是一种专门用于搜索引擎网络爬虫的文件,当构造一个网站时,如果作者希望该网站的内容被搜索引擎收录,就可以在网站中创建一个纯文本文件robots.txt,在这个文件中,声明该网站不想被 ...
- IOS8 不用计算Cell高度的TableView实现方案
这个新特性,意味着View被Autolayout调整frame后,会自动拉伸和收缩SupView. 具体到Cell,要求cell.contentView的四条边都与内部元素有约束关系. 在TableV ...
- ASP.NET MVC 音乐商店 - 目录
这一个系列的内容来自微软的音乐商店 Music Store, 这是项目在 Codeplex 上的地址:http://mvcmusicstore.codeplex.com/. 这个项目使用 ASP.NE ...
- javascript中this指针的认识
javascript中上下文环境就是this指针,即被调用函数所处的环境.这个上下文环境在大多数情况下指的是函数运行时封装这个函数的那个对象:当不通过任何对象单独调用一个函数时,上下文环境指的就是全局 ...
- Linux学习之开机启动
当我们打开计算机电源,计算机会自动从主板的BIOS(Basic Input/Output System)读取其中所存储的程序.这一程序通常知道一些直接连接在主板上的硬件(硬盘,网络接口,键盘,串口,并 ...
- Java的基本语法
Java基本语法格式 Java基本语法格式 Java中的所有程序代码都必须存在于一个类中,用class关键字定义类,在class前面可以有一些修饰符. 修饰符 class 类名{ 程序代码} 注: ① ...
- 【转】从框架看PHP的五种境界及各自的薪资待遇
无意中看到这篇文章,有些触动,作为博客开篇,用来激励自己. 原文地址:点击打开 在撰写此文前首先必须申明的是本人不鄙视任何一种框架,也无意于挑起PHP框架间的战争,更没有贬低某个框架使用者的用意,本文 ...
- php错误 mysql_query():supplied argument is not a valid MySQL result resource
出现这种错误,原因是出现该错误的函数的参数出现了问题 参数出现问题有多种情况: 1.sql查询语句有问题,可能多了一个逗号,少了一个逗号,多了括号之类的: 2.与数据库连接的参数有问题,用户名.密码. ...