这个学期学校开了应用密码学的课,老师布置的一个作业就是挑选一种置换或者代换密码然后用C语言实现。
刚开始拿到组长分配的任务,觉得playfair很简单,完全可以一个小时搞定,可是真正动手去写,才发现这个算法的复杂。
不是需要多么高深的算法技巧,但是对于明文的整理,生成5x5矩阵的过程,加密的分析,需要用到二维数组,动态内存,等等。关键是网上对于playfair密码的介绍实在是很少啊。(如果大家有好的网站推荐一下,博主在此谢过了,嘿嘿)
当时我去了解这个原理完全是靠百度百科,后来一看,咦!与书上不一样。我去。。白写了大半天,百度百科中对明文的处理,加密的方式和书上差距比较大。
所以在这里我需要啰嗦一下加密的规则。
/**************************************************************************************************>
** 项目声明:本加密算法采用 playfair 加密方式。
** 传入的明(密)文允许出现非法字符(除字母之外的字符),算法可以自动剔除,得到合法明(密)文
** 且算法不会改变原始明(密)文的值。
**
** 使用须知:
** 由于无法对解密密文得到的明文中的缺省值做判断,例如
** 无法判断JKASX中的X是来自于缺省值还是明文中本就有的字符,故明文加密得到的
** 密文再经解密可能会与明文出现差异,并且由于I和J被视为一个字符,故解密得到
** 的明文需要判断I和J,大体上不影响明文翻译。
**
** 加密规则:
** 1、同行不同列:依次取右一位。
** 2、同列不同行:依次取下一位。
** 3、不同行且不同列:依次取对应的角。
**
** 解密规则:
** 1、同行不同列:依次取左一位。
** 2、同列不同行:依次取上一位。
** 3、不同行且不同列:依次取对应的角。
**
*******************************************************************************************/
下面是头文件playfair.h的内容
#ifndef _PLAYFAIR_H_
#define _PLAYFAIR_H_
typedef unsigned int status;
typedef unsigned short ushort;
#define ERROR 1
#define SUCCESS 0
#define OVERFLOW -1
#define EXTRA_LENGTH 20
#define TEXT_LENGTH 100
char *encrypt(char *plaintext, char *key);
char *decrypt(char *code, char *key);
status changeDeaultValue(char ch);
void getKeyMatrix(char (*matrix)[5], char *srckey);
char *sortPlainText(char *text);
char getDefaultValue();
ushort get_XY_From5x5Matrix(char ch, char (*matrix)[5]);
int abs(int num);
#endif
一、playfair密码:
-
Playfair密码是查尔斯▪惠斯通于1854年发明的一种加密方式,属于代换密码。
-
Playfair加密得到的密文有一个特征,位数必然为偶数,任意同组(两个一组,后面会介绍)的字母都是不一样的。
-
Playfair密码使用方便,并且可以很好地抵抗频度分析法的攻击。但是在1915年的一战被破译了。
二、加密规则: -
值得吐槽的是,我的教材上和网上的规则是不一样的,一度导致我写完算法之后测试,诶,这个怎么不对,,,,我去,又错了?!后来发现,我用网上的标准去给书上的明文加密,得到的密文和书上不一样。然后改为了书上的规则,发现又对不上网上的答案了。好吧,我当时肯定是写算法写晕了。
-
吐槽完毕,开始讲规则!
-
加密规则:
0、两个一组进行加密。
1、同行不同列:依次取右一位。
2、同列不同行:依次取下一位。
3、不同行且不同列:依次取对应的角。
-
解密规则:
1、同行不同列:依次取左一位。
2、同列不同行:依次取上一位。
3、不同行且不同列:依次取对应的角。
-
举例:
n 见下面矩阵,如果根据这个矩阵对HEYDDYLMOT进行加密,那么HE得到的密文是RY,,YD加密得到HC,DY加密得到CH,LM加密得到MN,OT加密得到PO。(声明一下,我这个规则可能不是标准的,但是我代码写好了,修改加密规则之后,我只要改几条语句就OK,当然,主要还是因为我现在还没找到传说中的标准-,如果大家有资源,推荐一下,感激不尽!)
-
详细步骤:
1.首先需要根据密钥生成5x5字母矩阵,这里我们规定矩阵中的字母都是大写的,而且因为5x5==25,所以有一个字母得被舍弃掉。我们把密钥依次加到矩阵里面,重复的字母忽略,密钥添加完毕之后,开始添加26字母中密钥之外的字母,例如我们的密钥是keyhere,那么得到的矩阵应该是:
K E Y H R
A B C D F
G I L M N
O P Q S T
U V W X Z
/*****************************************************************>
** 函数功能:
** 生成一个5x5大小,符合playfair规则的加密矩阵,并且返回。
**
** 参数传入:
** char martix[5][5]:一个5x5的矩阵(二维数组实现)
** char *key :密钥
**
** 函数返回:
** 无返回值。
****************************************************************/
void getKeyMatrix(char (*matrix)[5], char *key)
{
/* 生成5X5加密矩阵 */
int k = 0;
char temp[26];
memset(temp, 0, 26);
/* 将密钥加入到矩阵中 */
while (*key != 0) {
/* strchr的功能是判断一个字符串中是否有某个字符,
如果有,返回下标,没有则返回NULL */
if (strchr(temp, *key) == NULL) {
if ((*key >='a' && *key <= 'z') || (*key >= 'A' && *key <= 'Z')) {
temp[k++] = *key;
}
}
/* 指针后移 */
key++;
}
/* 将字符数组中的字母变为大写,由于是字符数组,所以不能使用strupr函数 */
int i = 0;
while (temp[i] != '\0') {
temp[i] = temp[i] >= 'a' ? temp[i] - 'a' + 'A' : temp[i];
i++;
}
/* 填充矩阵 */
for (int i = 'A'; i <= 'Z'; i++) {
if (i == 'J') {
continue;
}
/* 仍然需要查重 */
else {
if (strchr(temp, i) == NULL) {
temp[k++] = i;
}
}
}
/* 将temp中的数据转存到二维数组中 */
memcpy(matrix, temp, 25);
}
检查是否正确的方法很简单,只需要看一下最后一位是不是’Z’就行。这里需要注意的是,矩阵中是没有’J’的,在playfair密码中,把’I’和’J’视为同一个字母,在我的算法里面直接就把所有明文或者密文或者密钥中的’J’变为’I’。
2.接下来需要做的是整理明文,需要做的有五步:
1 去掉非法字符(字母以外都是非法字符)。
2 将明文变为大写(便于后续加密)。
3 保证每两个一组的明文不会重复。
4 把明文中的’J’替换为’I’。
5 保证明文为偶数位。