实验五、古典密码(中)

实验目的和原理简介参见博客:古典密码(上)

一、实验内容

1、弗纳姆密码(Vernam Cipher)

(1)、算法原理

加密原理:
加密过程可以用方程 ci = pi (+) ki 表示,其中 pi 是明文第 i 个二进制位,ki 是密钥第 i 个二进制位,ci 是密文第 i 个二进制位,(+) 是异或运算符。密文是通过对明文和密钥的逐位异或而成的。

解密原理:
根据异或运算的性质,解密过程可以用方程 pi = ci (+) ki 表示,其中 pi 是明文第 i 个二进制位,ki 是密钥第 i 个二进制位,ci 是密文第i个二进制位,(+) 是异或运算符。

(2)、算法的代码实现(C语言)

使用文件进行读/写,而非从终端中直接输入明/密文。(此程序存在bug,正在修复中)

#include <stdio.h>
#include <string.h>

char plaintext[10010] = { 0 };  // 明文
char ciphertext[10010] = { 0 };  // 密文

char key[10010] = { 0 };  // 密钥

void encrypt(char plaintext[], char key[]);  // 加密函数
void decrypt(char ciphertext[], char key[]);  // 解密函数

int main()
{
	char a;
	int i = 0;
	
	printf("该程序实现弗纳姆密码。请输入密钥:\n");
	gets(key);
	
	
	int input;
	printf("请输入选项:1为加密;2为解密\n");
	scanf("%d", &input);
	
	if(input == 1)
	{
		// 加密
		printf("******加密中******\n\n");
		encrypt(plaintext, key);
		printf("******加密完成******\n");
	}
	
	else if(input == 2)
	{
		// 解密
		printf("******解密中******\n\n");
		decrypt(ciphertext, key);
		printf("******解密完成******\n");
	}
	
	else
		printf("错误的选项(只能为1或2)。程序退出。");
	
	return 0;
}

void encrypt(char plaintext[], char key[])
{
	FILE *in, *out;
	in = fopen("input1.txt", "r");
	out = fopen("output1.txt", "w");
	char a;
	int i;
	int j;
	while((a = fgetc(in)) != EOF)
	{
		plaintext[i ++] = a;
	}
	for(j = 0;j < i;j ++)
	{
		ciphertext[j] = plaintext[j] ^ key[j % strlen(key)];
	}
	fputs(ciphertext, out);
	fclose(in);
	fclose(out);
	return;
}

void decrypt(char ciphertext[], char key[])
{
	FILE *in, *out;
	in = fopen("input2.txt", "r");
	out = fopen("output2.txt", "w");
	char a;
	int i;
	int j;
	while((a = fgetc(in)) != EOF)
	{
		ciphertext[i ++] = a;
	}
	for(j = 0;j < i;j ++)
	{
		plaintext[j] = ciphertext[j] ^ key[j % strlen(key)];
	}
	fputs(plaintext, out);
	fclose(in);
	fclose(out);
	return;	
}

(3)、算法测试

加密过程演示:

明文:(位于input1.txt中):

Here lies a toppled god - His fall was not a small one. We did but build his pedestal, A narrow and a tall one. - Tleilaxu Epigram

(语出弗兰克·赫伯特 Frank Herbert 的《沙丘:救世主 Dune: Messiah》,简单翻译过来是:这里躺着一个被推翻的神明——他轰然倒下。我们未曾有他助——只是将他供奉起来,让他如履薄冰。)

密钥:crucible (考验)

(插一句,借鉴(特别是二战)历史和经典谍战电影,双方在加密通信时使用某本书中约定好的某个单词,这里crucible是《沙丘:救世主》(原)书第125页的第1个单词。)

密文:(位于output1.txt中,为非打印(可见)字符。)

运行截图:

在这里插入图片描述

解密过程演示(恢复明文):

密文:(位于input2.txt中,内容为加密后的输出)

密钥:crucible(和加密过程所用密钥相同)

明文:(位于output2.txt中):

