AES加密java底層實現

基本結構

AES爲分組密碼,分組密碼也就是把明文分成一組一組的,每組長度相等,每次加密一組數據,直到加密完整個明文,

下面實現AES-128,也就是密鑰長度爲128位,加密輪數爲10輪。
上面說到,AES的加密公式爲 C = E (K,P),在加密函數E中,會執行一個輪函
數,並且會執行10次這個輪函數,這個輪函數的前9次執行的操作是一樣的,只有第10次有所不同,也就是說,一個明文分組會被加密10輪。
AES的核心就是實現一輪中的所有操作。
AES的處理單位是字節,128位的輸入明文分組P和輸入密鑰K都被分成16字節,
分別記爲P=P0 … P15和K =K0 K1 … K15。
例如明文分組P = abcdefghijklmnop,其中的字符
a對應P0,p對應P15,一般的,明文分組用字節爲單位的正方形矩陣描述,稱爲狀態矩陣。在算法的每一輪中,狀態矩陣的內容不斷髮生變化,最後的結果作爲密文輸出。
該矩陣中字節的排列順序爲從上到下,從左到右依次排列,如下圖所示

在這裏插入圖片描述
以剛纔的例子爲例假設現在的明文分組P爲“abcdefghijklmnop”,則對應上面
的生成狀態矩陣圖如下:
在這裏插入圖片描述
以剛纔的例子爲例假設現在的明文分組P爲“abcdefghijklmnop”,則對應上面
的生成狀態矩陣圖如下:
在這裏插入圖片描述
上圖中0x61爲字符a的十六進制表示,可以看出,明文經過AES加密後,已經面目全非。
類似的,128位密鑰也是用字節爲單位的矩陣表示,矩陣的每一列被成爲1個32位比特字。通過密鑰編排函數該密鑰矩陣被拓展成爲一個44個字節組成的序列
W[0],W[1],…,W[43],該序列的前4個元素W[0],W[1],W[2],W[3],是原始密鑰,用
於加密算法中初始密鑰加(下面還會寫到);後面40個字節分爲10組,每組4個字(128比特)分別用於10輪加密運算中的輪密鑰加,如下圖所示:
在這裏插入圖片描述
在這裏插入圖片描述
上圖中,設K = “abcdefghiklmnop”,則K0 = a,K15 = p,
W[0]=K0 K1 K2 K3=“abcd”。
AES的整體結構如下圖所示,其中W[0,3]是指W[0],W[1],W[2]和W[3]串聯組成的
128位密鑰。加密的第1輪到第9輪的輪函數一樣,
包括4個操作:字節代換,行位移,列混合和輪密鑰加,最後一輪迭代不執行列混合。另外,在第一輪迭代之前,先將明文和原始密鑰進行一次異或加密操作。
第十輪:
在這裏插入圖片描述
第1-9輪:
在這裏插入圖片描述

基本操作詳解

字節替代

在進行字節代替之前,首先將明文和初始祕鑰構成4*4的字節矩陣,並且將兩個矩陣進行異或運算得到進行字節代替之前的正確輸入矩陣
行移位
在這裏插入圖片描述
S盒代替
加密時,輸出的字節S1作爲0x12,則查S盒的第0x01行和0x02列,得到值0xc9,然後替換S1原有的0x12爲0xc9
在這裏插入圖片描述
在這裏插入圖片描述
字節代替逆操作
逆字節代換也就是S盒來變換,逆S盒如下:
在這裏插入圖片描述
字節代換的代碼

行移位

行移位操作:
行移位是一個簡單的左循環移位操作。當密鑰長度爲128比特時,狀態矩陣的第0行左移0個字節,第1行左移1字節,第2行左移2字節,第3行左移3字節,如下圖所示
在這裏插入圖片描述
行移位的逆變換:

行移位的逆變換是將狀態矩陣中的每一行執行相反的只爲操作,例如AES-128中,狀態矩陣的第0行右移0字節,第一行右移1字節,第二行右移2字節,第三行右移3字節

