希尔加密原理
在学习过程中,无意中接触到希尔加密C++语言程序的编写,在这个过程中,遇到了很多无知的错误,比如说:希尔斯加密的原理混淆了,不会求伴随矩阵,不会求矩阵的逆,不会计算两矩阵相乘,使用string定义的变量不能与char定义的变量进行数据的交换,等等一些问题。不过在最后,还是使用C++编写出来了,不过在这里,我不得不抱怨一下哈,之前写C习惯使用的输入输出scanf()和printf()在Visual Studio 中编译不通过,无奈之下使用了C++编译的习惯,使用了std::cout与std::cin进行编写,但是在我写这个代码的时候,emmm因为"甲方"必须要我使用scanf()和printf(),我最后更换编译解决了这个问题。emmm本人还是个菜鸟,技术不过关,希望大家不要喷我。
希尔原理
希尔密码(Hill Cipher)是运用基本矩阵论原理的替换密码,由Lester S. Hill在1929年发明。每个字母当作26进制数字:A=0, B=1, C=2… 一串字母当成n维向量,跟一个n×n的矩阵相乘,再将得出的结果MOD26。
下面通过一个简单的例子介绍一下希尔斯加密与加密的过程。
例如:当我们需要对QianJiu进行加密处理时。
首先,我们需要创建一个密钥矩阵,这个矩阵必须是nn列的可逆矩阵,切必须是mod26的矩阵,当然,这个矩阵的逆矩阵里面的元素必须是整数。
在这里,我们创建一个3*3的可逆矩阵:
接下来,我们来学习一下加密过程**:
第一步:将需要将被加密的明文转换成数字,例如A = 0 , B = 1 , a = 0 , b = 1。
根据这个规律,我们可以把明文“QianJiu”转化成数据为:
(16 8 0 13 9 8 20)
然而,使用程序怎么将字符串进行转化呢?下面给出C语言参考代码。
#include<stdio.h>
void main()
{
char str[10] = "QianJiu";//明文字符串
int i;
for(i = 0 ; i < 10 ; i ++)//遍历
{
if(str[i] >= 'A' && str[i] <= 'Z')//当字符在A~Z之间时 将字符对应的ASCLL减去A对应的ASCLL
printf("%d\t",str[i]-'A');//可以将该数据保存在数组中
if(str[i] >= 'a' && str[i] <= 'z')//当字符在a~z之间时 将字符对应的ASCLL减去z对应的ASCLL
printf("%d\t",str[i]-'a');//可以将该数据保存在数组中
}
}
输出结果为:
得到这组数据之后,我们需要将这组数据转变成一个矩阵,在这里值得注意的是。当我们的密钥矩阵是22的矩阵时,我们的明文矩阵必须是n2的矩阵,当我们的密钥矩阵是33的矩阵时,我们的明文矩阵必须是n3的矩阵,因此呢,我们需要将(16 8 0 13 9 8 20)转化成n3的矩阵,如下图所示:
我们可以看出,当字符的个数不是3的倍数时,需要在后面补0,构成n3矩阵的形式。
第二步:根据第一步得到的矩阵,与密钥矩阵相乘得到的结果对26取余数就是我们字符串加密之后得到的矩阵。
举例如下:
上图所示,矩阵的运算结果为:
我们还需要对矩阵运算的结果对26取余,得到的矩阵如下:
C代码参考代码实现如下:
#include<stdio.h>
void main()
{
int use_str2int[3][3] = {{16,8,0},{13,9,8},{20,0,0}};
int key[3][3] = {{1,1,0},{1,1,1},{0,1,1}};
int Temp[3][3];
int i,j,s;
for ( i = 0; i < 3; i++)
{
for ( j = 0; j < 3; j++)
{
Temp[i][j] = 0;
for ( s = 0; s < 3; s++)
{
Temp[i][j] += use_str2int[i][s] * key[s][j];
}
}
}
for(i = 0 ; i < 3 ; i ++)
{
for(j = 0 ; j < 3 ; j ++)
{
printf("%d\t",Temp[i][j]%26);
}
printf("\n");
}
}
输出结果为:
第三步:将第二步得到的矩阵转化成字符串。我的做法是将二位矩阵转化成一维矩阵,因为有大小写的区别,所以需要点对点的进行转化。注意的是,之前是大写,加密之后依然是大写,以前是小写,加密之后依然是小写,若字符串中存在空格,则不需要进行转化。
例如,明文为“QianJiu”,第一个字母是大写的Q,那么转化后依然是大写,根据之前字符到数据的转化规律,Y= 24就是“Q”转化之后的结果:
使用C语言完成该功能,实现代码参考如下:
#include<stdio.h>
void main()
{
char str[10] = "QianJiu";
int Temp[3][3] = {{24,24,8},{22,4,17},{20,20,0}};
int Temp1[9];
int i = 0,count = 0,j = 0;
int len = 0;//计算字符串长度
while(str[i] != 0)
{
len++;
i++;
}
//将二维矩阵转化成一维矩阵
for(i = 0 ; i < 3 ; i++)
{
for(j = 0 ; j < 3 ; j++)
{
Temp1[count] = Temp[i][j];
count++;
}
}
for(i = 0 ; i < len ; i ++)
{
if(str[i] >= 'A' && str[i] <= 'Z')
printf("%c",Temp1[i]+'A');
else if(str[i] >= 'a' && str[i] <= 'z')
printf("%c",Temp1[i]+'a');
else
printf("%c",Temp1[i]);
}
}
其加密后的字符串结果为:YyiwEru
接下来,我们来学习一下解密过程:
第一步:我们需要对YyiwEru进行解密,需要将这个字符串转化成对应的数,与加密对应关系相同。转化后的结果为:(24 24 8 22 4 17 20)。
实现代码如下:
#include<stdio.h>
void main()
{
char str[10] = "YyiwEru";//明文字符串
int i;
for(i = 0 ; i < 10 ; i ++)//遍历
{
if(str[i] >= 'A' && str[i] <= 'Z')//当字符在A~Z之间时 将字符对应的ASCLL减去A对应的ASCLL
printf("%d\t",str[i]-'A');//可以将该数据保存在数组中
if(str[i] >= 'a' && str[i] <= 'z')//当字符在a~z之间时 将字符对应的ASCLL减去z对应的ASCLL
printf("%d\t",str[i]-'a');//可以将该数据保存在数组中
}
}
第二步:第加密相同,需要将第一步转化成n*3的矩阵,结果后的结果乘以密钥矩阵的逆。
过程如下:
注意的是,需要对结果对26取余数
C语言实现代码如下:
#include<stdio.h>
void main()
{
int use_str2int[3][3] = {{24,24,8},{22,4,17},{20,20,0}};
int key[3][3] = {{0,1,-1},{1,-1,1},{-1,1,0}};
int Temp[3][3];
int i,j,s;
for ( i = 0; i < 3; i++)
{
for ( j = 0; j < 3; j++)
{
Temp[i][j] = 0;
for ( s = 0; s < 3; s++)
{
Temp[i][j] += use_str2int[i][s] * key[s][j];
}
}
}
for(i = 0 ; i < 3 ; i ++)
{
for(j = 0 ; j < 3 ; j ++)
{
printf("%d\t",(Temp[i][j]+26)%26);
}
printf("\n");
}
}
第三步:将第二步得到的矩阵转化成字符串。
使用C语言完成该功能,实现代码参考如下:
#include<stdio.h>
void main()
{
char str[10] = "YyiwEru";
int Temp[3][3] = {{16,8,0},{13,9,8},{20,0,0}};
int Temp1[9];
int i = 0,count = 0,j = 0;
int len = 0;//计算字符串长度
while(str[i] != 0)
{
len++;
i++;
}
//将二维矩阵转化成一维矩阵
for(i = 0 ; i < 3 ; i++)
{
for(j = 0 ; j < 3 ; j++)
{
Temp1[count] = Temp[i][j];
count++;
}
}
for(i = 0 ; i < len ; i ++)
{
if(str[i] >= 'A' && str[i] <= 'Z')
printf("%c",Temp1[i]+'A');
else if(str[i] >= 'a' && str[i] <= 'z')
printf("%c",Temp1[i]+'a');
else
printf("%c",Temp1[i]);
}
}
结果为:
综合代码
我们将这些所有的代码进行整合,就得到了希尔斯加密与解密的全部代码。在这个项目中,既可以电脑生成密钥矩阵,也可以使用手动输入矩阵,但是,手动输入需要判断是否可逆,且逆矩阵是否含有小数。
参考代码如下:
#include <iostream>
#include <string>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
//函数的声明
void Creat_reversible(void);//创建一个密钥 即可逆矩阵
//int abs(int temp);//求绝对值
void Get_matrix_Rever(void);//求解矩阵的逆
int Get_Covalent(int m, int n);//计算矩阵的代数余子式
void Str2int_function(string str);//将得到的字符串进行加密
void Str_encryption(string str);//字符串加密
void Str_Decryption(string str);//字符串解密
void matrix_Trans(void);//矩阵对26取余
int Mod(int temp);
int abs_matrix(int temp);//求绝对值函数
int matrix_multiple(void);//判断行列式的值是否为矩阵的公约数
void GCD(void);//创建一个模26的可逆矩阵
string use_str = "I am from China";//用户输入的代编码字符串
string mid_str;
//可逆矩阵
int key[3][3];
//矩阵的逆
int key_inv[3][3];
//可逆矩阵的逆
int Pro_Rever;
int use_str2int[10][3] = { 0 };//创建一个将字符传化为整形
int line;//字符串转变为矩阵所对应的行数
//主函数
int main()
{
int temp;
printf("明文为:");
for (int i = 0; i < use_str.length(); i++)
printf("%c", use_str[i]);
printf("\n");
//GCD();
//输入可逆矩阵
printf("请输入密钥矩阵:\n");
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
scanf("%d", &key[i][j]);
}
}
temp = key[0][0] * key[1][1] * key[2][2] - key[0][0] * key[1][2] * key[2][1]
+ key[0][1] * key[1][2] * key[2][0] - key[0][1] * key[1][0] * key[2][2]
+ key[0][2] * key[1][0] * key[2][1] - key[0][2] * key[1][1] * key[2][0];
while (1)
{
//printf("您输入的矩阵不可逆\n");
//输入可逆矩阵
if(temp != 0 )
{
Pro_Rever = temp;
if(0 == matrix_multiple())
break;
}
if(temp == 0)
printf("矩阵不可逆请重新输入\n");
if(temp != 0)
{
Pro_Rever = temp;
if(1 == matrix_multiple())
printf("矩阵的逆存在小数,请重新输入\n");
}
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
scanf("%d", &key[i][j]);
}
}
temp = key[0][0] * key[1][1] * key[2][2] - key[0][0] * key[1][2] * key[2][1]
+ key[0][1] * key[1][2] * key[2][0] - key[0][1] * key[1][0] * key[2][2]
+ key[0][2] * key[1][0] * key[2][1] - key[0][2] * key[1][1] * key[2][0];
}
printf("您输入的矩阵可逆\n");
Get_matrix_Rever();//求矩阵的逆
Str2int_function(use_str);//将该字符串进行
Str_encryption(use_str);
Str2int_function(mid_str);//将该字符串进行
Str_Decryption(mid_str);
return 0;
}
int matrix_multiple(void)//判断行列式的值是否为矩阵的公约数
{
for(int i = 0 ; i < 3 ; i ++)
{
for(int j = 0 ; j < 3 ; j ++)
{
if(key[i][j] % Pro_Rever != 0)
return 1;
}
}
return 0;
}
void matrix_Trans(void)//矩阵对26取余
{
for (int i = 0; i < line; i++)
{
for (int j = 0; j < 3; j++)
{
use_str2int[i][j] = use_str2int[i][j] % 26;
}
}
}
void Str_Decryption(string str)//字符串解密
{
int Temp[20][3] = {0};//创建一个二维矩阵,将其矩阵的成绩存入该数组
int Temp1[100] = {0};
int count = 0, count1 = 0;
int str2int_x = 0, str2int_y = 0;
string result;
for (int i = 0; i < line; i++)
{
for (int j = 0; j < 3; j++)
{
Temp[i][j] = 0;
for (int s = 0; s < 3; s++)
{
Temp[i][j] += use_str2int[i][s] * key_inv[s][j];
}
}
}
//将二位数组铺成一维数组
while (count1 < 3 * line)
{
Temp1[count1] = Temp[str2int_x][str2int_y] % 26;
//cout << Temp1[count1] << endl;
str2int_y++;
count1++;
if (str2int_y == 3)
{
str2int_y = 0;
str2int_x++;
}
}
count1 = 0;
while (count < str.length())
{
if (str[count] >= 'A' && str[count] <= 'Z')
result += Mod(Temp1[count1]) + 'A', count1++;
else if (str[count] >= 'a' && str[count] <= 'z')
result += Mod(Temp1[count1]) + 'a', count1++;
else
result += str[count];
count++;
}
printf("解密后的密文为:");
for (int i = 0; i < str.length(); i++)
{
printf("%c", result[i]);
}
printf("\n");
}
int Mod(int temp)
{
return temp >= 0 ? temp % 26 : (26 + temp % 26);
}
void Str_encryption(string str)//字符串加密
{
int Temp[20][3] = {0};//创建一个二维矩阵,将其矩阵的成绩存入该数组
int Temp1[100] = {0};
int count = 0, count1 = 0;
int str2int_x = 0, str2int_y = 0;
string result;
for (int i = 0; i < line; i++)
{
for (int j = 0; j < 3; j++)
{
Temp[i][j] = 0;
for (int s = 0; s < 3; s++)
{
Temp[i][j] += use_str2int[i][s] * key[s][j];
}
}
}
//将二位数组铺成一维数组
while (count1 < 3 * line)
{
Temp1[count1] = Temp[str2int_x][str2int_y] % 26;
str2int_y++;
count1++;
if (str2int_y == 3)
{
str2int_y = 0;
str2int_x++;
}
}
count1 = 0;
while (count < str.length())
{
if (str[count] >= 'A' && str[count] <= 'Z')
result += Mod(Temp1[count1]) + 'A', count1++;
else if (str[count] >= 'a' && str[count] <= 'z')
result += Mod(Temp1[count1]) + 'a', count1++;
else
result += str[count];
count++;
}
printf("加密后的密文为:");
for (int i = 0; i < str.length(); i++)
{
printf("%c", result[i]);
}
printf("\n");
mid_str = result;
}
void Str2int_function(string str)//将得到的字符串转化为int数组
{
int str2int_x = 0, str2int_y = 0;
int len = str.length();
for (int i = 0; i < len; i++)
{
if ((str[i] >= 'A' && str[i] <= 'Z') || (str[i] >= 'a' && str[i] <= 'z'))
{
if (str[i] >= 'A' && str[i] <= 'Z')
use_str2int[str2int_x][str2int_y] = str[i] - 'A';
if (str[i] >= 'a' && str[i] <= 'z')
use_str2int[str2int_x][str2int_y] = str[i] - 'a';
str2int_y++;
if (str2int_y == 3)
{
str2int_y = 0;
str2int_x++;
}
}
}
line = str2int_x;//将行数赋值为全局变量
}
//创建一个密钥 即可逆矩阵
void Creat_reversible(void)
{
int temp = 0;
while (temp == 0 || temp > 5 || temp < -5)
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
key[i][j] = rand() % 2;
}
}
temp = key[0][0] * key[1][1] * key[2][2] - key[0][0] * key[1][2] * key[2][1]
+ key[0][1] * key[1][2] * key[2][0] - key[0][1] * key[1][0] * key[2][2]
+ key[0][2] * key[1][0] * key[2][1] - key[0][2] * key[1][1] * key[2][0];
}
Pro_Rever = temp;
}
int Get_Covalent(int m, int n)//计算矩阵的代数余子式
{
int temp[2][2] = { 0 };
int temp_x = 0, temp_y = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if (j != n && i != m)
{
temp[temp_x][temp_y] = key[i][j];
temp_y++;
}
}
if (i != m)
temp_x++;
temp_y = 0;
}
return (pow(-1, m + n) * ((temp[0][0] * temp[1][1] - temp[0][1] * temp[1][0]) / Pro_Rever));
}
int abs_matrix(int temp)//求绝对值函数
{
if(temp >= 0)
return temp;
else
return -temp;
}
void Get_matrix_Rever(void)//求解矩阵的逆
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
key_inv[j][i] = Get_Covalent(i, j);
}
}
}
void GCD(void)//创建一个模26的可逆矩阵
{
int a, b = 26;
int temp;
while (1)
{
//Creat_reversible();
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
scanf("%d", &key[i][j]);
}
}
temp = key[0][0] * key[1][1] * key[2][2] - key[0][0] * key[1][2] * key[2][1]
+ key[0][1] * key[1][2] * key[2][0] - key[0][1] * key[1][0] * key[2][2]
+ key[0][2] * key[1][0] * key[2][1] - key[0][2] * key[1][1] * key[2][0];
while (temp == 0)
{
//printf("您输入的矩阵不可逆\n");
//输入可逆矩阵
printf("矩阵不可逆请重新输入\n");
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
scanf("%d", &key[i][j]);
}
}
temp = key[0][0] * key[1][1] * key[2][2] - key[0][0] * key[1][2] * key[2][1]
+ key[0][1] * key[1][2] * key[2][0] - key[0][1] * key[1][0] * key[2][2]
+ key[0][2] * key[1][0] * key[2][1] - key[0][2] * key[1][1] * key[2][0];
}
Pro_Rever = temp;
a = Pro_Rever;
if (a < b)
{
temp = a;
a = b;
b = temp;
}
while (a % b)
{
temp = b;
b = a % b;
a = temp;
}
if (b == 1)
break;
}
}
结果为: