古典密碼學雖然現在已經不再使用,但其反映了密碼設計和破譯的基本思想,是學習密碼學的入口。
古典密碼學主要有兩種體制:置換密碼和代換密碼。
置換密碼
根據一定的規則重新排列明文,以便打破原有的結構特性。即改變字符的原始位置,但字符還是那些字符。
- 列置換:比如明文m = “Beijing 2008 Olympic Games”,
密鑰=(1 4 3)(5 6)。
該密鑰表示:一個括號一個循環,f(1) = 4,f(4) = 3 , f(3) = 1,這裏3是括號的末尾,所以f(3)的值就循環到了括號的最前面,即1。(5 6)類似,f(5) = 6 , f(6) = 5。少了2,未表示出來的等於其自身,所以f(2) = 2。其中f(a) = b 表示 a 列和 b 列的數據交換。由於它最大的數是6,則表示分組長度是6,那麼將明文m寫成如下形式:
B e i j i n
g 2 0 0 8 O
l y m p i c
G a m e s *
‘*’號爲填充,經過加密後結果爲:
j e B i n i
0 2 g 0 O 8
p y l m c i
e a G m * s
獲得密文c=“jeBini02g0O8pylmcieaGm*s”
解密時需要變換密鑰,(1 4 3)的逆置換爲(1 3 4),變換規則:對於每一個括號,第一個數字不變,後面反轉。例:(1 2 3 4 5) -》 (1 5 4 3 2)。是不是很簡單。那麼上一個解密密鑰=(1 3 4)(5 6),容易由密文再根據解密密鑰獲得明文。
寫了代碼試了下,僅供參考:
package Classical;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Permutation {
// private static final int MAXCOL = 1024;
public static void main(String[] args) {
// TODO Auto-generated method stub
String c = String.valueOf(columnEncry("Beijing 2008 Olympic Games", "(1 4 3)(5 6)"));
System.out.println("密文:"+c.replace(" ",""));
String m = String.valueOf(columnEncry(c,"(1 3 4)(5 6)"));
System.out.println("明文:"+m.replace(" ",""));
}
/*
*@author 孤舟一葉
*@param in表示要加密的數據,key表示密鑰,形如(1 4 3)(5 6)
*@return 返回密文
*/
public static StringBuilder columnEncry(String in, String key){
int colNum = 0;
Map<Integer,Integer> relation = new HashMap<Integer,Integer>();
String[] keySplited = key.split("[(]");
for(int i = 1 ; i < keySplited.length ; i++){
int len = keySplited[i].length()-1;
keySplited[i] = keySplited[i].substring(0, len);
String[] block = keySplited[i].split(" ");
int[] intBlock = new int[block.length];
for(int j = 0 ; j < block.length ; j++){
intBlock[j] = Integer.valueOf(block[j]);
if(colNum < intBlock[j]){
colNum = intBlock[j];
}
}
int lenOfBlock = block.length;
for(int m =0 ; m < lenOfBlock-1 ; m++){
relation.put(intBlock[m], intBlock[m+1]);
}
relation.put(intBlock[lenOfBlock-1],intBlock[0]);
}
String inNoSpace = in.replace(" ", "");
StringBuilder inNoSpaceAddEle = new StringBuilder(inNoSpace);
int supplement = (colNum - inNoSpace.length()%colNum)%colNum;
for(int i = 0 ; i < supplement ; i++){
inNoSpaceAddEle.append("*");//如果添加“”,長度不會增加
}
StringBuilder out = new StringBuilder();
for(int i = 0 ; i < inNoSpaceAddEle.length() ; i++){
int from = 0;
if(relation.get(i % colNum + 1)!= null){
from = relation.get(i % colNum + 1)-1;
out.append(inNoSpaceAddEle.charAt(i/colNum*colNum+from));
}
else{
out.append(inNoSpaceAddEle.charAt(i));
}
}
return out;
}
}
運行結果:
密文:jeBini02g0O8pylmcieaGm*s
明文:Beijing2008OlympicGames*
- 週期置換
我認爲其原理和列置換完全相同,真的不理解書中爲什麼將其叫做兩個名字。如果非要說不同,那只有列置換將明文豎向排列,週期置換將明文橫向排列了。還是上個例子中的明文,週期置換這麼處理:
(Beijin)(g2008O)(lympic)(Games*)經過密鑰=(1 4 3)(5 6)處理後,密文:
(jeBini)(02g0O8) (pylmci)(eaGm*s)
代換密碼
使用其他字符代替明文字符,密文的字符不是由原來那些字符組成了。
單表代換
- 基於密鑰的單表代換:最簡單查表,找出明文對應的密文。
- 仿射密碼:加密時,明文經過一個線性變換 y = e(x) = ax + b (mod 26),將明文字符變換爲其他字符。解密時,需要先求解 a mod 26 的乘法逆元 a^-1,然後 x = a^-1[e(x)-b] (mod26) 求解明文。相關需要的數學知識包括:歐幾里得求解最大公約數,擴展歐幾里得算法求解乘法逆元。
代碼如下:
package Classical;
public class Substitution {
public static void main(String[] args) {
// TODO Auto-generated method stub
String c = affine("sorcery",11,6);
System.out.println("密文:"+c);
int inverseOfa = mulInverse(26,11);
String m = affine(c,inverseOfa,((-6*inverseOfa)%26+26)%26);
System.out.println("明文:"+m);
}
/*
* @author 孤舟一葉
* @param m:明文;a,b仿射函數參數,其中a和26互素
* @return 返回密文
*/
public static String affine(String m,int a,int b){
if(!isPrimeWith26(a)){
return "仿射函數必須滿足gcd(26,a)=1";
}
m = m.toUpperCase();
StringBuilder out = new StringBuilder();
for(int i = 0; i < m.length(); i++){
int ch = m.charAt(i);
if(ch >= 65 && ch <= 90){
ch = ch - 65;
out.append((char)((ch * a + b)%26+65));
}
else
out.append((char)ch);
}
return String.valueOf(out);
}
private static boolean isPrimeWith26(int a){
if(a == 1)return true;
int m = 26 ,n = a;
while(m%n > 1){
int temp = m%n;
m = n;
n = temp;
}
if(m%n == 1)return true;
return false;
}
/*
* @求解乘法逆元
* @param 需要求解b mod a的乘法逆元
* @return 乘法逆元
*/
private static int mulInverse(int a , int b){
if(0 == a || 0 == b){
return -1;
}
int x1 = 1 , x2 = 0 , x3 = a;
int y1 = 0 , y2 = 1 , y3 = b;
int t1 = 0 , t2 = 0 , t3 = 0;
int n;
for(t3 = x3 % y3 ; t3 != 0 ; t3 = x3 % y3){
n = x3 / y3;
t1 = x1 ; t2 = x2;
x1 = y1; x2 = y2 ; x3 = y3;
y1 = t1 - n * y1 ; y2 = t2 - n * y2 ; y3 = t3;
}
if(1 == y3){
return (y2%a+a)%a;
}
return -1;
}
}
運行結果:
密文:WELCYLK
明文:SORCERY
多表代換
多表代換典型的有三種:Playfair、維吉尼亞密碼、Hill密碼。
這裏給出維吉尼亞的代碼,其他的讀者可自行學習。
package Classical;
public class Vigenere {
public static void main(String[] args) {
// TODO Auto-generated method stub
String c = enCry("cyber greatwall","iscbupt");
System.out.println("密文:"+c);
String m = deCry(c,"iscbupt");
System.out.println("明文:"+m);
}
public static String enCry(String in , String key){
in = in.replace(" ", "");
in = in.toLowerCase();
key = key.toLowerCase();
StringBuilder out = new StringBuilder();
for(int i =0 ; i < in.length() ;i++){
out.append((char)((in.charAt(i)-97+key.charAt(i%key.length())-97)%26+97));
}
return String.valueOf(out);
}
public static String deCry(String in , String key){
in = in.replace(" ", "");
in = in.toLowerCase();
key = key.toLowerCase();
StringBuilder out = new StringBuilder();
for(int i =0 ; i < in.length() ;i++){
out.append((char)(((in.charAt(i)-key.charAt(i%key.length()))%26+26)%26+97));
}
return String.valueOf(out);
}
}
運行結果:
密文:kqdflvkmsvxuae
明文:cybergreatwall
分析方法
統計分析法:某種語言各個字符出現的頻率不同,比如英語中 E 的出行頻率最高。可以根據字母密文中各個字符出現的頻率不同,分析出該密文字符代表的明文字符。對單表代換有用。
重合指數法:利用隨機文本和英文文本的統計概率差別分析密鑰長度。
明文-密文對分析法:由於 Hill密碼 對重合指數法和統計分析法具有抵抗性,可使用明文-密文對分析法破譯。
至此,古典密碼介紹告一段落,接下來我們將進入現代密碼的學習。