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 }