实验五、古典密码(中)
实验目的和原理简介参见博客:古典密码(上)
一、实验内容
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月。