使用 Playfair 密码对消息进行编码的 Java 程序
加密是将信息转换为不可读格式或密文的任务。通常这样做是为了保护信息的机密性。加密数据的方法和算法有很多。Playfair 密码算法就是这样一个例子。在本文中,我们将了解如何编写 Java 程序使用 Playfair 密码对消息进行编码。
Playfair 密码使用 5X5 网格或矩阵和一组预定义规则。要加密,我们需要一个密钥和要加密的纯文本。
步骤
现在让我们看看使用 Playfair 密码加密消息的步骤 -
1. 生成密钥表
这是 5x5 的字母网格,不允许重复。我们首先将密钥的每个字母放入其中,然后放入所有剩余的字母。字母 i 和 j 被认为占据网格中的同一个单元格,因为 5x5 是 25,而英语中有 26 个字母,因此必须做出一些妥协。
2. 将明文分成对
如果明文的长度为奇数,我们将字母 X 附加到其末尾使其为偶数。然后,我们将整个明文分成对,即每组 2 个字母。例如:如果明文长度为 10,则将有 5 对字母。
3.遍历对
对于明文中的每对字母,我们执行以下操作 -
如果 2 个连续字母相同,则在它们之间插入一个 X。
在步骤 1 中创建的网格中找到 2 个字母的位置。
如果字母在同一行,我们将每个字母替换为正确的字母。如果它是行中的最后一个元素,我们可以转到行的第一个元素。
如果字母在同一列,我们将每个字母替换为其下方的字母。如果它是列中的最后一个元素,我们可以转到列的第一个元素。
如果字母不在同一列或同一行,我们将替换同一行但在另一个字母的列中的每个字母。
4. 上一步获得的字母集是加密文本。
现在让我们看看上面的 Java 实现。
示例
在下面的示例中,我们实现 Playfair 密码来编码消息。
public class PlayfairCipher { private char[][] keyTable; private static final int grid_dimension = 5; private static final char APPEND = 'X'; public PlayfairCipher(String key) { keyTable = generateKeyTable(key); } private char[][] generateKeyTable(String key) { // 使用所有 ' ' 字符初始化键表 char[][] table = new char[grid_dimension][grid_dimension]; for (int i = 0; i < grid_dimension; i++) { for (int j = 0; j < grid_dimension; j++) { table[i][j] = ' '; } } // 使用密钥的字母填充密钥表 int row = 0; int col = 0; boolean[] used = new boolean[26]; for (int i = 0; i < key.length(); i++) { char ch = Character.toUpperCase(key.charAt(i)); if (ch == 'J') { ch = 'I'; } if (!used[ch - 'A']) { table[row][col] = ch; used[ch - 'A'] = true; col++; if (col == grid_dimension) { row++; col = 0; } } } // 用字母表中的剩余字母填充关键表的剩余单元格 for (int i = 0; i < 26; i++) { char ch = (char) ('A' + i); if (ch == 'J') { continue; } if (!used[i]) { table[row][col] = ch; col++; if (col == grid_dimension) { row++; col = 0; } } } return table; } public String encrypt(String plaintext) { plaintext = preprocess(plaintext); StringBuilder ciphertext = new StringBuilder(); for (int i = 0; i < plaintext.length(); i += 2) { char ch1 = plaintext.charAt(i); char ch2 = plaintext.charAt(i + 1); int[] position1 = findPosition(ch1); int[] position2 = findPosition(ch2); if (position1[0] == position2[0]) { // 当字母存在于同一行时 int newCol1 = (position1[1] + 1) % grid_dimension; int newCol2 = (position2[1] + 1) % grid_dimension; ciphertext.append(keyTable[position1[0]][newCol1]); ciphertext.append(keyTable[position2[0]][newCol2]); } else if (position1[1] == position2[1]) { // 当字母存在于同一列时 int newRow1 = (position1[0] + 1) % grid_dimension; int newRow2 = (position2[0] + 1) % grid_dimension; ciphertext.append(keyTable[newRow1][position1[1]]); ciphertext.append(keyTable[newRow2][position2[1]]); } else { // 当字母不在同一列或同一行时 ciphertext.append(keyTable[position1[0]][position2[1]]); ciphertext.append(keyTable[position2[0]][position1[1]]); } } return ciphertext.toString(); } public String decrypt(String ciphertext) { StringBuilder plaintext = new StringBuilder(); for (int i = 0; i < ciphertext.length(); i += 2) { char ch1 = ciphertext.charAt(i); char ch2 = ciphertext.charAt(i + 1); int[] position1 = findPosition(ch1); int[] position2 = findPosition(ch2); if (position1[0] == position2[0]) { int newCol1 = (position1[1] + grid_dimension - 1) % grid_dimension; int newCol2 = (position2[1] + grid_dimension - 1) % grid_dimension; plaintext.append(keyTable[position1[0]][newCol1]); plaintext.append(keyTable[position2[0]][newCol2]); } else if (position1[1] == position2[1]) { int newRow1 = (position1[0] + grid_dimension - 1) % grid_dimension; int newRow2 = (position2[0] + grid_dimension - 1) % grid_dimension; plaintext.append(keyTable[newRow1][position1[1]]); plaintext.append(keyTable[newRow2][position2[1]]); } else { plaintext.append(keyTable[position1[0]][position2[1]]); plaintext.append(keyTable[position2[0]][position1[1]]); } } return postprocess(plaintext.toString()); } private String preprocess(String text) { // 将 J 替换为 I 并根据需要添加填充 StringBuilder sb = new StringBuilder(text.toUpperCase().replaceAll("[^A-Z]", "")); for (int i = 1; i < sb.length(); i += 2) { if (sb.charAt(i) == sb.charAt(i - 1)) { sb.insert(i, APPEND); } } if (sb.length() % 2 != 0) { sb.append(APPEND); } return sb.toString(); } private String postprocess(String text) { // 删除填充并将 X 替换为原始字符 StringBuilder sb = new StringBuilder(text); for (int i = 1; i < sb.length(); i += 2) { if (sb.charAt(i) == APPEND) { sb.deleteCharAt(i); } } return sb.toString().replace(APPEND, ' '); } private int[] findPosition(char ch) { int[] pos = new int[2]; for (int i = 0; i < grid_dimension; i++) { for (int j = 0; j < grid_dimension; j++) { if (keyTable[i][j] == ch) { pos[0] = i; pos[1] = j; return pos; } } } return null; } public static void main(String[] args) { String plaintext = "MOSQUE"; String key = "MONARCHY"; PlayfairCipher cipher = new PlayfairCipher(key); String ciphertext = cipher.encrypt(plaintext); System.out.println("明文: " + plaintext); System.out.println("密文: " + ciphertext); System.out.println("解密文本: " + cipher.decrypt(ciphertext)); } }
输出
上述程序将产生以下输出 -
明文:MOSQUE 密文:ONTSML 解密文本:MOSQUE
结论
Playfair 密码是一种使用 5x5 字母网格的替换密码。它遵循一组基于 if else 的规则,不会产生歧义。与简单的替换密码相比,它提供了更强的安全性。它易于理解和实施。虽然它有自己的弱点,例如易受已知明文攻击和密钥管理问题以及无法加密非字母字符,但它是对称加密算法的一个有趣且具有历史意义的例子。它不再用于建立现实生活中的安全通信,但提供了对现代加密算法中使用的基本概念和技术的深刻理解。