一种Playfair密码变种加密方法如下:首先选择一个密钥单词(称为pair)(字母不重复,且都为小写字母),然后与字母表中其他字母一起填入至一个5x5的方阵中,填入方法如下:
1.首先按行填入密钥串。
2.紧接其后,按字母序按行填入不在密钥串中的字母。
3.由于方阵中只有25个位置,最后剩下的那个字母则不需变换。
如果密钥为youandme,则该方阵如下:
y o u a n
d m e b c
f g h i j
k l p q r
s t v w x
在加密一对字母时,如am,在方阵中找到以这两个字母为顶点的矩形(红色字体):
y o u a n
d m e b c
f g h i j
k l p q r
s t v w x
这对字母的加密字母为该矩形的另一对顶点,如本例中为ob。
请设计程序,使用上述方法对输入串进行加密,并输出加密后的串。
另外有如下规定:
1、一对一对取字母,如果最后只剩下一个字母,则不变换,直接放入加密串中;
2、如果一对字母中的两个字母相同,则不变换,直接放入加密串中;
3、如果一对字母中有一个字母不在正方形中,则不变换,直接放入加密串中;
4、如果字母对出现在方阵中的同一行或同一列,如df或hi,则只需简单对调这两个字母,即变换为fd或ih;
5、如果在正方形中能够找到以字母对为顶点的矩形,假如字母对为am,则该矩形的另一对顶点字母中,与a同行的字母应在前面,在上例中应是ob;同样若待变换的字母对为ta,则变换后的字母对应为wo;
6、本程序中输入串均为小写字母,并不含标点、空格或其它字符。
解密方法与加密相同,即对加密后的字符串再加密,将得到原始串。
要求输入形式如下:
从控制台输入两行字符串,第一行为密钥单词(长度小于等于25),第二行为待加密字符串(长度小于等于50),两行字符串末尾都有一个回车换行符,并且两行字符串均为小写字母,不含其它字符。
在标准输出上输出加密后的字符串。
例如,若输入:
youandme
welcometohangzhou
则表示输入的密钥单词为youandme,形成的正方形如上所示;待加密字符串为welcometohangzhou。在正方形中可以找到以第一对字母we为顶点的矩形,对应另一对顶点字母为vb,因此加密后为vb,同理可找到与字母对lc,et,oh,ho对应的顶点字母对。而字母对om位于上述正方形中的同一列,所以直接以颠倒这两个字母来加密,即为mo,字母对an同理。字母对gz中的z不在上述正方形中,因此原样放到加密串中。最后剩一个字母u也原样输出。
因此输出的结果为:
vbrmmomvugnagzguu
分析:
纯逻辑题,自顶向下求解。先根据需求定义求解流程(顺序),再根据每个流程定义函数(包括功能,参数),最后实现每个函数。
解:
#include <stdio.h>
#include <string.h>
#define KEY_X 5 //方阵行
#define KEY_Y 5 //方阵列
#define MAX_PAIR_NUM KEY_X*KEY_Y //方阵字符数
#define MAX_STR 50 //字串最大字节数
typedef int INT32;
typedef char INT8;
typedef void VOID;
/****************************************************************************
* 函数:CreatKey *
* 参数:pair:密钥单词. *
* key:密钥方阵. *
* keyPos:(字母-'a')在key中位置(距key首地址的位置). *
* 返回值:无. *
* 功能:生成密钥方阵. *
*****************************************************************************/
VOID CreatKey(INT8 *pair, INT8 key[][KEY_Y], INT8 *keyPos)
{
INT32 i,j,k, np;
INT32 c26, n;
memset(keyPos, -1, 26);
np = strlen(pair);
//每个字母1bit
c26 = 0;
for(i=0; pair[i] != 0; i++)
c26 |= (1<<(pair[i]-'a'));
//生成密钥方阵和keyPos
k = 0;
for(i=0; i<KEY_X; i++)
for(j=0; j<KEY_Y; j++)
{
n = i*KEY_Y+j;
if(n < np)
{
key[i][j] = pair[n];
keyPos[key[i][j]-'a'] = n;
}
else
{
while(1 == ((c26 >> (k)) & 0x1))
k++;
key[i][j] = k+'a';
keyPos[k] = n;
k++;
}
}
/*
printf("Key:\n");
for(i=0; i<KEY_X; i++)
{
for(j=0; j<KEY_Y; j++)
{
printf("%c ", key[i][j]);
}
printf("\n");
}
printf("KeyPos:\n");
for(i=0; i<26; i++)
{
printf("%c:%d ", i+'a', keyPos[i]);
if(0 == (i+1)%KEY_Y)
printf("\n");
}
*/
}
/****************************************************************************
* 函数:Encrypt *
* 参数:str:待加密字串. *
* enstr:(out)密文. *
* key:密钥方阵. *
* keyPos:(字母-'a')在key中位置(距key首地址的位置). *
* 返回值:无. *
* 功能:加密字串str,并将密文保存在enstr. *
*****************************************************************************/
VOID Encrypt(INT8 *str, INT8 *enstr, INT8 key[][KEY_Y], INT8 *keyPos)
{
INT8 *p, *q;
INT8 a, b;
INT32 x1,y1,x2,y2;
p = str;
q = enstr;
//一对一对取字母
while(NULL != *(p+1))
{
a = *p++;
b = *p++;
a -= 'a';
b -= 'a';
//如果一对字母中的两个字母相同,则不变换,直接放入加密串中
//如果一对字母中有一个字母不在正方形中,则不变换,直接放入加密串中;
if(a == b || -1 == keyPos[a] || -1 == keyPos[b])
{
*q++ = a+'a';
*q++ = b+'a';
continue;
}
x1 = keyPos[a]/KEY_Y;
y1 = keyPos[a]%KEY_Y;
x2 = keyPos[b]/KEY_Y;
y2 = keyPos[b]%KEY_Y;
//如果字母对出现在方阵中的同一行或同一列,如df或hi,则只需简单对调这两个字母,即变换为fd或ih;
if(x1 == x2 || y1 == y2)
{
*q++ = b+'a';
*q++ = a+'a';
continue;
}
//如果在正方形中能够找到以字母对为顶点的矩形,假如字母对为am,则该矩形的另一对顶点字母中,与a同行的字母应在前面,在上例中应是ob;同样若待变换的字母对为ta,则变换后的字母对应为wo;
*q++ = key[x1][y2];
*q++ = key[x2][y1];
}
//如果最后只剩下一个字母,则不变换,直接放入加密串中
if(*p)
*q++ = *p;
*q = '\0';
}
INT32 main(INT32 argc, INT32 *argv[])
{
INT8 pair[MAX_PAIR_NUM+1]; //密钥单词
INT8 str[MAX_STR+1]; //待加密字串
INT8 enstr[MAX_STR+1]; //加密后字串
INT8 key[KEY_X][KEY_Y]; //密钥方阵
INT8 keyPos[26]; //(字母-'a')在key中位置(距key首地址的位置)
printf("请输入密钥单词(字母不重复,且都为小写字母)...\n");
scanf("%s", pair);
printf("请输入待加密字串(长度小于等于50)...\n");
scanf("%s", str);
CreatKey(pair, key, keyPos);
Encrypt(str, enstr, key, keyPos);
printf("\n\nEncrypt:\n%s\n", enstr);
Encrypt(enstr, str, key, keyPos);
printf("Deciphering:\n%s\n", str);
return 0;
}