1、引入PlayFair密码的原因
(1)、因为单表代替的密钥量很小,不能抵抗穷尽搜索攻击
(2)、单表代替密码没有将明文字母出现的概率隐藏起来。很容易收到频率分析的攻击
综上所述,我们会引入PlayFair密码加密
2、PlayFair密码的原理以及完成加密解密的步骤
(1)、编制密码表:
- 基于一个5×5的字母矩阵
- 该矩阵使用一个密钥(关键词)来构造
- 从左到右、从上到下;依次填入密钥的字母(PS:密钥中重复的字母不填),然后再以字母表顺序依次填入其他字母。
- 字母 I 和 J 算作一个字母 (PS:25的矩阵只能存储25个字母)
- 示例:(全片以此例举例)
密钥是:PLAYFAIR IS A DIGRAM CIPHER
第一步:填入不重复的密钥(如果有重复则跳过)
第二部:将剩未出现的字母依次写入
(2)、加密方法:(本质:按照特定方法替换)
1、将明文两两分组
case1:明文个数为偶数个
根据以上例子可以得到:
P=PL——AY——FA——IR——CI——PH——ER
case2:明文个数为奇数个
解决办法:缺补:在最后一个明文字母的后面补上其下一位字母
示例:
AB——C,补完变为AB——CD
case3:明文分组后同一组字母相同
解决办法:同插,在相同的字母中间插入其下一位字母
示例:
AA——BC 插完变为 AB——AB——CD(这里执行了一次缺补)
PS:同插缺补操作不分先后,执行结果相同
2、移位和替换
规则:
- 若p1 p2在同一行,对应密文c1 c2分别是紧靠p1 p2 右端的字母。其中第一列被看做是最后一列的右方。
- 若p1 p2在同一列,对应密文c1 c2分别是紧靠p1 p2 下方的字母。其中第一行被看做是最后一行的下方。
- 若p1 p2不在同一行,不在同一列,则c1 c2是由p1 p2确定的矩形的其他两角的字母
- 示例: P=PL——AY——FA——IR——CI——PH——ER
首先看前四个:
前四个都符合规则的第一条:
得到前四组的密文C:
C=LA——YF——PY——RS
再看剩余几组:满足变换规则第三条(本质:构建子矩阵并找到反对角)
PS:一定要注意字母对应的顺序:明文对应同行的才是密文
所以得到:C=LA——YF——PY——RS——MR——AM——CD
(3)、解密方法
三条规则逆推即可,不再做详细赘述!!!
3、代码
#include<iostream>
#include<cstring>
using namespace std;
void encrypt()
{
const int N = 100;
char letters[26] = "ABCDEFGHIKLMNOPQRSTUVWXYZ";//用于填充矩阵
int flag[25] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };//字母是否已在矩阵中,与letters数组对应
char ch[5][5];//5X5矩阵
char ch1[N];//密钥
char ch2[N];//明文
char ch4;//无关字符
int len = 'a' - 'A';
cout << "输入密钥:";
cin >> ch1;
int flg = 1;
while (flg == 1)
{
for (int i = 0; i < strlen(ch1); i++)//把所输入的密钥转化为大写字母
{
if (ch1[i] > 'z' || ch1[i] < 'a')
{
cout << "请重新选择操作:" << endl;
flg = 0; break;
}
else
ch1[i] = ch1[i] - len;
}
if (flg == 1)
{
for (int i = 0; i < strlen(ch1); i++)//把密钥中的J都变为I
{
if (ch1[i] == 'J')ch1[i] = 'I';
}
int i = 0; int j = 0;
//把密钥中的字母填入到矩阵中,并把该字母标记为已用
for (int k = 0; k < strlen(ch1); k++)
{
for (int t = 0; t < 25; t++)
{
if (ch1[k] == letters[t] && flag[t] == 0)
{
ch[i][j] = letters[t];
flag[t] = 1;
if (j < 4)j++;
else { i++; j = 0; }
}
}
}
for (int k = 0; k < 25; k++)//按字母表顺序把未用字母依次填入到矩阵中
{
if (flag[k] == 0)
{
ch[i][j] = letters[k];
flag[k] = 1;
if (j < 4)j++;
else { i++; j = 0; }
}
}
cout << "密钥填充后的矩阵为: " << endl;
for (i = 0; i < 5; i++)
for (j = 0; j < 5; j++)
{
cout << ch[i][j];
cout << " ";
if (j == 4)
cout << endl;
}
cout << endl;
cout << "请输入明文(请输入英文字符):";
cin >> ch2;
cout << "输入一个无关字符:";
cin >> ch4;
if (ch4 >= 'a')
ch4 = ch4 - len;
for (int k = 0; k < strlen(ch2); k++)//把所输入的明文转化为大写字母
{
if (ch2[k] >= 'a')
ch2[k] = ch2[k] - len;
}
for (int k = 0; k < strlen(ch2); k++)//把明文中的J都变为I
{
if (ch2[k] == 'J')
ch2[k] = 'I';
}
//为明文添加必要的无关字符以防止同一组的两个字符相同
for (int k = 0; k < strlen(ch2); k += 2)
{
if (ch2[k] == ch2[k + 1])
{
for (int t = strlen(ch2); t > k; t--)
ch2[t + 1] = ch2[t];
ch2[k + 1] = ch4;
}
}
//若明文有奇数个字符,则添加一个无关字符以凑够偶数个
if (strlen(ch2) % 2 != 0)
{
ch2[strlen(ch2) + 1] = ch2[strlen(ch2)];//字符串结尾赋'\0'
ch2[strlen(ch2)] = ch4;//明文串尾插入无关字符
}
cout << "经过处理后的明文为:";
for (int k = 0; k < strlen(ch2); k += 2)
cout << ch2[k] << ch2[k + 1] << " ";
cout << endl;
cout << "其最终长度为:" << strlen(ch2) << endl;
//明文输入并整理完毕///
for (int k = 0; k < strlen(ch2); k += 2)
{
int m1, m2, n1, n2;
for (m1 = 0; m1 <= 4; m1++)
{
for (n1 = 0; n1 <= 4; n1++)
{
if (ch2[k] == ch[m1][n1])break;
}
if (ch2[k] == ch[m1][n1])break;
}
for (m2 = 0; m2 <= 4; m2++)
{
for (n2 = 0; n2 <= 4; n2++)
{
if (ch2[k + 1] == ch[m2][n2])break;
}
if (ch2[k + 1] == ch[m2][n2])break;
}
m1 = m1 % 5;
m2 = m2 % 5;
if (n1 > 4) { n1 = n1 % 5; m1 = m1 + 1; }
if (n2 > 4) { n2 = n2 % 5; m2 = m2 + 1; }
if (m1 == m2)
{
ch2[k] = ch[m1][(n1 + 1) % 5];
ch2[k + 1] = ch[m2][(n2 + 1) % 5];
}
else
{
if (n1 == n2)
{
ch2[k] = ch[(m1 + 1) % 5][n1];
ch2[k + 1] = ch[(m2 + 1) % 5][n2];
}
else
{
ch2[k] = ch[m1][n2];
ch2[k + 1] = ch[m2][n1];
}
}
}
cout << "加密后所得到的密文是:";
for (int k = 0; k < strlen(ch2); k += 2)
cout << ch2[k] << ch2[k + 1] << " ";
cout << endl;
}
else break;
}
}
//解密算法
void decrypt()
{
const int N = 100;
char letters[26] = "ABCDEFGHIKLMNOPQRSTUVWXYZ";//用于填充矩阵
int flag[25] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
//标记字母是否已在矩阵中,与letters数组对应
char ch[5][5];//5X5矩阵
char ch1[N];//密钥
char ch2[N];//密文
int len = 'a' - 'A';
int flg = 1;
cout << "输入密钥:";
cin >> ch1;
while (flg == 1)
{
for (int i = 0; i < strlen(ch1); i++)//把所输入的密钥转化为大写字母
{
if (ch1[i] > 'z' || ch1[i] < 'a')
{
cout << "请重新选择操作:" << endl;
flg = 0; break;
}
else
ch1[i] = ch1[i] - len;
}
if (flg == 1)
{
for (int i = 0; i < strlen(ch1); i++)//把密钥中的J都变为I
{
if (ch1[i] == 'J')ch1[i] = 'I';
}
int i = 0; int j = 0;
//把密钥中的字母填入到矩阵中,并把该字母标记为已用
for (int k = 0; k < strlen(ch1); k++)
{
for (int t = 0; t < 25; t++)
{
if (ch1[k] == letters[t] && flag[t] == 0)
{
ch[i][j] = letters[t];
flag[t] = 1;
if (j < 4)j++;
else { i++; j = 0; }
}
}
}
for (int k = 0; k < 25; k++)//按字母表顺序把未用字母依次填入到矩阵中
{
if (flag[k] == 0)
{
ch[i][j] = letters[k];
flag[k] = 1;
if (j < 4)j++;
else { i++; j = 0; }
}
}
cout << "密钥填充后的矩阵为: " << endl;
for (i = 0; i < 5; i++)
for (j = 0; j < 5; j++)
{
cout << ch[i][j];
cout << " ";
if (j == 4)
cout << endl;
}
cout << endl;
// 矩阵生成完毕
int f = 0;
do {
cout << "请输入密文(英文字符):";
cin >> ch2;
for (int k = 0; k < strlen(ch2); k++)//把所输入的密文转化为大写字母
{
if (ch2[k] >= 'a')
ch2[k] = ch2[k] - len;
}
for (int k = 0; k < strlen(ch2); k++)//把密文中的J都变为I
{
if (ch2[k] == 'J')ch2[k] = 'I';
}
for (int k = 0; k < strlen(ch2); k += 2)
{
if (ch2[k] == ch2[k + 1])
{
cout << "同一分组中不能出现相同字符!请重新输入。" << endl;
f = 1;
break;
}
else f = 2;
}
if (f == 1)continue;
if (strlen(ch2) % 2 != 0)
{
cout << "字符串不能为奇数个!请重新输入。" << endl;
f = 1;
}
else f = 2;
} while (f == 1);
//解密开始
for (int k = 0; k < strlen(ch2); k += 2)
{
int m1, m2, n1, n2;
for (m1 = 0; m1 <= 4; m1++)
{
for (n1 = 0; n1 <= 4; n1++)
{
if (ch2[k] == ch[m1][n1])break;
}
if (ch2[k] == ch[m1][n1])break;
}
for (m2 = 0; m2 <= 4; m2++)
{
for (n2 = 0; n2 <= 4; n2++)
{
if (ch2[k + 1] == ch[m2][n2])break;
}
if (ch2[k + 1] == ch[m2][n2])break;
}
m1 = m1 % 5;
m2 = m2 % 5;
if (n1 > 4) { n1 = n1 % 5; m1 = m1 + 1; }
if (n2 > 4) { n2 = n2 % 5; m2 = m2 + 1; }
if (m1 == m2)
{
ch2[k] = ch[m1][(n1 + 4) % 5];
ch2[k + 1] = ch[m2][(n2 + 4) % 5];
}
else
{
if (n1 == n2)
{
ch2[k] = ch[(m1 + 4) % 5][n1];
ch2[k + 1] = ch[(m2 + 4) % 5][n2];
}
else
{
ch2[k] = ch[m1][n2];
ch2[k + 1] = ch[m2][n1];
}
}
}
cout << "解密后所得到的明文是:";
for (int k = 0; k < strlen(ch2); k += 2)
cout << ch2[k] << ch2[k + 1] << " ";
cout << endl;
}
else break;
}
}
int main()
{
int n;
cout << "请选择1加密2解密:" << endl;
while (true)
{
cin >> n;
switch (n)
{
case 1:
encrypt();
break;
case 2:
decrypt();
break;
default:
break;
}
}
return 0;
}
代码执行结果:(操作的时候不要打空格,空格视为默认字符)