Playfair密码(原理+代码)

目录

1、原理

2、流程图

3、编程实现

4、总结


1、原理

        Playfair密码是一种使用一个关键词方格来加密字符对的加密算法,是一种多表代换的对称加密技术

        它依据一个5*5的正方形组成的密码表来编写,密码表里排列有25个字母。如果一种语言字母超过25个,可以去掉使用频率最少的一个。如,法语一般去掉w或k,德语则是把i和j合起来当成一个字母看待。英语中z使用最少,可以去掉它。可以很好的防御频率分析法的攻击。

        同时Playfair密码对明文和密文有一定的要求:在加密之前首先需要整理明文,将明文中两个字母组成一组,如果两个字母相同则分成两组,在每组的后面加字母X或Q。如果明文字母个数是奇数,在最后一个字母之后加字母X或Q。密文的字母个数一定是偶数,任意两个同组的字母都不会相同。

        Playfair密码加密的一般过程:

        1、输入明文

        2、输入密钥得到密码表

        3、整理明文

        4、加密明文得到密文

        首先通过将密钥字母放在密码表的头部(第一行及之后或第一列及之后),之后将其他字母按字母顺序填充到密码表中以得到密码表。由于英文字母有26个,所以一般会将i和j放在同一个位置上。之后整理明文,最后加密明文。

        加密规则:

  • 若m1,m2在同一行,对应密文c1,c2分别是紧靠m1,m2 右端的字母。其中第一列被看做是最后一列的右方。

  • 若m1,m2在同一列,对应密文c1,c2分别是紧靠m1,m2 下方的字母。其中第一行被看做是最后一行的下方。

  • 若m1,m2不在同一行,不在同一列,则c1,c2是由m1,m2确定的矩形的其他两角的字母(横向替换或者纵向替换)。

    解密就是加密的逆过程。

  • 若c1,c2在同一行,对应明文m1,m2分别是紧靠c1,c2 左端的字母。其中第一列被看做是最后一列的右方。

  • 若c1,c2在同一列,对应明文m1,m2分别是紧靠c1,c2 上方的字母。其中第一行被看做是最后一行的下方。

  • 若c1,c2不在同一行,不在同一列,则m1,m2是由c1,c2确定的矩形的其他两角的字母(横向替换或者纵向替换)。

2、流程图

3、编程实现

        难点:

        1、如何根据密钥生成密码表

        2、如何将密文解密恢复成明文

        难点1:

        由于密码表是25个字母,可以将j和i都看作字母i,即将明文中的j全部看作是i。

        对于密码表,先将密钥字母填入密码表,再按字母顺序将未填入的字母填入到密码表中。

void initkey() {

	cout << endl << "请输入密钥以生成密码表:" << endl;

	string key = "";//输入密钥
	cin >> key;

	int i = 0;
	int j = 0;

	for (char ch : key) {
		if (ch == ' ' || vis[ch - 'a'])continue;
		if (ch == 'j')ch = 'i';
		m.insert(myMap::value_type(ch - 32, { i,j }));//不知道哪根筋抽了用map来存储密码表
		vis[ch - 'a'] = 1;
		i += j == 4;
		j = (j + 1) % 5;
	}

	for (int k = 0; k < 26; k++) {
		if (k == 9)continue;
		if (!vis[k]) {
			m.insert(myMap::value_type(k + 'A', { i,j }));
			vis[k] = 1;
			i += j == 4;
			j = (j + 1) % 5;
		}
	}

	//for (auto &temp : m) {
	//	cout << temp.first << " " << temp.second.first << " " << temp.second.second << endl;
	//}

	cout << "成功生成密码表" << endl;
}

        难点2:

        密文恢复成明文就是明文加密成密文的逆过程,但是由于明文中的j变成了i,如果明文为奇数或者相邻两个字母相同时,还会加X,所以最后得到的明文并不是真正的明文,需要经过一些处理,在这里省略了处理的过程(其实是不会处理),毕竟主要还是学习密码的思想。

        具体代码如下:  

        默认输入为小写字母不带空格

        主函数

#include"Playfair.h"

int main() {
	while (1) {
		show();		//菜单界面
		keyDown();	//按键处理
		system("pause");
		system("cls");
	}
}

        Playfair.h

#pragma once
#include<cstdio>
#include<iostream>
#include<string>
#include<Windows.h>
#include<unordered_map>
using namespace std;
void init();
void initkey();
void show();
void keyDown();
void readFile();
void saveFile();
void encrypt();
void decrypt();

        Playfair.cpp

#include "Playfair.h"

string finalStr;
string fileStr;
typedef unordered_map<char, pair<int, int>> myMap;//密码表
myMap m;//5*5密码表 大写
int vis[26];//生成密码表标记 去掉j

void init() {

	finalStr = "";
	fileStr = "";
	m = myMap();
	memset(vis, 0, sizeof(vis));
}

void show()
{
	cout << "****************Playfair密码****************" << endl;
	cout << "\t\t1.加密文件" << endl;
	cout << "\t\t2.解密文件" << endl;
	cout << "\t\t3.退出" << endl;
	cout << "******************************************" << endl;
}

