1、古典密码可以分为代替密码和置换密码两种,这里实现了代替密码中的仿射变换和置换密码中的换位变换。
2、仿射变换:
加密过程:e(x) = ax + b (mod m)
解密过程:d(e(x)) = a^(-1)*(e(x) - b) mod m
参数要求:a,m互质;a,b互质;m是集合中元素的个数。(例如当前取1~9和a~z中的所有元素作为集合,m为36)
加密实现:
1 import java.util.Scanner; 2 3 public class Main { 4 public static void main(String []args) { 5 int m = 36, thisNum, index = 0; // m是集合中元素的个数(例如当前取1~9和a~z中的所有元素作为集合,m为36) 6 Scanner s = new Scanner(System.in); 7 // 将输入的字符串转化为字符数组 8 char[] buff = s.nextLine().toCharArray(); 9 // 参数a、b手动输入 10 int a = s.nextInt(); 11 int b = s.nextInt(); 12 // 参数要求:a,m互质;a,b互质 13 while (fun1(m, a) != 1 || fun1(Math.max(a, b), Math.min(a, b)) != 1) { 14 System.out.println("参数不符合要求,请重新输入"); 15 a = s.nextInt(); 16 b = s.nextInt(); 17 } 18 for (char i : buff) { 19 // 由字符转换为数字 20 if (i > '9') thisNum = (int)i - 87; 21 else thisNum = (int)i - 48; 22 // 对该数字加密 23 thisNum = (thisNum*a+b)%m; 24 // 加密后再将数字转换为字符 25 if (thisNum < 10) buff[index++] = (char)(thisNum+48); 26 else buff[index++] = (char)(thisNum+87); 27 } 28 System.out.println(buff); 29 s.close(); 30 } 31 32 // 欧几里得算法求两个数的最大公因数 33 public static int fun1(int a, int b) { 34 return b == 0 ? a : fun1(b, a%b); 35 } 36 }
解密实现:
1 import java.util.Scanner; 2 3 public class Main { 4 public static void main(String []args) { 5 int m = 36, thisNum, index = 0, k; 6 Scanner s = new Scanner(System.in); 7 char[] buff = s.nextLine().toCharArray(); 8 int a = s.nextInt(); 9 int b = s.nextInt(); 10 while (fun1(m, a) != 1 || fun1(Math.max(a, b), Math.min(a, b)) != 1) { 11 System.out.println("参数不符合要求,请重新输入"); 12 a = s.nextInt(); 13 b = s.nextInt(); 14 } 15 // k为a模m的逆元 16 k = fun2(a, m); 17 for (char i : buff) { 18 // 将加密后的字符转换为数字 19 if (i > '9') thisNum = (int)i - 87; 20 else thisNum = (int)i - 48; 21 // 解密过程 D(E(x)) = a^(-1)*(E(x)-b) mod m 22 thisNum = ((thisNum-b)*k)%m; 23 // 如果结果是负数,则转换为正数,原理为 a % b = (a % b + b) % b 24 if(thisNum < 0) thisNum += m; 25 // 最后将解密后的数字转换为字符 26 if (thisNum < 10) buff[index++] = (char)(thisNum+48); 27 else buff[index++] = (char)(thisNum+87); 28 } 29 System.out.println(buff); 30 } 31 32 public static int fun1(int a, int b) { 33 return b == 0 ? a : fun1(b, a%b); 34 } 35 36 // 循环求a模m的逆元 37 public static int fun2(int a, int m) { 38 for (int i = 0; i < m; i++) { 39 if (a*i%m == 1) { 40 a = i; 41 break; 42 } 43 } 44 return a; 45 } 46 }
3、换位密码
加密过程:保持明文的所有字符不变,根据一定的规则重新排列明文。
解密过程:加密过程的逆过程。
注解:加密过程和解密过程都是创建索引的过程,即用数组存储哪个位置放哪个字符,最后再通过索引重新组合成密文或明文。
示例:
明文矩阵:
a s d f g
h j k l m
n b v c
密文矩阵:
d a g s f
k h m j l
v n b c
(计算结果中n和b之间有一个空格,但输出时将空格去掉了)
明文:asdfghjklmnbvc
密钥:31524
加密实现:
1 import java.util.Scanner; 2 3 public class Main { 4 public static void main(String []args) { 5 Scanner s = new Scanner(System.in); 6 char[] mingwen = s.nextLine().toCharArray(); 7 char[] miyao = s.nextLine().toCharArray(); 8 StringBuffer miwen = new StringBuffer(); 9 int[] poi = new int[miyao.length]; 10 int index = 0, thisRow = 0, realPoi; 11 12 // 计算出明文矩阵的列数和行数 13 int col = miyao.length; 14 int row = (mingwen.length / col) + (mingwen.length % col == 0? 0 : 1); // 处理明文矩阵最后一行可能不满的情况 15 16 // 密钥位置格式化(密钥中的所有字符都大于等于'1'所以最后减1方便后续计算) 17 for (int i = 0; i < poi.length; i++) poi[i] = miyao[i] - 48 - 1; 18 19 20 for (int i = 0; i < row * col; i++) { 21 // 计算出当前位置i的真正字符(例如加密后第0位为2,即字符d),如果该字符在明文中存在,则将其添加到密文中 22 if ((realPoi = poi[index++] + thisRow * col) < mingwen.length) miwen.append(mingwen[realPoi]); 23 // 如果当前位置无字符则用空格代替 24 else miwen.append(' '); 25 26 if (index >= col) { 27 index = index % col; 28 thisRow++; 29 } 30 } 31 32 // 密文去空格(解密时密文中的空格需要保留) 33 for (int i = 0; i < miwen.length(); i++) { 34 if (miwen.charAt(i) == ' ') miwen.deleteCharAt(i); 35 } 36 37 // 输出加密后的密文 38 System.out.println(miwen); 39 } 40 }
解密实现:
1 import java.util.Scanner; 2 3 public class Main { 4 public static void main(String []args) { 5 Scanner s = new Scanner(System.in); 6 char[] miwen = s.nextLine().toCharArray(); 7 char[] miyao = s.nextLine().toCharArray(); 8 StringBuffer mingwen = new StringBuffer(); 9 int []poi = new int[miyao.length]; 10 int index = 0, thisRow = 0, realPoi; 11 12 // 获取解密密钥并将密钥的位置格式化(最后减一),即加密的逆过程 13 for (char c : miyao) { 14 poi[(int)c - 48 - 1] = index++; 15 } 16 index = 0; 17 18 // 密文矩阵的列数和行数 19 int col = miyao.length; 20 int row = (miwen.length / col) + (miwen.length % col == 0? 0 : 1); 21 22 for (int i = 0; i < row * col; i++) { 23 // 计算出当前位置i的真正字符并将该字符添加到明文字符串中 24 if ((realPoi = poi[index++] + thisRow * col) < miwen.length) mingwen.append(miwen[realPoi]); 25 26 if (index >= col) { 27 index = index % col; 28 thisRow++; 29 } 30 } 31 32 // 输出解密后的明文 33 System.out.println(mingwen); 34 } 35 }
4、希尔密码(Hill密码)- 属于古典密码中的多表代换密码,运用了基本的矩阵论原理
注解:
1)每个字母当作 26 进制数字:a=0, b=1, c=2... 一串字母当成n维向量,跟一个n×n的矩阵相乘,再将得出的结果模 26。用作加密的矩阵(即密匙)必须是可逆的,否则就不可能译码。只有矩阵的行列式和 26 互质,才是可逆的。
2)明文和密文的长度必须是给定的矩阵的列数的整数倍。(矩阵相乘的前提)
加密过程:将明文转化为n维向量(字符转数字)与n×n的矩阵相乘,再将得出的结果模 26 后转化为字符。
解密过程:将明文转化为n维向量(字符转数字)与n×n的矩阵相乘,再将得出的结果模 26 后转化为字符。
加密实现:
1 import java.util.Scanner; 2 3 public class Main { 4 public static void main(String[] args) { 5 // 三阶可逆矩阵A 6 int[][] A = { { 1, 2, 3 }, { 1, 1, 2 }, { 0, 1, 2 } }; 7 8 Scanner s = new Scanner(System.in); 9 char[] mingwen = s.nextLine().toCharArray(); 10 char[] miwen = new char[mingwen.length]; 11 int row = 3, col = mingwen.length / 3, index = 0; 12 13 while (index < col) { 14 int sum; 15 for (int i = 0; i < row; i++) { 16 sum = 0; 17 for (int j = 0; j < row; j++) { 18 sum += A[i][j] * (int) (mingwen[j+index*row] - 97); 19 } 20 sum %= 26; 21 if (sum < 0) sum += 26; 22 miwen[i + index*row] = (char) (sum + 97); 23 } 24 index++; 25 } 26 27 for (char c : miwen) { 28 System.out.print(c); 29 } 30 } 31 }
解密实现:
1 import java.util.Scanner; 2 3 public class Main { 4 public static void main(String[] args) { 5 // A的逆矩阵 6 int[][] A_1 = {{0, 1, -1}, {2, -2, -1}, {-1, 1, 1}}; 7 8 Scanner s = new Scanner(System.in); 9 char[] miwen = s.nextLine().toCharArray(); 10 char[] mingwen = new char[miwen.length]; 11 int row = 3, col = mingwen.length / 3, index = 0; 12 13 while (index < col) { 14 int sum; 15 for (int i = 0; i < 3; i++) { 16 sum = 0; 17 for (int j = 0; j < 3; j++) { 18 sum += A_1[i][j] * (int)(miwen[j+index*row] - 97); 19 } 20 sum %= 26; 21 if (sum < 0) sum += 26; 22 mingwen[i+index*row] = (char)(sum+97); 23 } 24 index++; 25 } 26 27 for(char c : mingwen) { 28 System.out.print(c); 29 } 30 } 31 }