一.认识hill密码

1.1基础认识

  Hill密码是一种经典的分组密码,使用线性代数的方法将每个字符映射到一个数字,并使用矩阵乘法来加密和解密文本。具体地说,Hill密码将明文分成n个字符一组,将每组看作是一个列向量,然后用一个n×n的可逆矩阵A对其进行乘法运算,得到一个新的列向量,这个新的列向量就是密文。要解密密文,只需使用A逆矩阵A^-1进行矩阵乘法即可。
  为了更好地保护加密数据的安全性,必须仔细选择矩阵A以确保Hill密码的安全性。如果矩阵A不是可逆矩阵或者选择的矩阵有某些规律可循,那么Hill密码就会很容易被破解。因此,在实际应用中,需要使用高质量的伪随机数生成器来生成随机矩阵,或者使用特定的加密标准中定义的固定矩阵。

1.2使用范围

虽然Hill密码相对简单,但它已经被广泛应用于一些领域,比如计算机网络安全、金融领域以及军事通信等。

二.基本原理

2.1 前提条件

由于Hill密码是一种线性密码,因此加密矩阵的选取至关重要。如果加密矩阵不合适,就可能导致加密后的密文不安全。为了保证密码的安全性,加密矩阵必须满足以下两个条件:

  • 加密矩阵必须是可逆的
  • 加密矩阵的行列式必须不为0

满足上述两个条件的加密矩阵可以通过随机生成或者其他复杂的方式来获得,从而提高Hill密码的安全性。

2.2加密步骤

具体地,假设要对一个长度为n的明文字符串进行Hill加密,分组大小为m,加密矩阵为K,则Hill加密的基本流程如下:

  • 对明文字符串进行填充,使其长度为m的倍数(可以使用空格、随机字符等进行填充);
  • 将填充后的明文字符串按照m个字符一组划分为n/m个明文分组,每个分组看作是一个m维列向量;
  • 将每个明文分组与加密矩阵K进行乘法运算,得到m维的密文列向量;
  • 将所有密文列向量拼接起来,得到加密后的密文字符串。

需要注意的是,加密矩阵K必须满足可逆性和行列式非零的条件,否则无法正常进行加密。

2.3解密步骤

Hill解密的基本步骤如下:

  • 将密文字符串按照分组大小m划分为若干个密文分组;
  • 对于每个密文分组,将其看作是一个m维列向量,并用加密矩阵K的逆矩阵K^-1进行乘法运算,得到对应的明文列向量;
  • 将所有明文列向量拼接起来即可得到原始明文字符串。

需要注意的是,加密矩阵K必须可逆的,才能求出其逆矩阵K^-1。如果加密矩阵K不可逆,则无法进行解密操作。除此之外,还需要保证加密和解密使用的是同一个加密矩阵K。

三.优缺点

3.1优点

Hill密码的优点

  • 采用了矩阵运算,加密复杂度较高,能够提供一定的安全性;
  • 可以处理长文本,不需要分组前进行打乱或置换等预处理,保留了明文的统计特征,增加了密码的难度;
  • 加密和解密过程可以通过矩阵运算实现,速度较快;
  • 密钥空间较大,取决于矩阵的大小和可用字符集。

3.2缺点

Hill密码的缺点

  • 此密码的安全性依赖于加密矩阵的选取,如果加密矩阵被泄漏,则密码将立即失去保护作用;
  • 对于不同的分组大小,需要使用不同的加密矩阵,这意味着需要为每个分组大小选择一个新的矩阵,这给加密和解密带来了额外的复杂性;
  • 容易受到纠错码攻击,即攻击者可以改变密文中某些字符,从而影响整个分组,进而影响解密结果;
  • 不适合用于对称密钥加密,因为当密钥过多时,加、解密的计算量会变得非常大。

四.补充

4.1参数说明

hill密码的解密过程中,存在求逆矩阵这一步,而要使其逆矩阵存在,基本要求是加密矩阵可逆

4.2字母表说明

abcdefghijklmnopqrstuvwxyz
012345678910111213141516171819202122232425

在本次实验中,我们定义每个字母代表的数字如上所示。

五.C语言实现

5.1主程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nijuzhen.h"        //导入我们自己写的求逆矩阵文件

#define Maxsize 100        //定义最大信息量
#define N 2                // 定义密匙矩阵的维度

/*这里是以下面的密匙矩阵为例子,实际上我们只知道加密矩阵,需要自己求解密矩阵*/
int keys[2][2]={{11,8},{3,7}};    //加密密匙
int illkeys[2][2]={{7,18},{23,11}};    //解密密匙

//矩阵乘法运算
void muiti(char *&p)
{
	for(int i=0;i<strlen(p);i++)
	{
		p[i]= p[i]+'a'-97*2;
	}
	int result[strlen(p)] = {0}; // 用于存储结果
	for (int i = 0; i < 2; i++) { // 遍历矩阵的行
		for (int j = 0; j < 2; j++) { // 遍历矩阵的列
			result[i] += p[j] * keys[j][i]; // 计算乘积并累加到结果数组中
		}
	}
	for(int i=0;i<strlen(p);i++)
	{
		printf("%c",(char)(result[i]%26+97));
	}
}

