Playfair密码(英文:Playfair cipher 或 Playfair square)是一种替换密码,1854年由查尔斯·惠斯通(Charles Wheatstone)的英国人发明。经莱昂·普莱费尔提倡在英国军地和政府使用。
它有一些不太明显的特征:密文的字母数一定是偶数;任意两个同组的字母都不会相同,如果出现这种字符必是乱码和虚码。
它使用方便而且可以让频度分析法变成瞎子,在1854到1855年的克里米亚战争和1899年的布尔战争中有广泛应用。但在1915年的一战中被破译了。
编写分三步:1.编制密码表 2.整理明文 3.编写密文 构成部分:1.密钥 2.明文3.密文4.注明的某个字母代替的另一个字母
代码实现:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char playfair_table[5][5];
void CreateTable (char *in_key)
{
int i = 0, j, k;
int hash[26]={0};
char key[26] = {0};
int key_len;
strcpy (key, in_key);
key_len = strlen (key);
//一次遍历,处理密钥
while (1)
{
if (i == key_len)
break;
//转换成大写
if(key[i]>='a'&&key[i]<='z')
{
key[i]+='A'-'a';
}
//去除非法字符
if (!(key[i]<='Z'&&key[i]>='A'))
{
for (j = i; j < key_len - 1; ++j)
key[j] = key[j + 1];
key_len--;
continue;
}
if(key[i]=='J')
{
//I和J视为相同
key[i]='I';
}
if(hash[key[i]-'A']==0)
{
hash[key[i]-'A']=1;
}
else //去除重复
{
for (j = i; j < key_len - 1; ++j)
key[j] = key[j + 1];
key_len--;
continue;
}
i++;
}
//此时,密钥长度必然小于26,长度不够需补全
if(key_len<25)
{
for(i=0;i<26;i++)
{
if(key_len==25)
{
break;
}
if(i+'A'=='J')
continue;
if(hash[i]==0)
{
hash[i]=1;
key[key_len]=i+'A';
key_len++;
}
}
}
for (i = 0; i < 5;i++)
{
for (j = 0; j < 5; j++)
playfair_table[i][j] = key[5 * i + j];
}
}
void PrintTable ()
{
int i, j;
printf ("Playfair Table:\n");
for (i = 0; i < 5; ++i)
{
printf ("\t");
for (j = 0; j < 5; ++j)
{
printf ("%c ", playfair_table[i][j]);
}
puts("\n");
}
}
void GetPosition (char c, int *x, int *y)
{
int i, j;
if(c>='a')
{
c+=-'a'+'A';
}
for (i = 0; i < 5; ++i)
{
for (j = 0; j < 5; ++j)
{
if (playfair_table[i][j] == c)
{
*x = i; *y = j;
return;
}
}
}
}
char GetKey (int x, int y,char type)
{
if (x < 0)
x += 5;
if (y < 0)
y += 5;
if(type<'a')
{
return playfair_table[x % 5][y % 5];
}
else
{
return (playfair_table[x % 5][y % 5]+'a'-'A');
}
}
int Encrypt (char *input, char *output)
{
int length = strlen (input);
int i = 0, j=-1,find;
int ax, ay, bx, by;
int pair[2]={-1,-1};
//for(i=0;i<length;i++)
//{
// if(input[i]>='A'&&input[i]<='Z')
// {
// input[i]-='A'-'a';//全部转化成小写
// }
//}
while(1)
{
char between[1000]={0};
int betweenlength=-1;
find=0;
//找寻成对出现的第一个字符所在位置
for(i=pair[1]+1;i<length;i++)
{
if((input[i]>='a'&&input[i]<='z')||(input[i]>='A'&&input[i]<='Z'))
{
pair[0]=i;
pair[1]=i+1;
find=1;
break;
}
else
{
output[++j]=input[i];
}
}
if(find==0)
{
break;
}
//找寻成对出现的第二个字符所在位置
find=0;
for(i=pair[1];i<length;i++)
{
if((input[i]>='a'&&input[i]<='z')||(input[i]>='A'&&input[i]<='Z'))
{
pair[1]=i;
find=1;
break;
}
else
{
between[++betweenlength]=input[i];
}
}
if(find==0)
{
input[length]='Q';//代表最后一位的填充
pair[1]=length;
length++;
}
//开始加密过程
if(input[pair[1]]==input[pair[0]])
{
if(input[pair[0]]!='X')//放置连续的xx影响结果
{
//属于相同对中的重复的明文字母将用一个填充字母进行分隔(如x)
for(i=length;i>=pair[1];i--)
{
input[i+1]=input[i];
}
length++;
input[pair[1]]='X';
}
}
GetPosition(input[pair[0]],&ax,&ay);
GetPosition(input[pair[1]],&bx,&by);
//若p1 p2在同一行,对应密文c1 c2分别是紧靠p1 p2 右端的字母。其中第一列被看做是最后一列的右方。
if(ax==bx)
{
output[++j]=GetKey(ax,ay+1,input[pair[0]]);
for(i=0;i<=betweenlength;i++)
{
output[++j]=between[i];
}
output[++j]=GetKey(bx,by+1,input[pair[1]]);
}
//若p1 p2在同一列,对应密文c1 c2分别是紧靠p1 p2 下方的字母。其中第一行被看做是最后一行的下方。
else if(ay==by)
{
output[++j]=GetKey(ax+1,ay,input[pair[0]]);
for(i=0;i<=betweenlength;i++)
{
output[++j]=between[i];
}
output[++j]=GetKey(bx+1,by,input[pair[1]]);
}
//若p1 p2不在同一行,不在同一列,则c1 c2是由p1 p2确定的矩形的其他两角的字母,并且c1和p1, c2和p2同行。
else
{
output[++j]=GetKey(ax,by,input[pair[0]]);
for(i=0;i<=betweenlength;i++)
{
output[++j]=between[i];
}
output[++j]=GetKey(bx,ay,input[pair[1]]);
}
}
return length;
}
int Decrypt (char *input, char *output)
{
int length = strlen (input);
int i = 0, j=-1,find;
int ax, ay, bx, by;
int pair[2]={-1,-1};
while(1)
{
char between[1000]={0};
int betweenlength=-1;
find=0;
//找寻成对出现的第一个字符所在位置
for(i=pair[1]+1;i<length;i++)
{
if((input[i]>='a'&&input[i]<='z')||(input[i]>='A'&&input[i]<='Z'))
{
pair[0]=i;
pair[1]=i+1;
find=1;
break;
}
else
{
output[++j]=input[i];
}
}
if(find==0)
{
break;
}
//找寻成对出现的第二个字符所在位置
find=0;
for(i