维吉尼亚密码(又译维热纳尔密码)是使用一系列凯撒密码组成密码字母表的加密算法,属于多表密码的一种简单形式。
【加密原理】
明文:I Love You
密钥:OK
首先,密钥长度需要与明文长度相同,如果少于明文长度,则重复拼接直到长度相等。本例中,明文长度为8个字母(非字母忽略),密钥补全为“OKOKOKOK”。
然后根据密码表进行加密。明文第一个字母是“I”,密钥第一个字母是“O”,在表格中找到“I”列与“O”行的相交点,字母“W”就是密文的第一个字母。同理,“L”列与“K”行交点字母是“V”。“O”列与“O”行交点字母是“C”……以此类推,得到密文: W VCFS ICE。
【解密原理】
密文:PWZRNZBZ EA NQKBUHN LNB
密钥:wind
首先把密钥重复拼接到和密文长度相同,上例中密文为20位字母,密钥拼接后为:windwindwindwindwind。
密文P对应密钥W,在密码表中找出W行为P的列,沿着这一列向上找到最上面的字母是T。以此类推,得到明文:tomorrow is another day。
【卡西斯基试验】是基于类似的常用单词有可能被同样的密钥字母进行加密,从而在密文中重复出现。例如,明文中不同的CRYPTO可能被密钥ABCDEF加密成不同的密文:
密钥:ABCDEF AB CDEFA BCD EFABCDEFABCD
明文:CRYPTO IS SHORT FOR CRYPTOGRAPHY
密文:CSASXT IT UKSWT GQU GWYQVRKWAQJB
此时明文中重复的元素在密文中并不重复。然而,如果密钥相同的话,结果可能便为(使用密钥ABCD):
密钥:ABCDAB CD ABCDA BCD ABCDABCDABCD
明文:CRYPTO IS SHORT FOR CRYPTOGRAPHY
密文:CSASTP KV SIQUT GQU CSASTPIUAQJB
此时卡西斯基试验就能产生效果。对于更长的段落此方法更为有效,因为通常密文中重复的片段会更多。如通过下面的密文就能破译出密钥的长度:
密文:DYDUXRMHTVDVNQDQNWDYDUXRMHARTJGWNQD
其中,两个DYDUXRMH的出现相隔了18个字母。因此,可以假定密钥的长度是18的约数,即长度为18、9、6、3或2。而两个NQD则相距20个字母,意味着密钥长度应为20的约数,20、10、5、4或2。取两者的交集,则可以基本确定密钥长度为2。
一旦能够确定密钥的长度,密文就能重新写成多列,列数与密钥长度对应。这样每一列其实就是一个凯撒密码,而此密码的密钥(偏移量)则对应于维吉尼亚密码密钥的相应字母。与破译凯撒密码类似的方法,就能将密文破译。
【C++实现】
#include<iostream>
using namespace std;
#define MINCHAR 32
#define CHARSUM 94
char table[CHARSUM][CHARSUM];
bool Init();
bool Encode(char* key, char* source, char* dest);
bool Dncode(char* key, char* source, char* dest);
int main()
{if (!Init()){cout << "初始化错误!" << endl;return 1;}char key[256];char str1[256];char str2[256];int operation;while (1){do{cout << "请选择一个操作:1. 加密; 2. 解密; -1. 退出\n";cin >> operation;} while (operation != -1 && operation != 1 && operation != 2);if (operation == -1)return 0;else if (operation == 1)//加密 {cout << "请输入密钥:";cin >> key;cout << "请输入待加密字符串:";cin >> str1;Encode(key, str1, str2);cout << "加密后的字符串:" << str2 << endl;}else if (operation == 2)//解密 {cout << "请输入密钥:";cin >> key;cout << "请输入待解密字符串:";cin >> str1;Dncode(key, str1, str2);cout << "解密后的字符串:" << str2 << endl;}cout << endl;}return 0;
}
// 初始化维吉尼亚方阵
bool Init()
{int i, j;for (i = 0; i < CHARSUM; i++){for (j = 0; j < CHARSUM; j++){table[i][j] = MINCHAR + (i + j) % CHARSUM;}}return true;
}
// 加密
// key:密钥
// source:待加密的字符串
// dest:经过加密后的字符串
bool Encode(char* key, char* source, char* dest)
{char* tempSource = source;char* tempKey = key;char* tempDest = dest;do{*tempDest = table[(*tempKey) - MINCHAR][(*tempSource) - MINCHAR];tempDest++;if (!(*(++tempKey)))tempKey = key;} while (*tempSource++);dest[strlen(source)] = 0;return true;
}
// 解密
// key:密钥
// source:待解密的字符串
// dest:经过解密后的字符串
bool Dncode(char* key, char* source, char* dest)
{char* tempSource = source;char* tempKey = key;char* tempDest = dest;char offset;do{offset = (*tempSource) - (*tempKey);offset = offset >= 0 ? offset : offset + CHARSUM;*tempDest = MINCHAR + offset;tempDest++;if (!(*(++tempKey)))tempKey = key;} while (*++tempSource);dest[strlen(source)] = 0;return true;
}