單表代替密碼的缺點是通過分析每個字母出現的頻率可以破解出密碼, 那么如果我們把多個字母當成一個單元整體替換,那么這種概率就會小很多。
比如最著名的Playfair 密碼:
一、首先確定一個由加密詞所構成的一個5*5的加密矩陣, 比如我們使用monarchy,
| M | O | N | A | R |
| C | H | Y | B | D |
| E | F | G | I/J | K |
| L | P | Q | S | T |
| U | V | W | X | Z |
這個矩陣的規則是:
1. 先將加密詞中的不重復字母填充到矩陣的開頭;
2. 按照字母順序將剩余字母填充進矩陣,I和J 當成同一個字母處理。
二、對明文進行加密, 比如加密單詞balloon,
1, 將明文拆成字母對,如果該字母對是相同的字母,則在他們之間填充一個固定的字母,這里我們使用x, 那么上面的單詞就變成了如下的字母對:
ba-lx-lo-on
2,對上一步奏的字母對進行加密:
a), 落在矩陣同一行的明文字母對由其右邊的字母代替,每行最右邊的那個字母就使用該列最左邊的字母代替, 比如AR-> RM
b),落在矩陣同一列的明文字母對由其下面的字母代替,沒列最后一個字母就使用該列第一個字母字母代替,比如MU->CM
c),其他的每組明文字母對的中的字母按照如下方式代替,該字母所在行為密文所在行,另一個字母所在列作為密文所在列, 比如HS->BP,EA->IM
寫了個大概的Java實現,可能有點問題, 用語表達意思:
1 public class Playfair { 2 public static final String KEY = "MONARCHY"; 3 public static char[][] getKeyMatrix(String key){ 4 key = key.toUpperCase(Locale.ENGLISH).replace('J','I'); 5 List<Character> chars = new ArrayList<Character>(); 6 for (char c : key.toCharArray()){ 7 if(!chars.contains(c)){ 8 chars.add(c); 9 } 10 } 11 char flag; 12 for(int i = 65 ; i < 91 ; i++){ 13 flag = (char)i; 14 if(flag == 'J'){ 15 flag = 'I'; 16 } 17 if (!chars.contains(flag)){ 18 chars.add(flag); 19 } 20 } 21 char[][] keyMatrix = new char[5][5]; 22 for(int i = 0 ; i < 5 ; i ++){ 23 for(int j = 0 ; j < 5 ; j++){ 24 keyMatrix[i][j] = chars.get(i*5 + j); 25 } 26 } 27 return keyMatrix; 28 } 29 30 public static short[] getLocation(char c, char[][] keyMatrix){ 31 c = c == 'J' ? 'I' : c; 32 for (short i = 0 ; i < 5 ;i++){ 33 for(short j = 0 ; j < 5 ; j++){ 34 if( c == keyMatrix[i][j]){ 35 return new short[]{i,j}; 36 } 37 } 38 } 39 return null; 40 } 41 42 public static char[] mapKeyMatrix(char[] inputs, char[][] keyMatrix){ 43 short[] location1 = getLocation(inputs[0],keyMatrix); 44 short[] location2 = getLocation(inputs[1],keyMatrix); 45 if (location1[0] == location2[0]){ 46 if(location1[1] == 4){ 47 return new char[]{keyMatrix[location1[0]][0],keyMatrix[location1[0]][location2[1]+1]}; 48 } 49 if (location2[1] == 4){ 50 return new char[]{keyMatrix[location1[0]][location1[1]+1],keyMatrix[location1[0]][0]}; 51 } 52 return new char[]{keyMatrix[location1[0]][location1[1]+1],keyMatrix[location1[0]][location2[1]+1]}; 53 } 54 if(location1[1] == location2[1]){ 55 if(location1[0] == 4){ 56 return new char[]{keyMatrix[0][location1[1]],keyMatrix[location1[1]+1][location2[1]]}; 57 } 58 if (location2[0] == 4){ 59 return new char[]{keyMatrix[location1[0]+1][location1[1]],keyMatrix[0][location2[1]]}; 60 } 61 return new char[]{keyMatrix[location1[0]+1][location1[1]],keyMatrix[location1[0]+1][location2[1]]}; 62 } 63 return new char[]{keyMatrix[location1[0]][location2[1]], keyMatrix[location2[0]][location1[1]]}; 64 } 65 public static String encode(String str,String key){ 66 if (str == null || key == null){ 67 return null; 68 } 69 char[][] keyMatrix = Playfair.getKeyMatrix(key); 70 char[] chars = str.toUpperCase(Locale.ENGLISH).toCharArray(); 71 StringBuffer encode = new StringBuffer(); 72 char[] maps = new char[2]; 73 for (int i = 0 ; i < chars.length ; i++){ 74 if(chars[i] < 65 || chars[i] > 90){ 75 encode.append(chars[i]); 76 continue; 77 } 78 if((i+1) < chars.length && (chars[i+1] < 65 || chars[i+1] > 90)){ 79 maps = new char[]{chars[i],'X'}; 80 encode.append(mapKeyMatrix(maps,keyMatrix)); 81 i++; 82 continue; 83 } 84 if( i == chars.length - 1){ 85 maps = new char[]{chars[i],'X'}; 86 encode.append(mapKeyMatrix(maps,keyMatrix)); 87 break; 88 } 89 if(chars[i] == chars[i+1]){ 90 encode.append(mapKeyMatrix(new char[]{chars[i],'X'},keyMatrix)); 91 continue; 92 }else{ 93 encode.append(mapKeyMatrix(new char[]{chars[i],chars[i+1]},keyMatrix)); 94 } 95 i++; 96 } 97 return encode.toString(); 98 } 99 public static void main(String[] args) { 100 System.out.println(encode("I am Jerry, I come from Jian Yang, I don't know where is the future", KEY)); 101 } 102 }
這種密碼算法可以掩蓋一部分字母出現頻率導致密碼被破譯的問題, 但是這只是並沒有解決本質問題, 還是可以通過分析字母出現的頻率破解密碼。
-wellmax
