PlayFair加密方法原理及C+ +实现

2023-05-16

 普莱费尔密码(英文:Playfair cipher 或 Playfair square)是一种使用一个关键词方格来加密字符对的加密法,1854年由一位名叫查尔斯·惠斯通(Charles Wheatstone)的英国人发明。

加密步骤(我们采用英语作为语言)

1 选择一个(不包括z的)口令,然后去除其中重复的字母作为密钥

 我选择口令为augusttheodor,则密钥为augstheodr。

2 按照口令和字母表编制5*5密码表

 由于我们使用的是英语,采用去掉Z的规则时字母表共有25个,刚好不需要删减字母。事先约定密码表以行优先(也就是前n行放密钥),按照密钥-字母表中除了密钥之外的字母的顺序排列成5*5矩阵。

 对于这个矩阵,第一列是最后一列的右列,第一行是最后一行的下行,也就是它是循环的。

3 整理明文

 我们约定插入字母为X(也可以为Q)

 我们按照以下步骤对明文进行整理:

  1. 我们规定从头数起每两个字母为一个字母对。
  2. 在两两重复的字母对中的每个字母,插入一个插入字母构成两个字母对。
  3. 对插入之后的明文计数,如果明文的字母数为奇数,就在其末尾添加一个插入字母。

4 加密

 对于字母对<x,y>在密码表中的位置:

1.    x与y在同一行,我们用x与y在密码表中右侧的字母替代。

2.    x与y在同一列,我们用x与y在密码表中下方的字母替代。

3.    x与y既不在同一列也不在同一行,我们以其为对角线构造一个矩形,然后选择此矩形上另两个顶点对应的字母替代。至于替代顺序需要事先约定,我们这里规定同行替代

解密步骤

1 按照手上的密钥构造密码表

2 解密

 对密码对<a,b>,如果:

1.    ab在同一行:则明文对cd分别在其左侧

2.    ab在同一列:则明文对cd分别在其上侧

3.    ab的其他情况:则明文为以其为对角线构造的矩形的另两个顶点

# 解密算法

 相比于加密,解密的步骤其实并不困难,也就是最后的加密算法中最后的加密操作倒过来执行而已。

PlayFair算法的优点与缺点

优点

-破译工具简单

-采用双字母加密,相较于替换单字母的加密方式更为安全(请参照福尔摩斯中跳舞小人案)

缺点

-转换的密文不够精确,构建矩阵的时候需要取舍字母

-有很多需要提前约定的部分

示例

1 明文摘自Natural第一节:

Will you hold the line
When every one of them is giving up or giving in, tell me
In this house of mine
Nothing ever comes without a consequence or cost, tell me
Will the stars align?
Will heaven step in? Will it save us from our sin? Will it?

2 我们将明文全部小写然后去除非字母的所有符号,得到:

willyouholdthelinewheneveryoneofthemisgivinguporgivingintellmeinthishouseofminenothingevercomeswithoutaconsequenceorcosttellmewillthestarsalignwillheavenstepinwillitsaveusfromoursinwillit

# 可以使用ruby的downcase(全部小写)与gsub(正则表达式)完成这个步骤,代码如下:

a='''
你的未经处理的明文
'''
p a.downcase.gsub(/[^a-z]/,'')

口令为:

augusttheodor

则构造的密码表为:

[ A, U, G, S, T,

 H, E, O, D, R,

 B, C, F,  I, J,

 K, L, M, N, P,

 Q, V, W,X, Y,]

3 则明文构成的字母对为:

wi lx lx yo uh ol dt he li ne wh en ev er yo ne of th em is gi vi ng up or gi vi ng in te lx lx me in th is ho us eo fm in en ot hi ng ev er co me sw it ho ut ac on se qu en ce or co st te lx lx me wi lx lx th es ta rs al ig nw il lh ea ve ns te pi nw il li ts av eu sf ro mo ur si nw il li tx

4 通过加密,获得的密文为:

xfnvnvwrgeemraeomjldgodlcuohwrldfmaeolndsfxcmstldhsfxcmsnxaonvnvlonxaendedgtodmwnxdldadbmscuohfdlogxjsedgaubdmtoagdllcdhfdtaaonvnvloxfnvnvaeotaudtukfspxjmkehuucxdaonjpxjmmjatugcegihdwftednpxjmmjsy

