Hill密碼是一種傳統的密碼體系。加密原理:選擇一個二階可逆整數矩陣A稱爲密碼的加密矩陣,也就是這個加密體系的密鑰。加密過程:java
明文字母依次逐對分組,例如加密矩陣爲二階矩陣,明文就兩個字母一組,若是最後一組不足(明文長度爲奇數),就補充任意字母湊個雙,構成二維向量組a。計算矩陣A乘以向量組a,獲得新的二維列向量b,反查字母表獲得兩個字母即爲密文字母。算法
也就是說,加密過程爲:明文-->映射爲數字矩陣-->通過矩陣加密-->映射爲字符串(密文)this
解密過程也是一樣的過程,只不過中間使用矩陣解密,Hill密碼是一種傳統的密碼體系。加密
根據這個過程,每一階段功能代碼以下:spa
首先建立一個類,HillCrypto,code
成員變量有加密密鑰矩陣和解密密鑰矩陣,字母轉數值映射和數值轉字母映射orm
初始化階段,實例化以上成員變量,其中映射表較大,所以寫在了本地文件中便於重用,建立映射時須要讀取本地文件。blog
文件內容以下:ip
代碼以下:ci
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"。