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算法
首先 了解对称密码加密技术:采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密.但是有时候密钥不用完全相同 只要相似也可以.因为用一个密钥可 ...
随机推荐
- [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(七)
目标: 1. 完成最终的设备驱动,增加具体的watchdog设备操作的代码. 测试代码: 代码最终实现见cwd_demo.c 代码只实现了read与write. 没有实现ioctl. 因此,我们可以 ...
- Ubuntu adb devices 出现??? no permissions 的解决方法
在ubuntu 12.10下运行adb devices出现: List of devices attached ???????????? no permissions 1.用命令: lsusb 以 ...
- cocos2d-x获取系统时间
欢迎转载,本帖地址:http://blog.csdn.net/jinjian2009/article/details/9449585 之前使用过cocos2d-x获取系统时间,毫秒级的 long ge ...
- Neo4j数据库简单
作为世界上先进的地图数据库,Neo4j如今,公司已成为许多互联网的首选.Neo4j它是基于java开源地图数据库开发,另外一个NoSQL数据库.Neo4j在保证对数据关系的良好刻画的同一时候.还支持传 ...
- 2014.8.18for循环
for循环 1.初始状态 2.循环条件 3.循环体 4.状态改变 语法 for( 初始状态 ; 循环条件 ; 状态改变 ) { 循环体; } eg: ; i <= ; i++) { Con ...
- java 正则表达式抽取
package com.achun.test; import java.util.regex.Matcher;import java.util.regex.Pattern; public class ...
- FSM(有限状态机)
游戏引擎是有限状态机最为成功的应用领域之一,由于设计良好的状态机能够被用来取代部分的人工智能算法,因此游戏中的每个角色或者器件都有可能内嵌一个状态机.考虑RPG游戏中城门这样一个简单对象,它具有打开( ...
- [置顶] ZK高级特性:Style定制与客户端集成
1.ZK与传统MVC框架的集成 由于ZK应用本质上也是基于标准Web技术Servlet框架,因此与其它MVC框架的集成没有什么特别的, 以一个典型场景为例——为一个现有的Web项目(前端采用WebWo ...
- hdu 4455 Substrings(找规律&DP)
Substrings Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tota ...
- 公选网站作业4_2.php
序: 此实验的功能是只有已经登陆或者已经注册的用户才有发表留言的资格,否则只能够浏览留言. 1. 主要文件: 4_2login.html// 登陆的主页面 4_2login.php //处理登 ...