行移位的代碼:

這裏把字節代換和行移位一起演示:

package aES;

public class testSubBytesAndRowShift {

	public static void main(String[] args) {
		String[] liStrings = {
				"4D","34","55","CB",
				"12","3D","A1","5F",
				"4D","34","55","CB",
				"12","3D","A1","5F",
				};	
		System.out.println("要加密的值爲:");
		for(int i=0;i<liStrings.length;i++){
			System.out.print(liStrings[i]+" ");
			if((i+1)%4==0)
				System.out.println();
		}
		//字節代換
		String[] li = SubBytes.subBytes(liStrings);		
		System.out.println("字節代換結果:");
		for(int i=0;i<li.length;i++){
			System.out.print(li[i]+" ");
			if((i+1)%4==0)
				System.out.println();
		}
		//行移位
		String string[] = RowShift.rowShift(li);    
		System.out.println("行移位結果:");
		for(int i=0;i<string.length;i++){			
				System.out.print(string[i]+" ");
				if((i+1)%4==0)
					System.out.println();
		}
	}
}

列混合

列混合變換
是通過矩陣相乘來實現的,經行移位後的狀態矩陣與固定的矩陣相乘,得到混淆後的狀態矩陣,如下圖的公式所示:
在這裏插入圖片描述
列混合的逆變換:
逆向列混合變換可由下圖的矩陣乘法定義:
在這裏插入圖片描述
列混合代碼:
可以驗證,逆變換矩陣同正變換矩陣的乘積恰好爲單位矩陣。
演示列混合:

package aES;

public class testMixCol {

	public static void main(String[] args) {
		String[] strings = { 
				 "87", "6E", "46", "A6" , 
				 "F2", "4C", "E7", "8C" , 
				 "4D", "90", "4A", "D8" ,
				 "97", "EC", "C3", "95" ,
				};
		System.out.println("要加密的值爲:");
		for(int i=0;i<strings.length;i++){
			System.out.print(strings[i]+" ");
			if((i+1)%4==0)
				System.out.println();
		}
		System.out.println("列混淆的結果:");
		String[] strings3=MixColumn.MatrixMultiple(strings);
		for(int i=0;i<strings3.length;i++){		
			System.out.print(strings3[i]+" ");		
			if((i+1)%4==0)
				System.out.println();
		}

	}

}

輪密鑰加密

在首次進行輪密鑰加之前,要對16字節明文進行預處理,
將其組成一個4*4的矩陣,然後與初始密鑰進行異或運算。
之後每次輪密鑰加都是與對應擴展的密鑰進行異或
所以得先有擴展密鑰
那麼先來密鑰擴展:

AES密鑰擴展算法的輸入值是4個字(16字節),
輸出值是一個由44個字組成(176字節)的一維線性數組。

Rcon[i]是輪常量,代表一個字,這個字最右邊三個字節總是0,因此字與Rcon異或,其結果只是與該字最左邊的那個字節相異或。每一輪的輪常量都不相同,其定義Rcon[i] = (RC[i],0,0,0),其中RC[1] = 1,RC[i] = 2•RC[i-1] 乘法是定義在域GF(28)上的。

