在古典密码学中,有一种密码叫维吉尼亚密码,为了生成密码,需要使用表格法。这一表格(如图1所示)包括了26行字母表(大写),每一行都由前一行向左偏移一位得到。具体使用哪一行字母表进行编译是基于密钥进行的,在过程中会不断地变换。例如,假设明文为:ATTACKATDAWN选择某一关键词并重复而得到密钥,如关键词为LEMON时,密钥为:LEMONLEMONLE对于明文的第一个字母A,对应密钥的第一个字母L,于是使用表格中L行字母表进行加密,得到密文第一个字母L。类似地,明文第二个字母为T,在表格中使用对应的E行进行加密,得到密文第二个字母X。以此类推,可以得到:
明文:ATTACKATDAWN
密钥:LEMONLEMONLE
密文:LXFOPVEFRNHR
解密的过程则与加密相反。例如:根据密钥第一个字母L所对应的L行字母表,发现密文第一个字母L位于A列,因而明文第一个字母为A。密钥第二个字母E对应E行字母表,而密文第二个字母X位于此行T列,因而明文第二个字母为T。以此类推便可得到明文。请按照如上的解释设计一段程序,要求如下:
1)定义二维数组用于存储维吉尼亚密码表;
2)利用scanf函数或gets函数输入密钥和明文(密文),并使用合适的数据结构对输入数据进行存储(本次实验规定密钥为实验者本人姓名全拼,如LISHEN,明文为GOODGOODSTUDY);
3)根据密钥对明文进行加密或对密文进行解密。选择使用数组方式或指针方式编写加密函数及解密函数。
以下是代码
#include <stdio.h>
#include <stdlib.h>
#define MINCHAR 65
#define CHARSUM 26
#define DIFFER 32
//----利用指针实现----------
int encode(char* key, char* source, char* dest);
int decode(char* key, char* source, char* dest);
//----利用数组实现----------
int encode1(char key[], char source[], char dest[]);
int decode1(char key[], char source[], char dest[]);
char table[CHARSUM][CHARSUM];
int main()
{
int i, j;
char key[256];
char source[256];
char destination[256];
int operation;
//构造维吉尼亚密码表 CHARSUM是字母个数26,
//MINCHAR对应字母A,值是65
for(i = 0; i < CHARSUM; i++)
for(j = 0; j < CHARSUM; j++)
table[i][j] = MINCHAR + (i + j) % CHARSUM;
printf("please choose one operation code:\n");
printf("1. Encode; 2. Decode; Others. Exit.\n");
scanf("%d", &operation);
switch (operation)
{
case 1:
printf("please input the key code:\n");
scanf("%s", key);//密钥,将输入的字符存入数组
getchar();
printf("please input the source code you want to encode:\n");
gets(source);//允许输入空格及数字等其他字符,非字母符号不对其进行加密,源码显示
encode(key, source, destination);
printf("after encode is: \n");
printf("%s\n", destination);
encode1(key, source, destination);
printf("after encode1 is: \n");
printf("%s\n", destination);
break;
case 2:
printf("please input the key code:\n");
scanf("%s", key);
printf("please input the source code you want to decode:\n");
scanf("%s", source);
decode(key, source, destination);
printf("after decode is: \n");
printf("%s\n", destination);
decode1(key, source, destination);
printf("after decode1 is: \n");
printf("%s\n", destination);
break;
default:
return 0;
}
return 0;
}
int encode(char* pkey, char* psource, char* pdest)
{//只对大写字母加密
char* ptempkey = pkey;
do
{
if(*psource>=65&&*psource<=90)//是大写字母
*pdest = table[(*ptempkey) - MINCHAR][(*psource) - MINCHAR];
else
*pdest = *psource;
pdest++;
if (!(*(++ptempkey)))//密钥完成一轮,重新从第一个字符开始
ptempkey = pkey;
} while(*psource++);
*pdest = '\0';
return 1;
}
/*------encode1函数功能说明-------
-----允许接收明文为任意字符组合字符串----
----密钥要求是大写字母----
----字母按照密码表加密,小写密文为小写字母---
----非字母类字符不对其进行加密处理----
--*/
int encode1(char key[], char source[], char dest[])
{
int i,j;
for(i=0,j=0;source[i]!='\0';i++,j++)
{
if(key[j]=='\0')
j=0;//密钥循环使用,到最后一个字母再从头开始
//---加密实际就是查表,行标就是密钥和A(0行0列)的偏移
//列就是明文和A的偏移
//因为维吉尼亚密码表是对称的,所以行当明文,列当密钥或者行是密钥,列是明文都可以
//--判断是否为字母---
if(source[i]>=65&&source[i]<=90)
{//说明是大写字母
dest[i] = table[source[i]-MINCHAR][key[j]-MINCHAR];
}
else if(source[i]>=97 &&source[i]<=122)
{//小写字母
dest[i] = table[source[i]-MINCHAR-DIFFER][key[j]-MINCHAR] + DIFFER;
}
else
{//原样输出
dest[i]=source[i];
}
}
//加密结束,记得密文是字符串,要加'\0'
dest[i]='\0';
return 1;
}
int decode(char* pkey, char* psource, char* pdest)
{//解密就是密文和密钥的差
char* tempKey = pkey;
int offset;
do
{
offset = (*psource) - (*tempKey);
offset = offset >= 0 ? offset : (offset + CHARSUM);
*pdest = MINCHAR + offset;
pdest++;
if (!(*(++tempKey)))
tempKey = pkey;
} while(*++psource);
*pdest= '\0';
return 1;
}
int decode1(char key[], char source[], char dest[])
{
int i,j,offset;
for(i=0,j=0;source[i]!='\0';i++,j++)
{
if(key[j]=='\0')//密钥长度小于密文,循环使用
j=0;
//解密操作,实际就是找密文和密钥之间的偏移量
offset = source[i]-key[j];
if(offset<0)
offset = offset + CHARSUM;
dest[i] = MINCHAR + offset;
}
dest[i]='\0';
}
运行结果如图