古典密码之一的Playfair密码的实现

目录

一.古典密码

二.Playfair密码的介绍

三.Playfair密码的实现

1.密钥转大写和密钥去重

2.输入密钥并打印密钥表

2.输入明文并加密为密文

3.输入密文并解密为明文

四.全部代码 


一.古典密码

最早的加密方法可以追溯到公元前4000年左右的古代文明时期。埃及人、古希腊人和罗马人都使用了不同的加密方法来保护机密信息。埃及人使用简单的替换密码来隐藏他们的文字,而古希腊人使用了一种称为“斯巴达骑士”的替换密码。罗马人在军事和政治领域中广泛使用替换密码和移位密码,如凯撒密码。
 

古典密码是指在现代密码学发展之前使用的一类加密方法。它们通常基于简单的数学原理和替换、重排、置换等操作来对明文进行加密。

以下是一些常见的古典密码:

  1. 凯撒密码:凯撒密码是最早出现的替换密码,通过将明文中的每个字母按照一个固定的偏移量向后(或向前)替换成另一个字母来实现加密。

  2. 维吉尼亚密码:维吉尼亚密码是一种双重替代密码,它使用了一个关键词作为密钥,在第一次替换时按照关键词的字母顺序对明文进行替换,然后再按照另一个规则进行第二次替换。

  3. 栅栏密码:栅栏密码是一种重排密码,它将明文中的字母按照一定规则排列成多行,并按照特定的顺序读取密文。

  4. 培根密码:培根密码是一种置换密码,它将明文中的每个字母映射到一个五位二进制码,然后将这些二进制码组合成密文。

  5. 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;
}

  • 69
    点赞
  • 75
    收藏
    觉得还不错? 一键收藏
  • 62
    评论
评论 62
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值