Hill密碼(Java)

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"。

相關文章
相關標籤/搜索