AES首先將初始密鑰輸入到一個44的狀態矩陣中,如下圖所示
在這裏插入圖片描述
這個4
4矩陣的每一列的4個字節組成一個字,矩陣4列的4個字依次命名爲W[0]、W[1]、W[2]和W[3],
它們構成一個以字爲單位的數組W。
一共16個字128字節:
接着,對W數組擴充40個新列,構成總共44列的擴展密鑰數組。
新列以如下的遞歸方式產生:
1.如果i不是4的倍數,那麼第i列由如下等式確定:
W[i]=W[i-4]⨁W[i-1]
2.如果i是4的倍數,那麼第i列由如下等式確定:
W[i]=W[i-4]⨁T(W[i-1])
其中,T是一個有點複雜的函數。
函數T由3部分組成:字循環、字節代換和輪常量異或,這3部分的作用分別如下。
a.字循環:將1個字中的4個字節循環左移1個字節。即將輸入字[b0, b1, b2, b3]變
換成 [b1,b2,b3,b0]。
b.字節代換:對字循環的結果使用S盒進行字節代換。
c.輪常量異或:將前兩步的結果同輪常量Rcon[j]進行異或,其中j表示輪數。
輪常量Rcon[j]是一個字,其值見下表。
在這裏插入圖片描述
下面舉個例子:
設初始的128位密鑰爲:
3C A1 0B 21 57 F0 19 16 90 2E 13 80 AC C1 07 BD
那麼4個初始值爲:
W[0] = 3C A1 0B 21
W[1] = 57 F0 19 16
W[2] = 90 2E 13 80
W[3] = AC C1 07 BD
下面求擴展的第1輪的子密鑰(W[4],W[5],W[6],W[7])。
由於4是4的倍數,所以:
W[4] = W[0] ⨁ T(W[3])
T(W[3])的計算步驟如下:

  1. 循環地將W[3]的元素移位:AC C1 07 BD變成C1 07 BD AC;
  2. 將 C1 07 BD AC 作爲S盒的輸入,輸出爲78 C5 7A 91;
  3. 將78 C5 7A 91與第一輪輪常量Rcon[1]進行異或運算,將得到79 C5 7A 91,
    因此,T(W[3])=79 C5 7A 91,故
    W[4] = 3C A1 0B 21 ⨁ 79 C5 7A 91 = 45 64 71 B0
    其餘的3個子密鑰段的計算如下:
    W[5] = W[1] ⨁ W[4]
    = 57 F0 19 16 ⨁ 45 64 71 B0 = 12 94 68 A6
    W[6] = W[2] ⨁ W[5]
    =90 2E 13 80 ⨁ 12 94 68 A6 = 82 BA 7B 26
    W[7] = W[3] ⨁ W[6]
    = AC C1 07 BD ⨁ 82 BA 7B 26 = 2E 7B 7C 9B
    所以,第一輪的密鑰爲
45 64 71 B0
12 94 68 A6
82 BA 7B 26
2E 7B 7C 9B。

密鑰擴展代碼以及測試

擴展了密鑰之後再來談輪密鑰加密
以後每次輪密鑰加都是與對應擴展的密鑰進行異或
輪密鑰加密 代碼和測試

任意長度字符串加密過程&任意文件加密

字符串的難點在於填充:
常見幾種填充:

這裏採用 PKCS5Padding
進行填充(默認):
如果明文塊少於16個字節(128bit),
在明文塊末尾補足相應數量的字符,差幾個字節就補幾。比如明文:
{1,2,3,4,5,a,b,c,d,e},缺少6個字節,
則補全爲{1,2,3,4,5,a,b,c,d,e,6,6,6,6,6,6}
eg:
這裏我定義的
自立立人興安安
有七個字,差一個字也就是16bit也就是兩個字節。
下面的代碼會一直自動補2:直到補全128位爲止。
在這裏插入圖片描述

package aES;

//加密任意長度字符串
// 提示:a佔兩個bit,長度爲2,a佔四個bit,長度爲四



	
public class andlong {

	
 
	public   String str;
	private  StringToChars sun;
	public static	 StringBuilder hex;

	public static void main(String[] args) {
		
		String str="自李立國興安安";
		StringToChars sun =new StringToChars();
		 hex=sun.strTOhex(str);
		 int b= (128-(hex.length()*4)%128)/8;
		 while((hex.length()*4)%128!=0)
		 {
			 
		   hex.append(b);
		 }
		 System.out.println(hex);
		
	}

}
 

演示 :m.txt
讀取之後補全,再做等分。
0d0a 6161 6161 6161
6177 7777 7777 7777

但是我等分之後再做加密 會報錯。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章