目录
一.古典密码
二.Playfair密码的介绍
三.Playfair密码的实现
1.密钥转大写和密钥去重
2.输入密钥并打印密钥表
2.输入明文并加密为密文
3.输入密文并解密为明文
四.全部代码
一.古典密码
最早的加密方法可以追溯到公元前4000年左右的古代文明时期。埃及人、古希腊人和罗马人都使用了不同的加密方法来保护机密信息。埃及人使用简单的替换密码来隐藏他们的文字,而古希腊人使用了一种称为“斯巴达骑士”的替换密码。罗马人在军事和政治领域中广泛使用替换密码和移位密码,如凯撒密码。
古典密码是指在现代密码学发展之前使用的一类加密方法。它们通常基于简单的数学原理和替换、重排、置换等操作来对明文进行加密。
以下是一些常见的古典密码:
-
凯撒密码:凯撒密码是最早出现的替换密码,通过将明文中的每个字母按照一个固定的偏移量向后(或向前)替换成另一个字母来实现加密。
-
维吉尼亚密码:维吉尼亚密码是一种双重替代密码,它使用了一个关键词作为密钥,在第一次替换时按照关键词的字母顺序对明文进行替换,然后再按照另一个规则进行第二次替换。
-
栅栏密码:栅栏密码是一种重排密码,它将明文中的字母按照一定规则排列成多行,并按照特定的顺序读取密文。
-
培根密码:培根密码是一种置换密码,它将明文中的每个字母映射到一个五位二进制码,然后将这些二进制码组合成密文。
-
Playfair密码:Playfair密码是一种混合密码,它使用一个5x5的方阵作为密钥表,将明文中的字母按照特定规则进行替换。
这些古典密码在当时的年代可能用于各种机密文件的加密,随着社会的逐渐发展,这些古典密码在现代密码学中已经不再安全,因为它们的加密原理相对简单,容易被破解。然而,这些古典密码学都是前人的无数智慧所凝结成的。它们仍具有历史和教育意义,并且可以用于了解密码学的基本概念和技术。
而今天我们主要学习的就是Playfair密码的实现。
二.Playfair密码的介绍
Playfair密码是一种经典的对称加密方法,于1854年由英国密码学家查尔斯·维根·威廉姆斯·珀沙普(Charles Wheatstone)发明。它是基于一个5x5的方阵(称为Playfair Square)来进行加密和解密的。
加密过程中,首先需要创建一个密钥表(key table),该表由密钥中的字母组成。通常密钥中没有重复的字母,但是当我们输入密钥中有重复的字符时,我们就可以专门写一个函数,来对密钥进行去重,并且将"J"视为"I"(为了使表格保持为5x5)。因为字母一共有26个,当J被换做I了之后,字母就变成25个了,刚好可以构成5x5的字符表。然后将明文按照一定的规则进行分组和替换。
填充密钥表的规则:
1.如果密钥没有重复的字符,那么就直接使用密钥;如果密钥有重复的字符,使用一个函数来对密钥字符串进行去重。
2.然后把去重后的密钥字符串逐步填入5x5的密钥表,填完之后再把26位的字符中没有出现密钥的字符依次填入密钥表,其中"J"视为"I"。
3.所有字母都必须大写的,如果输入的密钥是小写的字母,我们还是可以使用一个函数把密钥字符串转换为大写的密钥字符串。
我们还是画图来理解一下密钥表是如何实现的:
加密的规则如下:
1.输入一个大写的明文字符串,然后两两字字符结合,然后找到这个两个字符在密钥表中的位置,如果是同一行的字母,得到的密文就是向右相邻的字符。如果第5列一列的字符的下一个就是第1列的字符,这个操作使用取余来实现。2.如果这两个字符是同一列的字符,然后加密后的字符就是向下相邻的字符。同样,如果是第5行的字符的下一个字符就是第1行的字符。
3.如果这两个字符既不是同行同列的字符,那么加密后的字符就是它们互为对角线的字符。
如果这两个字符是相等的,那么就在这两个相同字母中间插入一个字符,一般是X字符,然后得到新的明文字符串。如果明文字符串是奇数个字符,那么就在明文字符串后面加一个字符,该字符是X或者Z字符,得到一个新的偶数个字符的明文字符串。
注意:下面要实现加密算法,并没有考虑相同字符和奇数个字符,最开始我实现这个加密的时候,就没有考虑到这些,当写这篇文章的时候,我才知道,加密规则我漏看了两个,这里我也不想改代码了,就这样吧。
三.Playfair密码的实现
1.密钥转大写和密钥去重
关于小写转大写,我们可以不用ASCll值的加减来计算,而可以使用一个函数来实现。toupper
是 C 语言中的一个函数,用于将小写字母转换为大写字母。它是 ctype.h 头文件中的一个字符处理函数。
// 将密钥中的字母转换成大写
void ConverstAlphabet(char key[]) {
int keyLen = strlen(key);
for (int i = 0; i < keyLen; i++)
{
key[i] = toupper(key[i]);
//toupper函数将小写字母转换为大写字母
}
}
这样便可以实现小写转大写,非常的方便。
关于字符去重也是比较简单的,第一个字符不用管,后面的字符依次和前面的字符比较,如果和前面的某个字符相同,那么就去掉。
//将密钥的字符去重
int Deduplication(char key[], int keylen1)
{
//keylen1是原始的密钥的长度
if (keylen1 <= 1)
{
return;
}
int i = 0;
int j = 0;
int count = 1;
for (i = 1; i < keylen1; i++)
{
for (j = 0; j < i; j++)
{
if (key[i] == key[j])
{
break;
}
}
if (i == j)//退出循环,i==j说明该字符和前面的任意字符不相等
{
key[count++] = key[i];//count从1开始,因为第一个字符不用管
}
}
key[count] = '\0';//末尾加一个\0
return count;
}
2.输入密钥并打印密钥表
这里开始我们已经细说了步骤了,只是注意这里存放到密钥表中的操作是怎么样的。行是从0开始的,一行是放5列字母的,所以这里行是k/5,而列是k%5。
// 生成密码表
void CreatPassWordTable(char key[], char table[5][5], int keylen2)
{
char alphabet[] = "ABCDEFGHIKLMNOPQRSTUVWXYZ";
int alphabetLen = strlen(alphabet);
int k = 0;
//这个for循环是把密钥放到密钥表中
for (int i = 0; i < keylen2; i++) {//keylen2是去重后的密钥的长度
table[k / 5][k % 5] = key[i];
k++;
}
for (int i = 0; i < alphabetLen; i++) {
if (alphabet[i] == 'J') {
// 将字母J转换成I
continue;
}
int flag = 0;
for (int j = 0; j < keylen2; j++) {
if (key[j] == alphabet[i]) {//判断密钥和25个字母是否相等
flag = 1;
break;
}
}
if (!flag) {
table[k / 5][k % 5] = alphabet[i];//开始把字母存放到密钥表中去
k++;
}
}
}
写了这些部分,我们就可以把密钥表打印出来看一下了。
红圈的部分就是密钥转大写加去重后的。
2.输入明文并加密为密文
这是我们前面写的加密的规则:按照规则来写代码也就得心应手了。
1.输入一个大写的明文字符串,然后两两字字符结合,然后找到这个两个字符在密钥表中的位置,如果是同一行的字母,得到的密文就是向右相邻的字符。如果第5列一列的字符的下一个就是第1列的字符,这个操作使用取余来实现。
2.如果这两个字符是同一列的字符,然后加密后的字符就是向下相邻的字符。同样,如果是第5行的字符的下一个字符就是第1行的字符。
3.如果这两个字符既不是同行同列的字符,那么加密后的字符就是它们互为对角线的字符。
// 加密明文
void encrypt(char plaintext[], char table[][5]) {
int len = strlen(plaintext);
char p[100] = { 0 };
int t = 0;
for (int i = 0; i < len; i += 2) {//因为一次找两个字符,所以这里是i+=2
int row1 = 0, col1 = 0, row2 = 0, col2 = 0;
// 查找明文字母在密码表中的位置
for (int j = 0; j < 5; j++) {
for (int k = 0; k < 5; k++) {
if (table[j][k] == plaintext[i]) {
row1 = j;
col1 = k;
}
if (table[j][k] == plaintext[i + 1]) {
row2 = j;
col2 = k;
}
}
}
// 使用Playfair密码规则加密
if (row1 == row2) {
// 如果明文字母在同一行
col1 = (col1 + 1) % 5;
col2 = (col2 + 1) % 5;
}
else if (col1 == col2) {
// 如果明文字母在同一列
row1 = (row1 + 1) % 5;
row2 = (row2 + 1) % 5;
}
else {
// 如果明文字母不在同一行也不在同一列
int temp = col1;//找对角线
col1 = col2;
col2 = temp;
}
//打印加密明文后的密文
printf("%c%c", table[row1][col1], table[row2][col2]);
}
}
3.输入密文并解密为明文
这个逻辑和加密的逻辑大差不差的,只是反着找字符即可。
// 解密密文
void decrypt(char ciphertext[], char table[][5]) {
int len = strlen(ciphertext);
char q[100] = { 0 };
int t = 0;
printf("解密密文之后的明文为:\n");
for (int i = 0; i < len; i += 2) {
int row1 = 0, col1 = 0, row2 = 0, col2 = 0;
// 查找密文字母在密码表中的位置
for (int j = 0; j < 5; j++) {
for (int k = 0; k < 5; k++) {
if (table[j][k] == ciphertext[i]) {
row1 = j;
col1 = k;
}
if (table[j][k] == ciphertext[i + 1]) {
row2 = j;
col2 = k;
}
}
}
// 使用Playfair密码规则解密
if (row1 == row2) {
// 如果密文字母在同一行
col1 = (col1 + 4) % 5;
col2 = (col2 + 4) % 5;
}
else if (col1 == col2) {
// 如果密文字母在同一列
row1 = (row1 + 4) % 5;
row2 = (row2 + 4) % 5;
}
else {
// 如果密文字母不在同一行也不在同一列
int temp = col1;
col1 = col2;
col2 = temp;
}
//打印解密的明文
printf("%c%c", table[row1][col1], table[row2][col2]);
}
printf("\n");
}
四.全部代码
#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// 将密钥中的字母转换成大写
void ConverstAlphabet(char key[]) {
int keyLen = strlen(key);
for (int i = 0; i < keyLen; i++)
{
key[i] = toupper(key[i]);
//toupper函数将小写字母转换为大写字母
}
}
//将密钥的字符去重
int Deduplication(char key[], int keylen1)
{
//keylen1是原始的密钥的长度
if (keylen1 <= 1)
{
return;
}
int i = 0;
int j = 0;
int count = 1;
for (i = 1; i < keylen1; i++)
{
for (j = 0; j < i; j++)
{
if (key[i] == key[j])
{
break;
}
}
if (i == j)
{
key[count++] = key[i];
}
}
key[count] = '\0';//末尾加一个\0
return count;
}
// 生成密码表
void CreatPassWordTable(char key[], char table[5][5], int keylen2)
{
char alphabet[] = "ABCDEFGHIKLMNOPQRSTUVWXYZ";
int alphabetLen = strlen(alphabet);
int k = 0;
//这个for循环是把密钥放到密钥表中
for (int i = 0; i < keylen2; i++) {//keylen2是去重后的密钥的长度
table[k / 5][k % 5] = key[i];
k++;
}
for (int i = 0; i < alphabetLen; i++) {
if (alphabet[i] == 'J') {
// 将字母J转换成I
continue;
}
int flag = 0;
for (int j = 0; j < keylen2; j++) {
if (key[j] == alphabet[i]) {//判断密钥和25个字母是否相等
flag = 1;
break;
}
}
if (!flag) {
table[k / 5][k % 5] = alphabet[i];//开始把字母存放到密钥表中去
k++;
}
}
}
// 加密明文
void encrypt(char plaintext[], char table[][5]) {
int len = strlen(plaintext);
char p[100] = { 0 };
int t = 0;
for (int i = 0; i < len; i += 2) {//因为一次找两个字符,所以这里是i+=2
int row1 = 0, col1 = 0, row2 = 0, col2 = 0;
// 查找明文字母在密码表中的位置
for (int j = 0; j < 5; j++) {
for (int k = 0; k < 5; k++) {
if (table[j][k] == plaintext[i]) {
row1 = j;
col1 = k;
}
if (table[j][k] == plaintext[i + 1]) {
row2 = j;
col2 = k;
}
}
}
// 使用Playfair密码规则加密
if (row1 == row2) {
// 如果明文字母在同一行
col1 = (col1 + 1) % 5;
col2 = (col2 + 1) % 5;
}
else if (col1 == col2) {
// 如果明文字母在同一列
row1 = (row1 + 1) % 5;
row2 = (row2 + 1) % 5;
}
else {
// 如果明文字母不在同一行也不在同一列
int temp = col1;//找对角线
col1 = col2;
col2 = temp;
}
//打印加密明文后的密文
printf("%c%c", table[row1][col1], table[row2][col2]);
}
}
// 解密密文
void decrypt(char ciphertext[], char table[][5]) {
int len = strlen(ciphertext);
char q[100] = { 0 };
int t = 0;
printf("解密密文之后的明文为:\n");
for (int i = 0; i < len; i += 2) {
int row1 = 0, col1 = 0, row2 = 0, col2 = 0;
// 查找密文字母在密码表中的位置
for (int j = 0; j < 5; j++) {
for (int k = 0; k < 5; k++) {
if (table[j][k] == ciphertext[i]) {
row1 = j;
col1 = k;
}
if (table[j][k] == ciphertext[i + 1]) {
row2 = j;
col2 = k;
}
}
}
// 使用Playfair密码规则解密
if (row1 == row2) {
// 如果密文字母在同一行
col1 = (col1 + 4) % 5;
col2 = (col2 + 4) % 5;
}
else if (col1 == col2) {
// 如果密文字母在同一列
row1 = (row1 + 4) % 5;
row2 = (row2 + 4) % 5;
}
else {
// 如果密文字母不在同一行也不在同一列
int temp = col1;
col1 = col2;
col2 = temp;
}
//打印解密的明文
printf("%c%c", table[row1][col1], table[row2][col2]);
}
printf("\n");
}
// 打印密码表
void printTable(char table[][5])
{
printf("打印出Playfair密码表:\n");
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
printf("%c ", table[i][j]);
}
printf("\n");
}
printf("\n");
}
int main() {
char key[100] = { 0 };//密钥
char plaintext[100] = { 0 };//明文
char ciphertext[100] = { 0 };//密文
char table[5][5] = { 0 };
int input = 0;
printf("请输入密钥(不超过100个字符):");
scanf("%s", key);
ConverstAlphabet(key);//把密钥中的小写字母转换为大写字母
int keylen1 = strlen(key);//keylen1为去重前的密钥的长度
int keylen2 = Deduplication(key, keylen1);//把密钥中的字符去重
CreatPassWordTable(key, table, keylen2);//keylen2为密钥去重后的长度
printTable(table);
printf("请你输入要加密的明文:\n");
scanf("%s", plaintext);
printf("加密明文后得到的密文为:\n");
encrypt(plaintext, table);
printf("\n");
printf("请你输入要解密的密文:\n");
scanf("%s", ciphertext);
printf("\n");
decrypt(ciphertext, table);
return 0;
}