AES演算法測試用例程式Java實現(金鑰長度128位元)
AES演算法測試用例程式Java實現(金鑰長度128位元)
——2018.10.22
2.2.5 祕鑰擴充套件(KeyExpansion)... 7
2.3.3逆列混合(InvMixColumns)... 10
1引言
1.1任務概要
- 實現AES加密和解密基本功能;
- 實現AES加密測試用例功能,具體如下:
- 隨機產生n組128位元明文,加密產生n組128位元密文,金鑰不變;b)操作簡單,介面美觀。
- 輸入和輸出要求:
- 用函式實現AES加密和解密
- 輸入和輸出的128位元明文和密文采用十六進位制
- 要求至少有一組來自標準fips-197的測試。
- 隨機產生的明文和加密後的明文(即密文)儲存為txt檔案,儲存格式為每行記錄一組明文和對應密文。如txt檔案格式
明文:0xbce3 …… 密文:0x……
明文:0x…… 密文:0x……
加密金鑰為:0x……
- 解密驗證,從(4)中txt檔案,獲取任意一組密文和金鑰,解密輸出明文,並輸出顯示在螢幕上。
1.2執行環境和開發環境
- 執行環境:JVM;
- 開發環境:Windows10;JDK1.8;eclipse;
1.3密碼演算法原理簡單介紹
美國國家標準技術研究所在2001年釋出了高階加密標準(AES)。AES是一個對稱分組密碼演算法,旨在取代DES成為廣泛使用的標準。根據使用的密碼長度,AES最常見的有3種方案,用以適應不同的場景要求,分別是AES-128、AES-192和AES-256。本課題設計主要對AES-128進行介紹,另外兩種的思路基本一樣,只是輪數會適當增加。
如圖1所示,AES加密過程涉及到4種操作:位元組替(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和輪金鑰(AddRoundKey)。解密過程分別為對應的逆操作。由於每一步操作都是可逆的,按照相反的順序進行解密即可恢復明文。加解密中每輪的金鑰分別由初始金鑰擴充套件得到。演算法中16位元組的明文、密文和輪金鑰都以一個4x4的矩陣表示。
2 程式各模組詳細設計
2.1核心模組主要實現演算法的流程
AES程式流程如圖2所示
2.2 AES加密的演算法說明和實現方式
2.2.1位元組代替(Subbyte)
AES的位元組代換其實就是一個簡單的查表操作。AES定義了一個S盒和一個逆S盒。 狀態矩陣中的元素按照下面的方式對映為一個新的位元組:把該位元組的高4位作為行值,低4位作為列值,取出S盒或者逆S盒中對應的行的元素作為輸出。
// 位元組代替
public byte[][] subbyte(byte[][] sub) {
byte row, col;
byte[][] temp = new byte[4][4];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
col = (byte) (sub[i][j] & 0xf);
row = (byte) ((sub[i][j] >> 4) & 0xf);
temp[i][j] = sbox[row][col];
}
}
return temp;
}
2.2.2行移位(ShiftRows)
行移位是一個簡單的左迴圈移位操作。當金鑰長度為128位元時,狀態矩陣的第0行左移0位元組,第1行左移1位元組,第2行左移2位元組,第3行左移3位元組,如圖3所示:■圖3 行移位示意圖
// 行移位
public byte[][] shift(byte[][] sub) {
byte temp;
temp = sub[1][0];
sub[1][0] = sub[1][1];
sub[1][1] = sub[1][2];
sub[1][2] = sub[1][3];
sub[1][3] = temp;
temp = sub[2][0];
sub[2][0] = sub[2][2];
sub[2][2] = temp;
temp = sub[2][1];
sub[2][1] = sub[2][3];
sub[2][3] = temp;
temp = sub[3][0];
sub[3][0] = sub[3][3];
sub[3][3] = sub[3][2];
sub[3][2] = sub[3][1];
sub[3][1] = temp;
return sub;
}
2.2.3列混合(MixColumns)
列混合變換是通過矩陣相乘來實現的,經行移位後的狀態矩陣與固定的矩陣相乘,得到混淆後的狀態矩陣,如下圖的公式所示:
■圖4 AES列混合示意圖
// 列混合
public byte[][] mix(byte[][] sub) {
byte count = 0;
byte[][] temp = new byte[4][4];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
while (count < 4) {
temp[i][j] = (byte) (temp[i][j] ^ mu(mut[i][count], sub[count][j]));
count++;
}
count = 0;
}
}
return temp;
}
// 列混合中所用函式
public byte mu(byte b, byte c) {
byte ret = 0, count1 = 0, count2 = 0;
byte[] barray = new byte[8];
byte[] carray = new byte[8];
byte[] pro = new byte[15];
if (b == 1 | c == 0)
return c;
if (c == 1)
return b;
for (int i = 0; i < 8 && b != 0; i++) {
barray[i] = (byte) (b & 1);
b = (byte) (b >> 1);
count1++;
}
for (int i = 0; i < 8 && c != 0; i++) {
carray[i] = (byte) (c & 1);
c = (byte) (c >> 1);
count2++;
}
for (int i = 0; i < count1; i++)
for (int j = 0; j < count2; j++) {
if (barray[i] > 0 & carray[j] > 0)
pro[i + j] = (byte) ((pro[i + j] + 1) % 2);
}
for (int m = 0; m < count1 + count2; m++) {
if (pro[m] > 0)
ret = (byte) ((by[m]) ^ (ret));
}
return ret;
}
2.2.4 輪祕鑰加(AddRoundKey)
輪金鑰加是將128位輪金鑰Ki同狀態矩陣中的資料進行逐位異或操作,如下圖所示。其中,金鑰Ki中每個字W[4i],W[4i+1],W[4i+2],W[4i+3]為32位位元字,包含4個位元組。輪金鑰加過程可以看成是字逐位異或的結果,也可以看成位元組級別或者位級別的操作。
public byte[][] add(byte sub[][], byte[][] roundkey) {
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++) {
sub[i][j] = (byte) (sub[i][j] ^ roundkey[i][j]);
}
return sub;
}
2.2.5 祕鑰擴充套件(KeyExpansion)
AES首先將初始金鑰輸入到一個4*4的狀態矩陣中,這個4*4矩陣的每一列的4個位元組組成一個字,矩陣4列的4個字依次命名為W[0]、W[1]、W[2]和W[3],它們構成一個以字為單位的陣列W.接著,對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]是一個字,其值見下表。
■圖5 R值示意圖
// 金鑰擴充套件
public byte[][][] key(byte[][] okey) {
byte[][][] retarray = new byte[11][4][4];// 對W陣列擴充40個新列,構成總共44列的擴充套件金鑰陣列
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++) {
retarray[0][j][i] = okey[i][j];
}
for (int i = 1; i < 11; i++) {
retarray[i] = tkey(retarray[i - 1], r[i]);
}
return retarray;
}
// 金鑰擴充套件中所用函式
public byte[][] tkey(byte[][] okey, int ri) {
byte[][] temp = new byte[4][4];
byte col, row;
col = (byte) (okey[1][3] & 0xf);
row = (byte) ((okey[1][3] >> 4) & 0xf);
temp[0][0] = (byte) (ri ^ sbox[row][col] ^ okey[0][0]);
col = (byte) (okey[2][3] & 0xf);
row = (byte) ((okey[2][3] >> 4) & 0xf);
temp[1][0] = (byte) (sbox[row][col] ^ okey[1][0]);
col = (byte) (okey[3][3] & 0xf);
row = (byte) ((okey[3][3] >> 4) & 0xf);
temp[2][0] = (byte) (sbox[row][col] ^ okey[2][0]);
col = (byte) (okey[0][3] & 0xf);
row = (byte) ((okey[0][3] >> 4) & 0xf);
temp[3][0] = (byte) (sbox[row][col] ^ okey[3][0]);
for (int i = 1; i < 4; i++) {
temp[0][i] = (byte) (temp[0][i - 1] ^ okey[0][i]);
temp[1][i] = (byte) (temp[1][i - 1] ^ okey[1][i]);
temp[2][i] = (byte) (temp[2][i - 1] ^ okey[2][i]);
temp[3][i] = (byte) (temp[3][i - 1] ^ okey[3][i]);
}
return temp;
}
2.3 AES解密的演算法說明和實現方式
2.2.1逆行移位(InvShiftRows)
AES的逆行移位與行移位相反,是一個簡單的右迴圈移位操作。當金鑰長度為128位元時,狀態矩陣的第0行右移0位元組,第1行右移1位元組,第2行右移2位元組,第3行右移3位元組。
//逆行移位
public byte[][] shift(byte[][] sub, int mode) {
byte temp;
temp = sub[3][0];
sub[3][0] = sub[3][1];
sub[3][1] = sub[3][2];
sub[3][2] = sub[3][3];
sub[3][3] = temp;
temp = sub[2][0];
sub[2][0] = sub[2][2];
sub[2][2] = temp;
temp = sub[2][1];
sub[2][1] = sub[2][3];
sub[2][3] = temp;
temp = sub[1][0];
sub[1][0] = sub[1][3];
sub[1][3] = sub[1][2];
sub[1][2] = sub[1][1];
sub[1][1] = temp;
return sub;
}
2.2.2逆位元組代替(InvSubbyte)
AES的逆位元組代替與位元組位元組代替類似,逆位元組代替基於逆s盒。
//逆位元組代替
public byte[][] subbyte(byte[][] sub, int r) {
byte row, col;
byte[][] temp = new byte[4][4];
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++) {
col = (byte) (sub[i][j] & 0xf);
row = (byte) ((sub[i][j] >> 4) & 0xf);
temp[i][j] = rsbox[row][col];
}
return temp;
}
2.2.3逆列混合(InvMixColumns)
逆列混合變換也是通過矩陣相乘來實現的,與固定的矩陣相乘,得到逆混淆後的狀態矩陣。
//逆列混合
public byte[][] mix(byte[][] sub, int mode) {
byte count = 0;
byte[][] temp = new byte[4][4];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
while (count < 4) {
temp[i][j] = (byte) (temp[i][j] ^ mu(rmut[i][count], sub[count][j]));
count++;
}
count = 0;
}
}
return temp;
}
3 程式測試
3.1手動輸入明文和祕鑰測試過程
3.2隨機生成明文和祕鑰測試過程
3.3手動輸入密文和祕鑰測試過程
4 密碼演算法課程設計實踐總結
- 通過本次密碼演算法課程設計對AES演算法的理解更加深刻,並且掌握了各演算法的程式碼實現,將所學的理論知識運用到實際的程式中,做到了學以致用。
- 本次課程設計的選題內容是AES加密演算法的實現。在仔細看懂AES加密原理的基礎上,真正明白了加密的精髓雖然是簡單的擴散(diffusion)和混淆(confusion),但是卻是很精妙的所在。加密過程依次經過讀進明文、讀進金鑰、產生輪子金鑰、輪金鑰加、輪變換(包括位元組替換、列變換、列混淆、輪金鑰加)及最後輪變換得到密文,而解密過程則是加密過程的逆過程,依次經過讀取密文、讀取金鑰、產生輪子金鑰、輪金鑰加、輪變換(包括逆列變換、逆位元組替換、獲得輪金鑰、輪金鑰加、逆列混淆)和最後一輪變換。
- 在編寫程式碼的過程中同時也遇到了很多的問題,深刻地意識到自身的不足和在專業技能方面亟需提高。
- 在編寫程式碼的過程中遇到的最棘手的問題是關於輸入和輸出的問題。輸入需要16進位制,且需要將該16進位制放入byte陣列中。通過查詢大量資料和反覆考量,將16進製作為String型別輸入,再使用一個方法擷取該字串的某段轉換成int型別,最終轉換成byte型別裝入陣列。
- 在本次程式碼編寫中有一個運用的比較巧妙的技巧,要求自動產生明文且採用16進位制輸入,這裡巧妙地用到了UUID,UUID 是 通用唯一識別碼(Universally Unique Identifier)的縮寫,UUID的標準型式包含32個16進位制數字,恰好符合本課題的輸入要求。
5 參考文獻
[1] 何林波 物件導向程式設計[M] 西安:西安電子科技大學出版社,2016.8
[2] 張仕斌. 應用密碼學[M].西安:西安電子科技大學出版社,2017.1
[3] 張金全.資訊保安數學基礎[M]. 西安:西安電子科技大學出版社, 2015.12
相關文章
- Java實現AES和RSA演算法Java演算法
- STM32: 實現ADVANCED ENCRYPTION STANDARD(AES) – 128-BIT加密演算法加密演算法
- 【Java】跳躍表的實現以及用例測試Java
- PHP 實現 AES-128-CBC-PKCS5Padding 加密PHPpadding加密
- AES128-CMACMac
- 程式碼測試用例指南
- 測試用例最佳實踐
- AES和DES程式碼實現
- 測試面試-測試用例面試
- 測試用例
- C# Rsa加密(私鑰加密、公鑰解密、金鑰格式轉換、支援超大長度分段加密)C#加密解密
- 測試——水杯的測試用例
- 測試用例和測試方法
- 演算法題-測試用例執行計劃演算法
- 測試用例—教室
- 【5】測試用例
- 3.2 公開金鑰演算法演算法
- Java 開發者必備:一文解決 AES 加密中的“非法金鑰大小”異常Java加密
- 萬能測試用例及測試用例編寫方法(待更新)
- 手工測試用例與自動化測試用例的區別
- 自動化測試實戰技巧:「用例失敗重試機制」實現方案分享
- 從Java金鑰庫讀取輸出私鑰Java
- 介面測試平臺-66: 多介面用例實現之 小用例:新增+刪除+關閉+排序排序
- postman寫測試用例Postman
- 測試用例的方法
- 黑盒測試用例二
- 面經-測試用例
- Hanlp分詞例項:Java實現TFIDF演算法HanLP分詞Java演算法
- 【黑盒測試】測試用例的常用方法
- ccf 公共鑰匙盒 java實現Java
- C#和JAVA的RSA金鑰、公鑰轉換C#Java
- 長期迭代的系統如何管理維護測試用例?
- mindmaster啟用碼|mindmaster啟用金鑰AST
- 使用AES 128位加解密,加解密模式採用CBC,填充模式採用PKCS5Padding的Java工具方法示例解密模式paddingJava
- 測試用例設計指南
- 怎樣寫測試用例?
- 測試用例編寫方法
- Java volatile 的測試(Java程式碼實戰-004)Java