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

 普莱费尔密码(英文: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];
    }  //显示密文
    
}

  • 3
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值