void keyDown()//按键处理
{
	int userkey = 0;
	cin >> userkey;
	switch (userkey) {
	case 1:
		cout << "-----------------加密文件-----------------" << endl;
		readFile();
		initkey();
		encrypt();
		saveFile();
		init();
		break;
	case 2:
		cout << "-----------------解密文件-----------------" << endl;
		readFile();
		initkey();
		decrypt();
		saveFile();
		init();
		break;
	case 3:
		exit(0);
		break;
	}
}

void readFile()//读取文件
{
	cout << "请输入文件名:" << endl;
	string fileName;
	cin >> fileName;
	FILE* fp = fopen(fileName.c_str(), "r+");
	if (fp == nullptr) {
		cout << "未找到相关文件" << endl;
		return;
	}
	else {
		cout << "成功打开文件" << endl;
	}
	char ch;
	int pos = 0;
	while ((ch = fgetc(fp)) != EOF) {
		fileStr += ch;
	}
	cout << endl << "明文为:" << endl;
	cout << fileStr << endl;
	fclose(fp);
}



void saveFile()//保存文件
{
	string fileName;
	cout << endl << "请输入要保存信息的文件名:" << endl;
	cin >> fileName;
	FILE* fp = fopen(fileName.c_str(), "w+");
	if (fp == nullptr) {
		cout << endl << "保存文件失败" << endl;
		return;
	}
	else {
		cout << endl << "保存成功" << endl;
	}
	fprintf(fp, "%s", finalStr.c_str());
	fclose(fp);

}


void initkey() {

	cout << endl << "请输入密钥以生成密码表:" << endl;
	string key = "";//输入密钥
	cin >> key;
	int i = 0;
	int j = 0;
	for (char ch : key) {
		if (ch == ' ' || vis[ch - 'a'])continue;
		if (ch == 'j')ch = 'i';
		m.insert(myMap::value_type(ch - 32, { i,j }));
		vis[ch - 'a'] = 1;
		i += j == 4;
		j = (j + 1) % 5;
	}
	for (int k = 0; k < 26; k++) {
		if (k == 9)continue;
		if (!vis[k]) {
			m.insert(myMap::value_type(k + 'A', { i,j }));
			vis[k] = 1;
			i += j == 4;
			j = (j + 1) % 5;
		}
	}
	//for (auto &temp : m) {
	//	cout << temp.first << " " << temp.second.first << " " << temp.second.second << endl;
	//}
	cout << "成功生成密码表" << endl;
}

char researchkey(int x, int y) {//在密码表里面查找明文字母对应的密文字母
	for (myMap::iterator it = m.begin(); it != m.end(); it++) {
		if (it->second.first == x && it->second.second == y) {
			return it->first;
		}
	}
}

void encrypt() {//整理明文并加密

	string temp;//存放大写明文
	for (int i = 0; i < fileStr.size(); i++) {// 通过这个for循环去除非法字符以及将明文字母变为大写,将'J'变为'I'  
		if ((fileStr[i] >= 'a' && fileStr[i] <= 'z') || (fileStr[i] >= 'A' && fileStr[i] <= 'Z')) {
			if (fileStr[i] == 'j' || fileStr[i] == 'J') {
				temp += 'I';
			}
			else {
				temp += fileStr[i] >= 'a' ? fileStr[i] - 'a' + 'A' : fileStr[i];
			}
		}
	}

	if (temp.size() % 2 == 1) {
		temp += 'X';
	}
	//cout << temp << endl;

	int j1 = 0, j2 = 0, l1 = 0, l2 = 0;	//两个字符的坐标
	char p1, p2;								//两个字符
	for (int i = 0; i < temp.size(); i += 2) {
		p1 = temp[i];
		p2 = temp[i + 1];

		j1 = m[p1].first;
		l1 = m[p1].second;

		j2 = m[p2].first;
		l2 = m[p2].second;

		if (p1 != p2) {

			//printf("%c %c %d %d %d %d\n",p1,p2,j1,l1,j2,l2);
			if (j1 == j2) {
				finalStr += researchkey(j1, (l1 + 1) % 5);
				finalStr += researchkey(j2, (l2 + 1) % 5);
			}
			else if (l1 == l2) {
				finalStr += researchkey((j1 + 1) % 5, l1);
				finalStr += researchkey((j2 + 1) % 5, l2);
			}
			else if (j1 != j2 && l1 != l2) {
				finalStr += researchkey(j1, l2);
				finalStr += researchkey(j2, l1);
			}
		}
		else {
			//p1p3 p2p3
			char p3 = 'X';
			int j3, l3;
			j3 = m[p3].first;
			l3 = m[p3].second;

			if (j1 == j3) {
				finalStr += researchkey(j1, (l1 + 1) % 5);
				finalStr += researchkey(j3, (l3 + 1) % 5);
			}
			else if (l1 == l3) {
				finalStr += researchkey((j1 + 1) % 5, l1);
				finalStr += researchkey((j3 + 1) % 5, l3);
			}
			else if (j1 != j3 && l1 != l3) {
				finalStr += researchkey(j1, l3);
				finalStr += researchkey(j3, l1);
			}

			if (j2 == j3) {
				finalStr += researchkey(j2, (l2 + 1) % 5);
				finalStr += researchkey(j3, (l3 + 1) % 5);
			}
			else if (l2 == l3) {
				finalStr += researchkey((j2 + 1) % 5, l2);
				finalStr += researchkey((j3 + 1) % 5, l3);
			}
			else if (j3 != j2 && l3 != l2) {
				finalStr += researchkey(j2, l3);
				finalStr += researchkey(j3, l2);
			}
		}

	}
	cout << endl << "生成的密文为:" << endl;
	cout << finalStr << endl;
}