Here lies a toppled god - His fall was not a small one. We did but build his pedestal, A narrow and a tall one. - Tleilaxu Epigram

运行截图:

在这里插入图片描述

2、栅栏密码(Fence Cipher)

(1)、算法原理

加密原理:
栅栏密码按照列的顺序将明文(去掉空格)写入m行n列的数组,按照行的顺序将字符重新组合得到密文,这种方式称为m栏栅栏密码。

解密原理:
首先将密文分成n组,n为数组的行数,然后按照列的顺序将密文进行重新组合,最后将组合后的字符拼接起来,得到密文。

直观举例:

明文:
One Ring to rule them all, One Ring to find them, One Ring to bring them all and in the darkness bind them…

(语出J.R.R. 托尔金《魔戒 The Lord of the Rings》卷首,拙劣翻译为:一戒统御众人,一戒寻其踪迹,一戒召其而来,将其束于黑暗。)

首先去掉标点,变成:
OneRingtorulethemallOneRingtofindthemOneRingtobringthemallandinthedarknessbindthem,一个长“单词”,共82个字母:

在这里插入图片描述

写成一个10行9列(多余2个字母)的矩阵,竖读为明文,横读为密文:

在这里插入图片描述

(2)、算法的代码实现(C语言)

#include <stdio.h>
#include <string.h>

char plaintext[10010] = { 0 };
char ciphertext[10010] = { 0 };
char table[10010][10010] = { '*' };

void encrypt(char plaintext[], int n);
void decrypt(char ciphertext[], int n);

int n;  //行数 
int i, j, k;
int a, b; //len = a * n + b
int len;  //明文、密文的长度 

int main()
{	
	int input;
	printf("该程序实现栅栏密码。请输入选项:1为加密;2为解密\n");
	scanf("%d", &input);
	
	if(input == 1)
	{
		// 加密
		printf("请输入要加密的明文:\n");
		scanf("%s", plaintext);
		printf("请输入行数:\n");
		scanf("%d", &n);
		printf("加密结果:\n");
		encrypt(plaintext, n);
	}
	
	else if(input == 2)
	{
		// 解密
		printf("请输入要解密的密文:\n");
		scanf("%s", ciphertext);
		printf("请输入行数:\n");
		scanf("%d", &n);
		printf("解密结果:\n");
		decrypt(ciphertext, n);
	}
	
	else
		printf("错误的选项(只能为1或2)。程序退出。");
	
	return 0;
}

void encrypt(char plaintext[], int n)
{
	for(j = 0;j < n;j ++)
	{
		for(i = 0;i < strlen(plaintext);i ++)
		{
		    if(i % n == j)
			{
		    	putchar(plaintext[i]);
			}
		}
	}
	return;
}

void decrypt(char ciphertext[], int n)
{
	k = 0;
	len = strlen(ciphertext);
	a = len / n;
	b = len - a * n;
	//printf("%d %d", a, b);
	for(i = 0;i < b;i ++)
	{
		for(j = 0;j < a + 1;j ++)
		{
			table[i][j] = ciphertext[k ++];
		}
	}
	for(i = b;i < n;i ++)
	{
		for(j = 0;j < a;j ++)
		{
			table[i][j] = ciphertext[k ++];
		}
	}
	for(j = 0;j < a;j ++)
	{
		for(i = 0;i < n;i ++)
		{
			putchar(table[i][j]);
		}
	}
	for(i = 0;i < b;i ++)
	{
		putchar(table[i][a]);
	}
	return;
}

(3)、算法测试

使用上述例子中的明文进行测试,n(行数)为10.

加密过程:

在这里插入图片描述

解密过程:

在这里插入图片描述

成功恢复出明文。

二、参考文献

1、《密码编码学与网络安全——原理与实践(第七版)》(Cryptography and Network Security, Principles and Practice, Seventh Edition),【美】威廉 斯托林斯 William Stallings 著,王后珍等 译,北京,电子工业出版社,2017年12月。

2、《密码学实验教程》,郭华 刘建伟等 主编,北京,电子工业出版社,2021年1月。

更多推荐