希尔(加密-->解密)

希尔加密原理

在学习过程中,无意中接触到希尔加密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,构成n
3矩阵的形式。
第二步:根据第一步得到的矩阵,与密钥矩阵相乘得到的结果对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;
	}
}

结果为:
在这里插入图片描述

  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小鱼子爱吃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值