加密算法的C++实现

 其实可以算是C语言的实现了,除了io之外并没有用到什么C++特有的部分。

1 算法文件构成

 -_playfair.h:头文件,包括各种用于加密的函数

 -_playfair.cpp:实现_playfiar.h文件中声明的文件

 -PLAYFAIR.cpp:主文件,main函数的文件

2 _playfair.h

#pragma once

const int MAXH = 22256; //输入最大文字数
const int MAXC = 1024; //输入的明文对最大数量
const char STOPC = 'x'; //插入字母

char* getCipher(char* a); //根据口令获取密钥
int countCipher(char* a); //返回密钥长度
char** getRect(int count,char* a); //返回密码矩阵
void getPairs(char in[], char p[][2]); //获得明文对
int wh_location(char p, char** rect); //返回一个十位为行个位为列的描述字母位置的数字
int is_situation(int p1, int p2); //判断两个字母在密码表中的情况
void getCiphertext(char** rect, char p[][2], char*ciphertext); //获取密文
int cipherCicle(int c); //判断循环

3 _playfair.cpp

#include "_playfair.h"
#include<stdlib.h>
#include<iostream>
using namespace std;

char* getCipher(char* a)
{
    char _s[27]; //存放出现过的字母的临时数组
    int _m = 0; //游标
    int _n = 0; //同上
    bool flag = false; //相同字母是否出现的bool
    static char re[27] ; //返回的字符串//口令转换后的长度必然不大于26 //请注意,这是一个静态变量,因为要保证返回的指针的正确性
    for (int i = 0; i < 27; i++) {
        _s[i] = ' ';
        re[i] = ' ';
    } //赋值
    for (int i = 0; a[i]!='\0'; i++) {
        for (int m = 0; m < 26; m++) {
            if (_s[m] == a[i]) { //出现过这个字母
                flag = true;
                break;
            }
        }
        if (flag == false) {
            re[_m] = a[i]; //输出字母
            _m++;
            _s[_n] = a[i]; //记录出现
            _n++;
        }
        flag = false;
    }
    re[_m] = '\0'; //记录结束
    return re;
}

int countCipher(char* a) { 
    int count = 0;
    for (int i = 0; a[i] != '\0'; i++) {
        count++;
    }
    return count;
}

char** getRect(int count, char* a) { //列先行后
    static char **rect;
    rect = (char**)malloc(sizeof(char*) * 5); //是五个指向字符数组的指针
    for (int i = 0; i < 5; i++) {
        rect[i] = (char*)malloc(sizeof(char) * 5); //是五个成员的字符数组
    }
     char alfabeto[] = "abcdefghijklmnopgrstuvwxy"; //字母表 //我们采用去掉z的方式构建,所以口令与密码矩阵里都没有z
    for (int i = 0; i < count; i++) {
        int l = i % 5;
        int h = i / 5;
        rect[h][l] = a[i]; //赋值
    }
    for (int m = 0; m < count; m++) { //去除出现过的字母
        for (int i = 0; i < 26; i++) {
            if (alfabeto[i] == a[m]) {
                alfabeto[i] = ' ';
                break;
            }
        }
    }
    for (int i = count; i < 25; i++) {
        int l = i % 5;
        int h = i / 5;
        for (int m = 0; m < 26; m++) { //遍历到第一个非空字母
            if (alfabeto[m] != ' ') {
                rect[h][l] = alfabeto[m];
                alfabeto[m] = ' ';
                break; 
            }
        }
    }
    return rect;
}

void getPairs(char in[], char p[][2]) {
    int keys = 0; //字母对计数
    for (int m = 0; in[m] != ' '; m=m+2) { //步长为2
        if (in[m] == in[m + 1]) { //如果出现重复字母对
            p[keys][0] = in[m];
            p[keys][1] = STOPC;
            p[keys + 1][0] = in[m];
            p[keys + 1][1] = STOPC;
            keys = keys + 2;
            }
        else {
            p[keys][0] = in[m];
            if (in[m + 1] == '\0') { //奇数末尾的处理
                p[keys][1] = STOPC;
            }
            else {
                p[keys][1] = in[m + 1];
            }
            keys++;
            }
    }
    return;
}

