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

news2024/11/15 8:25:19

目录

一.古典密码

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/728260.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python解析考试题库数据

应单位要求需要参加某个考试&#xff0c;但考试需要从手机端登陆学习&#xff0c;1000多道题需要挨个刷一遍太过于麻烦&#xff0c;萌生了把题目和答案全部扒下来的想法&#xff0c;再用python做数据的清洗和梳理&#xff0c;最后整合出来所有的考试题库信息。 首先打开浏览器…

Socks5代理是什么?有哪些优势?

在线隐私和⽹络领域&#xff0c;Socks5代理已经成为⼀种受欢迎且强⼤的⼯具。但是Socks5是什么呢&#xff1f;使⽤Socks代理有什么优势&#xff1f;在本⽂中&#xff0c;Oxylabs和你一起探讨这些问题&#xff0c;揭⽰Socks5代理的世界。 深⼊了解Socks5代理 socks&#xff08…

计算机网络 day3 广播风暴 - VLAN - Trunk

目录 广播风暴&#xff1a; 1.什么是广播风暴&#xff1f; 2.危害&#xff1a; 3.防范 STP生成树协议&#xff1a;(72条消息) 生成树协议 — STP_生成树协议步骤_一下子就醒了的博客-CSDN博客 VLAN&#xff1a; VLAN是什么&#xff1f; VLAN起到的作用&#xff1a; 广…

软件测试常见基础知识面试题

一、测试基础理论 1.1 什么是软件测试&#xff1f; 为了发现程序中的错误而执行程序的过程 1.2 软件测试流程 1&#xff09;需求调查 2&#xff09;制定初步的项目计划 3&#xff09;测试准备 4&#xff09;测试设计 5&#xff09;测试实施 6&#xff09;测试评估 1.3 软件…

华为OD机试真题 Python 实现【字符串解密】【2023 B卷 100分】,附详细解题思路

目录 一、题目描述二、输入描述三、输出描述四、解题思路五、Python算法源码六、效果展示1、输入2、输出3、说明 一、题目描述 给定两个字符串string1和string2。 string1是一个被加扰的字符串。string1由小写英文字母&#xff08;‘a’-‘z’&#xff09;和数字字符&#xf…

【SCI征稿】计算机算法、通信、人工智能、网络、物联网、机器学习等领域,13本期刊影响因子上涨,这几本期刊录用快

2023年JCR发布后&#xff0c;计算机领域SCI期刊有13本影响因子上涨&#xff0c;审稿周期短&#xff0c;进展顺利&#xff1a; 1️⃣IF&#xff1a;6.0-7.0↑&#xff0c;JCR2区&#xff0c;中科院3区&#xff0c;SCI&EI 双检&#xff0c;CCF-C类 征稿领域&#xff1a;概率…

喜讯 | ShowMeBug CEO 李亚飞获评“稀土开发者”年度技术引领人物

近日&#xff0c;由稀土开发者大会举办的「掘金技术引力榜」评选活动揭晓&#xff0c;李亚飞从众多优秀的技术开发者中脱颖而出&#xff0c;获评年度技术引领人物。 稀土开发者大会年度技术引领人物获奖榜单 稀土开发者大会是由稀土掘金技术社区主办&#xff0c;集国际化前瞻…

c# opencv 找到图像的轮廓,并绘制轮廓

Mat colorMat new Mat();Mat outMat new Mat();Mat resultMat new Mat();Mat src Cv2.ImRead("I:\\mask.jpg");//转成灰度图Cv2.CvtColor(src, colorMat, ColorConversionCodes.RGB2GRAY);//对灰度图像进行阈值操作得到二值图像Cv2.Threshold(colorMat, outMat, 0…

【Python编程系列】3、初识Python代码:输入输出

1、输出 在PyCharm上新建项目和python文件后,编写一行代码,输出:"Hello World"这句话。代码为: print("Hello World")PyCharm上运行代码有两种方式,如下图1和2。代码运行结果在下方显示: print函数打印字符串时加单引号或双引号,不能混用。 单引…

大厂都在做的jmeter接口自动化测试登峰造极的JMETER实现接口自动化测试