//逆矩阵乘法运算
void illmuiti(char *&p)
{
	for(int i=0;i<strlen(p);i++)
	{
		p[i]= p[i]+'a'-97*2;
	}
	int result[strlen(p)] = {0}; // 用于存储结果
	for (int i = 0; i < 2; i++) { // 遍历矩阵的行
		for (int j = 0; j < 2; j++) { // 遍历矩阵的列
			result[i] += p[j] * illkeys[j][i]; // 计算乘积并累加到结果数组中
		}
	}
	for(int i=0;i<strlen(p);i++)
	{
		printf("%c",(char)(result[i]%26+97));
	}
	
}

//加密函数
void jiami(char *&p)
{
	/*一般的hill密码都是n维列向量乘以一个n阶加密矩阵,但是有时候明文长度m远大于n,
	  不符合线性代数中矩阵乘法的原则,我们可以把分成若干个n维列向量,分别进行加密,最后把
	  加密的密文组合即可。*/
	if(strlen(p)%2==0)
	{
		int n=strlen(p)/2;
		char mimi[n][2];
		for(int i=0;i<strlen(p);i+=2)          //分割数组,两个一起构成新的数组
		{
			mimi[i/2][0]=p[i];
			mimi[i/2][1]=p[i+1];
		}
		printf("密文:");
		for(int j=0;j<n;j++)
		{
			char a=mimi[j][0];
			char b=mimi[j][1];
			char str[Maxsize];
			str[0]=a;
			str[1]=b;
			char *p =str;
			muiti(p);
		}
	}
	if(strlen(p)%2==1)
	{
		int n=strlen(p)/2+1;    //不能被2整除时,我们需要手动补位
		char mimi[n][2];
		for(int i=0;i<strlen(p);i+=2)          //分割数组,两个一起构成新的数组
		{
			mimi[i/2][0]=p[i];
			mimi[i/2][1]=p[i+1];
		}
		printf("密文:");
		for(int j=0;j<n;j++)           //每个分割出来的数组执行运算函数
		{
			char a=mimi[j][0];
			char b=mimi[j][1];
			char str[Maxsize];
			str[0]=a;
			str[1]=b;
			char *p =str;
			muiti(p);
		}
	}
	
}

//解密函数
void jiemi(char *&p)
{
	if(strlen(p)%2==0)
	{
		int n=strlen(p)/2;
		char mimi[n][2];
		for(int i=0;i<strlen(p);i+=2)          //分割数组,两个一起构成新的数组
		{
			mimi[i/2][0]=p[i];
			mimi[i/2][1]=p[i+1];
		}
		printf("明文:");
		for(int j=0;j<n;j++)
		{
			char a=mimi[j][0];
			char b=mimi[j][1];
			char str[Maxsize];
			str[0]=a;
			str[1]=b;
			char *p =str;
			illmuiti(p);
		}
	}
	if(strlen(p)%2==1)
	{
		int n=strlen(p)/2+1;    //不能被2整除时,我们需要手动补位
		char mimi[n][2];
		for(int i=0;i<strlen(p);i+=2)          //分割数组,两个一起构成新的数组
		{
			mimi[i/2][0]=p[i];
			mimi[i/2][1]=p[i+1];
		}
		printf("明文:");
		for(int j=0;j<n;j++)           //每个分割出来的数组执行运算函数
		{
			char a=mimi[j][0];
			char b=mimi[j][1];
			char str[Maxsize];
			str[0]=a;
			str[1]=b;
			char *p =str;
			illmuiti(p);
		}
	}
}


int main() 
{
	char str1[Maxsize],str2[Maxsize];
	char *p=str1,*q=str2;         //两个数组指针,分别代替明文、密文
	int a,b;
	printf("请输入明文:");
	scanf("%s",str1);
	jiami(p);
	printf("\n");
	printf("-----------------");
	printf("\n");
	printf("请输入密文:");
	scanf("%s",str2);
	jiemi(q);
}

5.2求逆矩阵.h文件

#include <stdio.h>

#define N 2 // 矩阵的维度

void print_matrix(int matrix[N][N]) {
	for(int i=0; i<N; i++) {
		for(int j=0; j<N; j++) {
			printf("%d ", matrix[i][j]);
		}
		printf("\n");
	}
}

int determinant(int a[N][N]) {
	return a[0][0]*a[1][1] - a[0][1]*a[1][0];
}

int inverse_matrix(int a[N][N], double b[N][N]) {
	int det = determinant(a);
	
	if(det == 0) {
		return 0;
	} else {
		b[0][0] = a[1][1]/det;
		b[0][1] = -a[0][1]/det;
		b[1][0] = -a[1][0]/det;
		b[1][1] = a[0][0]/det;
		return 1;
	}
}

5.3说明

这里的主程序为了方便模拟实验,博主在写代码时直接代入了一个加密矩阵和对应的解密矩阵,事实上我们可以通过第二部分求逆矩阵函数调用求逆矩阵。

六.运行结果

i1Ok.jpg
i8VO.jpg
博主这是用C语言实现的第三种加密方式了,如果感兴趣可以点击阅读其他的加密方式。
仿射密码
移位密码

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