JavaScript 实现 Playfair 与 Hill 古典密码算法
Playfair 密码基于密钥生成的 5x5 矩阵,通过同行、同列或矩形规则加密双字母组合;Hill 密码则运用线性代数中的矩阵乘法对明文进行转换。本文通过 JavaScript 代码展示这两种算法的底层逻辑与实现方式。
Playfair 密码实现
Playfair 密码(Playfair cipher)是一种多字母替换密码。它依赖一个 5x5 的正方形网格作为密码表,通常包含 26 个英文字母中除去最常见的 'Z' 后的 25 个字母(或将 I/J 合并)。加密过程需要将明文划分为双字母组,并根据字母在矩阵中的相对位置执行不同的替换规则。
第一步:构建密钥矩阵
密钥矩阵是算法的核心。编制时首先将密钥单词去除重复字母和空格,然后按顺序填入矩阵剩余位置。通常使用 A-Y 的字母序列填充未被占用的位置,J 往往与 I 共用或省略 Z。
例如,若密钥为 "crazy dog",处理后的前导字母为 C、O、H、M、T...,随后依次填入其余字母:
| C | O | H | M | T |
| R | G | I | N | U |
| A | B | J | P | V |
| Y | E | K | Q | W |
| D | F | L | S | X |
以下是使用 JavaScript 构建该矩阵的逻辑:
/*
* 功能:编制 Playfair 密码表
* 参数:keychars (已转大写、去空格、去重复的密钥字符串)
*/
function createKey(keychars) {
// 定义基础字母表 (A-Y,排除 Z)
var allChars = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y'];
// 从基础表中移除密钥中包含的字母
for(var i = 0; i < keychars.length; i++){
var index = allChars.indexOf(keychars[i]);
if (index > -1) {
allChars.splice(index, 1);
}
}
// 将密钥首字母逆向插入到数组头部(保持密钥字母优先且正序排列)
for(var i = keychars.length-1; i >= 0; i--){
allChars.unshift(keychars[i]);
}
// 将一维数组填充为 5x5 二维矩阵
var keyMatrix = new Array(5).fill(0).map(() => new Array(5));
for(var i = 0 ; i < 5 ; i++){
for(var j = 0; j < 5; j++){
keyMatrix[i][j] = allChars[i*5+j];
}
}
return keyMatrix;
}
为了保证密钥字符唯一性,预处理阶段需使用去重函数:
function removeDuplicate(str){
var result = [], tempStr = "";
var arr = str.split('');
for(var i = 0; i < arr.length; i++){
var isRepeat = false;
for(var j = 0; j < result.length; j++){
if(arr[i] == result[j]) {
isRepeat = true;
break;
}
}
// 仅当不是重复字符时才加入结果
if(!isRepeat){
result.push(arr[i]);
}
}
return result.join("");
}
第二步:整理明文
加密前需对明文进行标准化:
1. 去除所有空格并将字符转为大写。
2. 按顺序将字母两两分组。如果分组出现相同字母相邻(如 LL),则在中间插入分隔符(通常为 'X')。如果最后剩单数个字母,末尾补 'X'。
第三步:加密与解密规则
根据两个字母在矩阵中的坐标,应用以下规则生成密文:
- 同行:替换为各自右侧紧邻的字母(循环回到行首)。
- 同列:替换为各自下方紧邻的字母(循环回到列首)。
- 矩形:选取同一行不同列的两个点构成的矩形,替换为与这两个点同行但位于另一列角的字母(通常约定左对齐右移)。
示例演示:
明文:where there is life,there is hope.
处理后分组:wh er et he re is li fe th er ei sh op ex
依据上述矩阵,密文结果为:kt yg wo ok gy nl hj of cm yg kg lm mb wf
解密过程则是上述规则的逆向操作。完整算法逻辑运行效果如下:
Hill 密码实现
Hill 密码是一种利用矩阵论的替换密码。它将明文分为长度为 n 的向量,通过与一个 n x n 的可逆密钥矩阵相乘来生成密文向量,最后对 26 取模转换为字母。
关键步骤解析
- 准备字母表:使用
A-Z的索引映射数字 0-25。 - 生成密钥:通常生成一个
n x n矩阵。为保证可解性,实际应用中需验证行列式与 26 互质,但在本算法的简化版中,主要演示计算流程。
/*
* 随机生成 3x3 密钥矩阵 (数值范围 0-25)
*/
function randomCreateKey() {
let key = [];
for(let i = 0; i < 3; i++) {
key[i] = [];
for(let j = 0; j < 3; j++) {
key[i][j] = Math.floor(Math.random() * 26);
}
}
return key;
}
- 矩阵运算加密: 每三个字母构成一个列向量,与 3x3 密钥矩阵做乘法,结果对 26 取模后映射回字母。
function hillEncrypt(pText, keyMatrix) {
let chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
let plainArr = pText.toUpperCase().replace(/[^A-Z]/g, '').split('');
let res = "";
// 每次处理 3 个字母
let round = Math.ceil(plainArr.length / 3);
for(let b = 0; b < round; b++) {
let tempArr = [];
let sumArr = [];
// 提取当前块并转为数字索引
for(let i = 0; i < 3; i++) {
let charCode = plainArr[b*3 + i] || 'X'; // 不足补X
tempArr[i] = chars.indexOf(charCode);
}
// 矩阵乘法与取模
for(let i = 0; i < 3; i++) {
let sum = 0;
for(let k = 0; k < 3; k++) {
sum += keyMatrix[i][k] * tempArr[k];
}
sumArr[i] = sum % 26;
}
// 还原为字母
for(let i = 0; i < 3; i++) {
res += chars[sumArr[i]];
}
}
return res;
}
总结与优化方向
通过上述 JavaScript 代码可以看出,Playfair 和 Hill 算法在逻辑上是纯粹的规则应用和数学计算。目前的实现存在以下几点值得优化的地方:
- 耦合度问题:代码偏向面向过程,函数间变量传递较多,适合初学者理解原理,但难以复用。
- 性能考量:过多的嵌套循环在处理长文本时会降低效率,可利用更高级的数据结构或位运算优化。
- 健壮性增强:原始输入未充分处理非字母字符;在 Hill 密码中应添加校验机制,确保密钥矩阵可逆,否则解密将无法完成。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