目录 一、JMETER的环境搭建 二、JMETER的汉化 三、JMETER的接口请求 四、JMETER的参数化 五、JMETER的JSON传参 六、JMETER的断言添加 一、JMETER的环境搭建 二、JMETER的汉化 临时汉化方法&#xff1a;打开jmeter&#xff0c;options-->choose language-->选择语言…

simulink 常用子系统 核心是函数封装simulink function

目录 Enabled Subsystem Triggered Subsystem Function-call Subsystem Simulink Function Enabled Subsystem Triggered Subsystem Function-call Subsystem Simulink Function 类似c函数的封装 数组形参

ikun猛戳进来丨使用Python打造属于你的ikun音乐播放器,简直不要太好用

首先声明&#xff0c;我不是小黑子&#xff0c;我不是小黑子&#xff01; 作为一个ikun&#xff0c;时刻都在想着我们家姐姐&#xff01; 这不上次用Python做了一个ikun飞机大战&#xff0c;今天再给大家整活一手&#xff0c;Python tkinter开发一个专属ikun音乐播放器&#x…

CVPR 2023 | 掩码图像建模MIM的理解、局限与扩展

编者按&#xff1a;掩码图像建模&#xff08;Masked Image Modeling, MIM&#xff09;的提出&#xff0c;为计算机视觉模型训练引入无监督学习做出了重要贡献。得益于 MIM 的预训练算法&#xff0c;计算机视觉领域在近年来持续输出着优质的研究成果。然而整个业界对 MIM 机制的…

【Java从入门到大牛】程序流程控制

&#x1f525; 本文由 程序喵正在路上 原创&#xff0c;CSDN首发&#xff01; &#x1f496; 系列专栏&#xff1a;Java从入门到大牛 &#x1f320; 首发时间&#xff1a;2023年7月7日 &#x1f98b; 欢迎关注&#x1f5b1;点赞&#x1f44d;收藏&#x1f31f;留言&#x1f43e…

STM32CubeMX实现USB虚拟串口环回测试功能

STM32CubeMX实现USB虚拟串口环回测试功能 &#x1f4cd;参考ST官方给出的参考案例&#xff1a;https://www.stmcu.com.cn/Designresource/detail/LAT/711466&#x1f388;同功能配置可以参考《STM32 USB使用记录&#xff1a;使用CDC类虚拟串口&#xff08;VCP&#xff09;进行通…

MySQL系统函数

系统函数&#xff1a; 数学函数 1、abs() pi() 2、sqrt() 3、cell() floor() round() truncate() cell是向上取整&#xff0c;floor是向下取整。 注意&#xff1a;round负数和java里面的四舍五入有所不同 Round还可以指明位数 turncate是截断 4、power() …

颜色分类 (力扣)JAVA

给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums &#xff0c;原地对它们进行排序&#xff0c;使得相同颜色的元素相邻&#xff0c;并按照红色、白色、蓝色顺序排列。 我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 必须在不使用库内置的 sort 函数的情况下解决这…

用牛鲨NFT赚取SUI的机会,这不就来了?

很高兴向大家宣布Mysten Labs的Bullshark Quests活动&#xff0c;这是一个为Bullshark持有者提供赚取SUI奖励&#xff08;和其他福利&#xff09;的全新方式&#xff0c;Bullshark是ACES计划的一部分。Bullshark Quests将成为一个持续的活动&#xff0c;为Bullshark持有者提供通…

为什么需要多语言并行机器翻译?

随着全球化的加速和不同语言之间的交流需求不断增长&#xff0c;多语言机器翻译&#xff08;Multilingual Parallel Machine Translation&#xff09;成为一个备受关注的领域。传统上&#xff0c;机器翻译系统主要集中于一对特定语言之间的翻译&#xff0c;但这种单一语言对的模…

微服务的划分姿势分享

微服务是一种理念&#xff0c;没有确切的定义和边界&#xff0c;好比设计原则&#xff0c;是属于抽象的概念。在定义不明确的情况下谈划分也是一种各说各话&#xff0c;具体问题需要具体分析。 微服务的划分矛盾在于粒度&#xff0c;如果粒度太大了&#xff0c;分和不分似乎都差…