int wh_location(char p, char**rect) {
    for (int i = 0; i < 5; i++) {
        for (int m = 0; m < 5; m++) {
            if (p == rect[i][m]) {
                return i * 10 + m;
            }
        }
    }
    return -1; //Z的情况我没看到有资料写怎么处理
}

int is_situation(int p1, int p2) {
    if (abs(p1 - p2) < 10) { //在同一行
        return -1;
    }
    else if (abs(p1 - p2) % 10 == 0) { //在同一列
        return 1;
    }
    else {
        return 0;
    }
}

int cipherCicle(int c) {
    if (c >= 5) {
        return c - 5;
    }
    else if (c < 0) {
        return c + 5;
    }
    return c;
}

void getCiphertext(char** rect, char p[][2], char* ciphertext) {
    int loc1, loc2;
    int m = 0; //游标
    for (int i = 0; p[i][0] != ' '; i++) {
        loc1 = wh_location(p[i][0],rect);
        loc2 = wh_location(p[i][1], rect);
        switch (is_situation(loc1, loc2)) {
        case 1:
            ciphertext[m] = rect[cipherCicle(loc1 / 10 + 1)][loc1 % 10];
            ciphertext[m + 1] = rect[cipherCicle(loc2 / 10 + 1)][loc2 % 10];
            break;
        case -1:
            ciphertext[m] = rect[loc1 / 10][cipherCicle(loc1 % 10 + 1)];
            ciphertext[m + 1] = rect[loc2 / 10][cipherCicle(loc2 % 10 + 1)];
            break;
        case 0: //同行取代也就是,保留行数,交换列数
            ciphertext[m] = rect[loc1 / 10][loc2 % 10];
            ciphertext[m + 1] = rect[loc2 / 10][loc1 % 10];
            break;
        }
        m = m + 2;
    }
}

4 PLAYFAIR.cpp 

#include"_playfair.h"
#include<iostream>

using namespace std;

