Hill密碼是一種傳統的密碼體系。加密原理:選擇一個二階可逆整數矩陣A稱為密碼的加密矩陣,也就是這個加密體系的金鑰。加密過程:
明文字母依次逐對分組,例如加密矩陣為二階矩陣,明文就兩個字母一組,如果最後一組不足(明文長度為奇數),就補充任意字母湊個雙,構成二維向量組a。計算矩陣A乘以向量組a,得到新的二維列向量b,反查字母表得到兩個字母即為密文字母。
也就是說,加密過程為:明文-->對映為數字矩陣-->經過矩陣加密-->對映為字串(密文)
解密過程也是同樣的過程,只不過中間使用矩陣解密,Hill密碼是一種傳統的密碼體系。
根據這個過程,每一階段功能程式碼如下:
首先建立一個類,HillCrypto,
成員變數有加密金鑰矩陣和解密金鑰矩陣,字母轉數值對映和數值轉字母對映
初始化階段,例項化以上成員變數,其中對映表較大,因此寫在了本地檔案中便於重用,建立對映時需要讀取本地檔案。
檔案內容如下:
程式碼如下:
import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; public class HillCrypto { private Map<Character, Integer> table; private Map<Integer, Character> getPlainMap; private int[][] encryption = {{1, 1},{0, 3}}; private int[][] decryption; public HillCrypto(String tableFilePath) { // TODO Auto-generated constructor stub int mrow = encryption.length;int mcolumn = encryption[0].length; this.decryption = new int[mrow][mcolumn]; // 二階矩陣的逆矩陣,如果是更高階的,使用其他辦法,比如通過餘子式除以行列式得到逆矩陣,行列式的求法見鄙人的其他部落格。 decryption[0][0] = (encryption[1][1] * 27 / (encryption[0][0]*encryption[1][1] - encryption[0][1]*encryption[1][0])) % 26; decryption[0][1] = - (encryption[0][1] * 27 / (encryption[0][0]*encryption[1][1] - encryption[0][1]*encryption[1][0])) % 26; decryption[1][0] = - (encryption[1][0] * 27 / (encryption[0][0]*encryption[1][1] - encryption[0][1]*encryption[1][0])) % 26; decryption[1][1] = (encryption[0][0] * 27 / (encryption[0][0]*encryption[1][1] - encryption[0][1]*encryption[1][0])) % 26; // 該演算法的所有矩陣在求出之後都需要取餘數 for (int i = 0; i < decryption.length; i++) { for (int j = 0; j < decryption[0].length; j++) { if (decryption[i][j] < 0) { decryption[i][j] += 26; } } } this.print(decryption); this.table = this.readFile(tableFilePath); this.getPlainMap = this.mapReverse(table); } private void print(int[][] matrix) { for (int i = 0; i < matrix.length; i++) { for (int j = 0; j < matrix[0].length; j++) { System.out.print(matrix[i][j]+", "); } System.out.println(); } } // map的鍵值互換 private Map<Integer, Character> mapReverse(Map<Character, Integer> table){ Iterator<Character> it = table.keySet().iterator(); Map<Integer, Character> result = new HashMap<Integer, Character>(); while (it.hasNext()) { Character character = (Character) it.next(); result.put(table.get(character), character); } return result; } /** * 從本地讀取一個檔案以建立字母值表,例如A->0, B->1,...,Z->25 * @param tableFilePath * @return */ private Map<Character, Integer> readFile(String tableFilePath) { File file = new File(tableFilePath); FileReader fr; Map<Character, Integer> map = new HashMap<Character, Integer>(); try { fr = new FileReader(file); BufferedReader br = new BufferedReader(fr); String line = ""; String[] kv = null; while ((line = br.readLine())!= null) { kv = line.split(","); // System.out.println("讀取鍵值對:<"+kv[0]+", "+kv[1]+">"); map.put(kv[0].charAt(0), Integer.parseInt(kv[1])); } br.close(); fr.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return map; } }
密文是字串,需要根據所讀取的對映表,兩兩一組轉換為數字矩陣:
/** * 由密文根據對映錶轉換為二維向量 * @return */ private int[][] getVectorFromString(String text){ int textLength = text.length(); // System.out.println("密文長度為:" + textLength); int row = textLength; if (row % 2 != 0) { row = (row + 1) / 2; int column = this.encryption.length; int[][] vector = new int[row][column]; for (int i = 0; i < row-1; i++) { for (int j = 0; j < column; j++) { vector[i][j] = this.table.get(text.charAt(i*column + j)); } } vector[row-1][column-2] = this.table.get(text.charAt((row-1)*column + column-2)); // this.print(vector); vector[row-1][column-1] = this.table.get('A'); return this.transpose(vector); }else { row = row / 2; int column = this.encryption.length; int[][] vector = new int[row][column]; for (int i = 0; i < row; i++) { for (int j = 0; j < column; j++) { vector[i][j] = this.table.get(text.charAt(i*column + j)); } } return this.transpose(vector); } } // 求矩陣轉置 public int[][] transpose(int[][] matrix){ int row = matrix.length; int column = matrix[0].length; int[][] newmatrix = new int[column][row]; for (int i = 0; i < row; i++) { for (int j = 0; j < column; j++) { newmatrix[j][i] = matrix[i][j]; } } return newmatrix; }
加密或者解密都需要用到矩陣乘法
// 求矩陣乘以向量的結果 public int[][] transform(int[][] matrix, int[][] vector) { int mrow = matrix.length; int mcolumn = matrix[0].length; int column = vector[0].length; int[][] result = new int[mcolumn][column]; for (int k = 0; k < column; k++) { for (int i = 0; i < mcolumn; i++) { for (int j = 0; j < mrow; j++) { result[i][k] += matrix[i][j] * vector[j][k]; // System.out.printf("result[%d][%d] = %d\n", i, k, result[i][k]); } } } for (int i = 0; i < mcolumn; i++) { for (int j = 0; j < column; j++) { result[i][j] %= 26; } } return result; }
從數字矩陣中反查對映表得到字串
public char[] getPlain(int[][] table) { int row = table.length; int column = table[0].length; char[] plaintext = new char[row*column]; for (int i = 0; i < column; i++) { for (int j = 0; j < row; j++) { plaintext[i*row+j] = this.getPlainMap.get(table[j][i]); } } return plaintext; }
然後用一個例項演示加密然後解密的過程:
public static void main(String[] args) { String tableFilePath = "src/zhaoke/table.csv"; // 加密金鑰 String originPlain = "JAVAISTHEBESTLANGUAGEINTHEWORLD"; System.out.println("明文:"+originPlain); HillCrypto hc = new HillCrypto(tableFilePath); // 加密過程 // 首先字串對映為數值 int[][] plainNum = hc.getVectorFromString(originPlain); // hc.print(plainNum); // 然後用加密矩陣進行加密 int[][] encryptedPlain = hc.transform(hc.encryption, plainNum); // 然後對映為字串就是密文了 String cipherPlain = new String(hc.getPlain(encryptedPlain)); System.out.println("加密後的密文"+cipherPlain); // 解密過程 // 首先對映為數值 int[][] cipherNum = hc.getVectorFromString(cipherPlain); // hc.print(cipherNum); // 使用解密矩陣進行解密 int[][] newtable = hc.transform(hc.decryption, cipherNum); // 然後對映為明文 String plainText = new String(hc.getPlain(newtable)); System.out.println("解密所得明文為: "+plainText); }
執行結果:
明文:JAVAISTHEBESTLANGUAGEINTHEWORLD
加密後的密文KCWCBEBXGFXEFJOPBKHUNAHHMOLSDJEC
解密所得明文為: JAVAISTHEBESTLANGUAGEINTHEWORLDA
可以看到結果正確,說明演算法是有效的,至於為什麼最終會多出一個A,那是因為明文長度是奇數,為了能對映為二維向量需要湊整,查表階段加上了一個隨機字母湊雙因此多了一個A,但是這不影響我們能看出明文就是"Java is the best language in the world"。