zoukankan      html  css  js  c++  java
  • Hill密码(Java)

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

  • 相关阅读:
    codevs 3160 最长公共子串
    bzoj1593 [Usaco2008 Feb]Hotel 旅馆
    bzoj1230 [Usaco2008 Nov]lites 开关灯
    洛谷P1558 色板游戏
    洛谷P2253 好一个一中腰鼓!
    洛谷P2345 奶牛集会
    TopCoder SRM420 Div1 500pt RedIsGood
    洛谷P3144 [USACO16OPEN]关闭农场Closing the Farm_Silver
    洛谷P1455 搭配购买
    洛谷P2398 GCD SUM
  • 原文地址:https://www.cnblogs.com/zhaoke271828/p/13939591.html
Copyright © 2011-2022 走看看