具体求解流程图可见C语言实现S-DES密码体制,这篇文章主要是谈谈在子密钥求解模块化过程中遇到的问题和解决办法,这个解决办法可以指导我们以后的代码书写。
代码:
#define _CRT_SECURE_NO_WARNINGS 1
//S-DES密码体制
#include<stdio.h>
//打印菜单
void menu() {
printf("------- S-DES --------\n");
printf("------- 1. 加密 --------\n");
printf("------- 2. 解密 --------\n");
printf("------- 0. 退出 --------\n");
}
//f函数
int f(int R[4], int key[8]) {//传R1/R2和K1/K2
int s[4] = { 0 }, s1[4] = { 0 }, s2[4] = { 0 }, f[4] = { 0 };
int temp = 0;
//定义两个S盒
int sh1[4][4] = { {1,0,3,2},
{3,2,1,0},
{0,2,1,3},
{3,1,0,2}
};
int sh2[4][4] = { {0,1,2,3},
{2,0,1,3},
{3,2,1,0},
{2,1,0,3}
};
//E/P扩位及置换 4 1 2 3 2 3 4 1
int R0[8] = { 0 };
int ret = 0;
R0[0] = R[3];
R0[1] = R[0];
R0[2] = R[1];
R0[3] = R[2];
R0[4] = R[1];
R0[5] = R[2];
R0[6] = R[3];
R0[7] = R[0];
//与传进的子秘钥异或
for (int i = 0; i < 8; i++)
{
ret = R0[i] ^ key[i];
R0[i] = ret;
}
//分为两段,查找对应S盒
for (int i = 0; i < 4; i++)
{
s1[i] = R0[i];
s2[i] = R0[i + 4];
}
//将查找S盒得到的数据十进制转化为二进制
s[0] = s1[0] * 2 + s1[3];
s[1] = s1[1] * 2 + s1[2];
temp = sh1[s[0]][s[1]];
s[0] = temp / 2;
s[1] = temp % 2;
s[2] = s2[0] * 2 + s2[3];
s[3] = s2[1] * 2 + s2[2];
temp = sh2[s[2]][s[3]];
s[2] = temp / 2;
s[3] = temp % 2;
//p4置换 2 4 3 1
f[0] = s[1];
f[1] = s[3];
f[2] = s[2];
f[3] = s[0];
return f;
}
//子秘钥生成
int subkey(int key[10],int key1[8],int key2[8]) {
//子秘钥定义
int p10[10] = { 0 };//p10置换
int LS_1[10] = { 0 };
int LS_2[10] = { 0 };
//p10置换 3 5 2 7 4 10 1 9 8 6
p10[0] = key[2];
p10[1] = key[4];
p10[2] = key[1];
p10[3] = key[6];
p10[4] = key[3];
p10[5] = key[9];
p10[6] = key[0];
p10[7] = key[8];
p10[8] = key[7];
p10[9] = key[5];
//左移一位(分两段移位)
for (int i = 0; i < 5; i++)
{
LS_1[i] = p10[(i + 1) % 5];
LS_1[i + 5] = p10[((i + 1) % 5) + 5];
}
//p8置换 6 3 7 4 8 5 10 9 求key1
key1[0] = LS_1[5];
key1[1] = LS_1[2];
key1[2] = LS_1[6];
key1[3] = LS_1[3];
key1[4] = LS_1[7];
key1[5] = LS_1[4];
key1[6] = LS_1[9];
key1[7] = LS_1[8];
//左移两位(在左移一位的基础上再左移两位)
for (int i = 0; i < 5; i++)
{
LS_2[i] = LS_1[(i + 2) % 5];
LS_2[i + 5] = LS_1[((i + 2) % 5) + 5];
}
//p8置换 6 3 7 4 8 5 10 9 求key2
key2[0] = LS_2[5];
key2[1] = LS_2[2];
key2[2] = LS_2[6];
key2[3] = LS_2[3];
key2[4] = LS_2[7];
key2[5] = LS_2[4];
key2[6] = LS_2[9];
key2[7] = LS_2[8];
return;
}
//加密
void encryption_and_decryption(int input) {
//加密定义
int m[8] = { 0 };//存贮明文(在解密时存储的是密文)
int c1[8] = { 0 };//存贮IP置换前的密文
int c[8] = { 0 };//存贮密文
int m1[8] = { 0 };//IP置换后
int L0[4] = { 0 }, R0[4] = { 0 };//定义L0和R0
int L1[4] = { 0 }, R1[4] = { 0 };
int L2[4] = { 0 }, R2[4] = { 0 };
//子秘钥定义
int key[10] = { 0 };
int key1[8] = { 0 };
int key2[8] = { 0 };
//f函数定义
int temp = 0;
int* pf = NULL;
//根据输入的值不同,选择打印不同内容
if (input == 1) {
//输入明文
printf("请输入二进制明文(以空格隔开):");
}
else
{
//输入密文
printf("请输入二进制密文(以空格隔开):");
}
//存储输入的明文或者密文
for (int i = 0; i < 8; i++)
{
scanf_s("%d", &m[i]);
}
//输入主密钥
printf("请输入主密钥(以空格隔开):");
for (int i = 0; i < 10; i++)
{
scanf_s("%d", &key[i]);
}
//调用求子秘钥的函数,进行子秘钥求解
subkey(key, key1, key2);
//IP置换(26314857)
m1[0] = m[1];
m1[1] = m[5];
m1[2] = m[2];
m1[3] = m[0];
m1[4] = m[3];
m1[5] = m[7];
m1[6] = m[4];
m1[7] = m[6];
//将输入的明文或者密文分为左右两个部分
for (int i = 0; i < 4; i++)
{
L0[i] = m1[i];
R0[i] = m1[i + 4];
L1[i] = m1[i + 4];//L1与R0相同
}
//加密和解密过程唯一的不同点就是key1和key2使用顺序的不同
if (input == 1) {
pf = f(R0, key1);
}
else
{
pf = f(R0, key2);
}
//用f函数和明文(密文)的左边部分异或
for (int i = 0; i < 4; i++)
{
temp = L0[i] ^ pf[i];
R1[i] = temp;
R2[i] = temp;//R2和R1一样
}
//第二次
if (input == 1) {
pf = f(R1, key2);
}
else
{
pf = f(R1, key1);
}
for (int i = 0; i < 4; i++)
{
temp = L1[i] ^ pf[i];
L2[i] = temp;
}
//将左右两部分合并到c1
for (int i = 0; i < 4; i++)
{
c1[i] = L2[i];
c1[i + 4] = R2[i];
}
//IP逆置换 4 1 3 5 7 2 8 6 得到密文(明文)
c[0] = c1[3];
c[1] = c1[0];
c[2] = c1[2];
c[3] = c1[4];
c[4] = c1[6];
c[5] = c1[1];
c[6] = c1[7];
c[7] = c1[5];
if (input == 1) {
printf("密文是:");
}
else
{
printf("明文是:");
}
for (int i = 0; i < 8; i++)
{
printf("%d ", c[i]);
}
printf("\n");
}
void test() {
int input = 0;
do {
menu();
printf("请选择:->");
scanf_s("%d", &input);
switch (input)
{
case 1:
encryption_and_decryption(input);
break;
case 2:
encryption_and_decryption(input);
break;
case 0:
printf("已退出加密解密系统\n");
break;
default:
printf("输入有误,请重新输入!\n");
break;
}
} while (input);
}
int main() {
test();
return 0;
}
这次的代码和之前发的那篇唯一的区别在于这次的子密钥求解单独成一个函数。在没与老师交流前我的想法是用一个指针数组接收返回的两个子密钥key1和key2。定义为int (*pkey)[2]={key1,key2};在运行的时候发生了错误,但是在调试过程中显示的错误是我没有独立求解子密钥可以通过的位置。问老师后没有继续用指针,如有读者知道怎么用指针解决,欢迎告诉我!
下面讲讲老师的方法。在加密解密算法中定义主密钥key[10]数组和子密钥key1[8]、key2[8]两个数组。然后通过for循环输入主密钥存储在key[10]中。在调用子密钥求解算法的时候,将key[10]和两个空的子密钥数组key1、key2作为参数传给子密钥求解函数。这样在函数运行完的时候,两个子密钥已经算好放在key1和key2两个数组中了,后面要用的时候直接用就好。不用像指针那么复杂。
语言描述能力有限,而且可能之前你也是这么做的,但是这个技巧对于我来说恍然大悟,所以想分享给大家。
如果内容对你有帮助,关注我,给我点个小小的赞吧!
若内容有误,请指正并多多包容,谢谢。