Hill密码原理
首先随机生成或选取一个密钥矩阵(该矩阵必须是可逆的),过程如下图所示
在加密过程中,先将明文分为三个字母一组,不足的用“X”代替,然后将其转化成数字,如0==‘A’,得到每个字母所对应的数字,再与密钥矩阵相乘,得到的数字转成字母,如’B’==1,进行拼接,即得到密文字符串,最后进行输出。
在解密过程中,先求出密钥的逆矩阵,本文直接给出逆矩阵,需要注意的是逆矩阵的元素不可以为负数,若存在负数,则将所有元素+26的若干倍,再mod26,得到全为正整数的逆矩阵。将得到的密文字母转成数字(三个一组),与逆矩阵相乘,将所得的数字转成字母,即得到明文字符串,最后进行输出。
代码如下:
package gdut.com.information;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Hill {
private static int[][] key = {{1,2,3},{1,1,2},{0,1,2}}; //密钥矩阵
private static int[][] fkey = {{0,1,25},{2,24,25},{25,1,1}}; //密钥逆矩阵
private static Map<Character,Integer> map1 = new HashMap<>();
private static Map<Integer,Character> map2 = new HashMap<>();
private static Scanner cin = new Scanner(System.in);
public static void main(String[] args) {
System.out.println("明文先按三个字母一组分组,不足三个是用X代替");
System.out.println();
System.out.println("==========加密过程==========");
System.out.println("密钥矩阵如下:");
showMartrix(key);
matches();
System.out.println("请输入明文:" );
String s1 = cin.nextLine();
String format1 = format(s1);
encode(format1);
System.out.println("==========解密过程==========");
System.out.println("密钥逆矩阵如下:");
showMartrix(fkey);
int len = format1.length();
decode(len);
}
/**
* 将字母与数字进行一一对应
*/
private static void matches() {
for (int i = 0; i < 26; i++) {
map1.put((char) (i+65), i);
}
for (int i = 0; i < 26; i++) {
map2.put(i,(char) (i+65));
}
}
/**
* 展示矩阵
*
*/
private static void showMartrix(int[][] key2) {
for (int i = 0; i < key2.length; i++) {
for (int j = 0; j < key2[0].length; j++) {
System.out.print(key2[i][j]+" ");
}
System.out.println();
}
}
/**
* 将小写字母转换成大写字母
*/
private static String format(String str) {
String regEx="[^(A-Za-z)]"; //正则表达式
Pattern p = Pattern.compile(regEx);
Matcher getStr = p.matcher(str);
str = getStr.replaceAll("").trim();
str = str.toUpperCase();
return str;
}
/**
* 加密
*/
private static void encode(String str) {
int len = str.length()/3;
if(str.length()%3!=0) {
len++;
}
String[] sb = new String[len];
for (int i = 0; i < sb.length; i++) {
sb[i]="";
}
int num = 0;
int length = str.length();
//将明文按照按照三个字母进行分组
for (int i = 0; i < sb.length; i++) {
for (int j = i*3; j < length; j++) { //第i轮,应从下标i*3开始
char c = str.charAt(j);
sb[i] += c;
num++;
if(num%3 == 0) {
num=0;
break;
}
}
//当不足三个字母时补X
while(num!=0 && num<3) {
sb[i] += "X";
num++;
}
}
System.out.print("密文为:");
for (int i = 0; i < sb.length; i++) {
int[] a = new int[3];
for (int j = 0; j < sb[i].length(); j++) {
a[j] = map1.get(sb[i].charAt(j));
}
int[] b = new int[3];
for (int j = 0; j < a.length; j++) {
b[j] = (key[j][0]*a[0]+key[j][1]*a[1]+key[j][2]*a[2])%26;
System.out.print(map2.get(b[j]));
}
}
System.out.println();
}
/**
* 解密
*/
private static void decode(int length) {
StringBuffer sb = new StringBuffer();
System.out.println("请输入密文:");
String s2 = cin.nextLine();
String format2 = format(s2);
int len = format2.length();
if(len%3 != 0) {
System.out.println("密文数量必须为3的倍数,请重新输入");
decode(length);
}
int i = 0;
System.out.print("明文为:");
while (i < len) {
int[] a = new int[3];
for (int j = 0; j < a.length; j++) {
a[j] = map1.get(format2.charAt(i));
i++;
}
int[] b = new int[3];
for (int j = 0; j < a.length; j++) {
b[j] = (fkey[j][0]*a[0]+fkey[j][1]*a[1]+fkey[j][2]*a[2])%26;
sb.append(map2.get(b[j]));
}
}
//把补充的字母"X"去掉
String substring = sb.toString().substring(0, length);
System.out.println(substring);
}
}
ps: 如有不足,期待您的指点,谢谢!