int main() {
    char passkey[256];
    char plaintext[MAXH]; //可以更改
    char ciphertext[MAXH*2]; //加密算法生成的密文不会膨胀
    char p[MAXC][2]; //明文对
    for (int i = 0; i < MAXC; i++) {
        p[i][0] = ' ';
        p[i][1] = ' ';
    }
    for (int i = 0; i < MAXH; i++) {
        plaintext[i] = ' ';
        ciphertext[i] = ' ';
    } //赋初值

    cout << "请输入口令(不包括z):";
    cin >> passkey;
    cout << "正在生成密钥。。。" << endl;
    char* cipher = getCipher(passkey);
    for (int i = 0; cipher[i] != ' '; i++) {
        cout << cipher[i];
    }//显示密钥
    cout << endl;

    int count = countCipher(cipher); //获取密钥长度

    cout << "正在获取密码矩阵。。。" << endl;
    char** rect = getRect(count, cipher); //获取矩阵
    for (int i = 0; i < 5; i++) {
        for (int m = 0; m < 5; m++) {
            cout << rect[i][m] << ' ';
        }
        cout << endl;
    }//显示矩阵

    cout << "请输入明文(不包括z):" << endl;
    cin >> plaintext; //获取明文

    cout << "正在获取明文对。。。" << endl;
    getPairs(plaintext, p); //获取明文对 
    for (int i = 0; p[i][0] != ' '; i++) {
        cout << p[i][0] << p[i][1] << ' ';
    }//显示明文对
    cout << endl;

    cout << "正在获取密文。。。" << endl;
    getCiphertext(rect, p, ciphertext); //获取密文
    for (int i = 0; ciphertext[i] != ' '; i++) {
        cout << ciphertext[i];
    }  //显示密文
    
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PlayFair加密方法原理及C+ +实现 的相关文章

随机推荐

  • ThinkPad E430 蓝牙驱动 BCM43142A0

    最近我意外发现公司的 ThinkPad E430 笔记本竟然是带有蓝牙的 D 查看蓝牙设备标识 ID 利用 lsusb 命令找到蓝牙模块信息 Bus 001 Device 004 ID 105b e065 Foxconn Internati
  • cephadm 安装部署 ceph 集群

    介绍 手册 xff1a https access redhat com documentation zh cn red hat ceph storage 5 html architecture guide index http docs c
  • PVE Cloud-INIT 模板配置

    PVE Cloud INIT 模板配置 Cloud init是什么 Cloud init是开源的云初始化程序 xff0c 能够对新创建弹性云服务器中指定的自定义信息 xff08 主机名 密钥和用户数据等 xff09 进行初始化配置 通过Cl
  • openstack 环境部署

    22 1 了解云计算 人类基于千年的物种衍变基础 xff0c 在这个世纪终于有了爆发式的科技成果 xff0c 尤其这二十年内互联网的发展 xff0c 更像是一种催化剂 xff0c 让原本已经热闹的地球更加的沸腾 xff0c 互联网经济泡沫破
  • C语言,计算圆的面积程序

    C语言 xff0c 计算圆的面积程序 span class token comment 计算圆的面积程序 日期 xff1a 2020 8 29 姓名 xff1a 张倩峰 span span class token macro propert
  • 博图软件搜索不到网卡

  • 台达伺服手动调试

  • 博途V15.1激活工具出错。

    博图V15 1激活 xff0c 软件出错 出现以下报错信息 解决方法 xff1a 下载新版本激活工具 再次激活
  • winCC正常运行,不显示画面。

    winCC正常运行 xff0c 不显示画面 解决方法 xff1a 需要重装系统 xff0c 重新安装博途
  • S7-1500PLC仿真

    S7 1500PLC仿真
  • 一些已安装产品需要许可证,请启动Automation License Manager

    更新系统版本号 完成更新 xff0c 再次安装即可解决该问题
  • ubuntu 硬盘管理工具

    就我目前所用的系统举例说明吧 xff0c 应该都大同小异的 有图形界面的 xff0c 也有命令行的 xff1a 首先是 ubuntu 系统自带的 Disk Utility 工具集 利用该工具可以对硬盘进行 Format Drive View
  • MCS-51单片机,定时1分钟,汇编程序

    MCS 51单片机 xff0c 定时1分钟 xff0c 汇编程序 去博客设置页面 xff0c 选择一款你喜欢的代码片高亮样式 xff0c 下面展示同样高亮的 代码片 span class token constant ORG span 00
  • c++枚举字符串转换工具

    为什么会需要这样一个枚举转字符串 xff0c 字符串转枚举的工具 xff1f 在太多的工程中 xff0c 我们可能都需要将一些枚举 整形标记打到日志中去 xff0c 如果只打印数组 xff0c 那也不行啊 xff0c 出问题翻看日志 xff
  • AD16在PCB布局的时候如何批量复制布局布线!!

    本人也是看了很多博主的帖子反反复复推敲 xff0c 最后发现有的博主没讲到关键部分所以在批量复制布局的时候总是事与愿违 话不多说请看招 xff01 第一步选中需要复制的布局 xff01 如图所示 第二步 复制选中布局的 offset Cha
  • Atcoder abc250 题解 (A~G)

    A Adjacent Squares xff08 枚举 xff09 枚举一下 xff0c 满足题意则ans 43 43 即可 cin span class token operator gt gt span h span class tok
  • 简单理解epoll

    epoll系列系统调用 epoll是Linux特有的I O复用函数 epoll使用一组函数来完成任务 epoll把用户关心的文件描述符上的事件放在内核里的一个事件表中 epoll需要使用一个额外的文件描述符 xff0c 来唯一标识内核中的事
  • glibc-2.23 puts源码分析

    在分析puts代码之前先看一些基本的知识 一些flag span class token macro property span class token directive hash span span class token direct
  • Sublime Text 搭建 C++ 环境

    一 下载MinGW文件 1 下载mingw get setup xff1a 网址 xff1a https sourceforge net projects mingw 由于这是境外网站 xff0c 请自行解决连接问题 xff08 下载的文件
  • PlayFair加密方法原理及C+ +实现

    普莱费尔密码 xff08 英文 xff1a Playfair cipher 或 Playfair square xff09 是一种使用一个关键词方格来加密字符对的加密法 xff0c 1854年由一位名叫查尔斯 惠斯通 xff08 Charl