void decrypt() {//解密密文

	string temp = fileStr;


	int j1 = 0, j2 = 0, l1 = 0, l2 = 0;	//两个字符的坐标
	char p1, p2;						//两个字符
	for (int i = 0; i < temp.size(); i += 2) {

		p1 = temp[i];
		p2 = temp[i + 1];

		j1 = m[p1].first;
		l1 = m[p1].second;

		j2 = m[p2].first;
		l2 = m[p2].second;

		if (j1 == j2) {
			finalStr += researchkey(j1, (l1 + 4) % 5) + 32;
			finalStr += researchkey(j2, (l2 + 4) % 5) + 32;
		}
		else if (l1 == l2) {
			finalStr += researchkey((j1 + 4) % 5, l1) + 32;
			finalStr += researchkey((j2 + 4) % 5, l2) + 32;
		}
		else if (j1 != j2 && l1 != l2) {
			finalStr += researchkey(j1, l2) + 32;
			finalStr += researchkey(j2, l1) + 32;
		}
	}

	cout << endl << "明文为:" << endl;
	cout << finalStr << endl;

}

4、总结

        Playfair密码作为一种多表代换密码,不同位置上相同的字母会被加密成不一样的密文,可以有效的防止频率分析法的攻击。不过在使用Playfair之前双方需要进行一些规定,比如密码表是去掉z还是将i和j放到一个位置上,是加字母Q还是加字母X等等。所以没有必要去纠结一些细节,现在也不会再使用这样的古典密码算法,学习密码算法的思想才是关键。

下面是一个简单的 Python Playfair 密码实现代码: ```python from collections import OrderedDict class PlayfairCipher: def __init__(self, key): self.key = key self.key_matrix = self.generate_matrix(key) def generate_matrix(self, key): key = key.upper().replace("J", "I") key = "".join(OrderedDict.fromkeys(key)) key_matrix = [] for i in range(0, len(key), 5): key_matrix.append(list(key[i:i+5])) return key_matrix def encrypt(self, plaintext): plaintext = plaintext.upper().replace("J", "I") plaintext = "".join(OrderedDict.fromkeys(plaintext)) plaintext = plaintext.replace(" ", "") if len(plaintext) % 2 != 0: plaintext += "X" ciphertext = "" for i in range(0, len(plaintext), 2): a, b = plaintext[i], plaintext[i+1] a_pos = self.get_position(a) b_pos = self.get_position(b) if a_pos[0] == b_pos[0]: ciphertext += self.key_matrix[a_pos[0]][(a_pos[1]+1)%5] ciphertext += self.key_matrix[b_pos[0]][(b_pos[1]+1)%5] elif a_pos[1] == b_pos[1]: ciphertext += self.key_matrix[(a_pos[0]+1)%5][a_pos[1]] ciphertext += self.key_matrix[(b_pos[0]+1)%5][b_pos[1]] else: ciphertext += self.key_matrix[a_pos[0]][b_pos[1]] ciphertext += self.key_matrix[b_pos[0]][a_pos[1]] return ciphertext def decrypt(self, ciphertext): plaintext = "" for i in range(0, len(ciphertext), 2): a, b = ciphertext[i], ciphertext[i+1] a_pos = self.get_position(a) b_pos = self.get_position(b) if a_pos[0] == b_pos[0]: plaintext += self.key_matrix[a_pos[0]][(a_pos[1]-1)%5] plaintext += self.key_matrix[b_pos[0]][(b_pos[1]-1)%5] elif a_pos[1] == b_pos[1]: plaintext += self.key_matrix[(a_pos[0]-1)%5][a_pos[1]] plaintext += self.key_matrix[(b_pos[0]-1)%5][b_pos[1]] else: plaintext += self.key_matrix[a_pos[0]][b_pos[1]] plaintext += self.key_matrix[b_pos[0]][a_pos[1]] return plaintext def get_position(self, letter): for i, row in enumerate(self.key_matrix): if letter in row: return (i, row.index(letter)) raise ValueError(f"{letter} is not in the key matrix") ``` 使用示例: ```python key = "PLAYFAIREXAMPLE" cipher = PlayfairCipher(key) plaintext = "Hide the gold in the tree stump" ciphertext = cipher.encrypt(plaintext) print(ciphertext) # 输出:BMNDZBXDNABEKUDMUIXMMOUVIF decrypted_plaintext = cipher.decrypt(ciphertext) print(decrypted_plaintext) # 输出:HIDETHEGOLDINTHETREXESTUMP ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值