AES算法测试用例程序Java实现(密钥长度128比特)

AES算法测试用例程序Java实现(密钥长度128比特)   

                         ——2018.10.22

目录

1引言... 1

    1.1任务概要... 1

    1.2运行环境和开发环境... 1

    1.3密码算法原理简单介绍... 1

2  程序各模块详细设计... 3

    2.1核心模块主要实现算法的流程... 3

    2.2 AES加密的算法说明和实现方式... 4

        2.2.1字节代替(Subbyte)... 4

        2.2.2行移位(ShiftRows)... 4

        2.2.3列混合(MixColumns)... 5

        2.2.4 轮秘钥加(AddRoundKey)... 7

        2.2.5 秘钥扩展(KeyExpansion)... 7

    2.3 AES解密的算法说明和实现方式... 8

        2.3.1逆行移位(InvShiftRows)... 8

        2.3.2逆字节代替(InvSubbyte)... 9

        2.3.3逆列混合(InvMixColumns)... 10

3  程序测试... 10

      3.1手动输入明文和秘钥测试过程... 10

      3.2随机生成明文和秘钥测试过程... 11

      3.3手动输入密文和秘钥测试过程... 12

4  密码算法课程设计实践总结... 13

5  参考文献... 14

 

 

1引言

1.1任务概要

  1. 实现AES加密和解密基本功能;
  2. 实现AES加密测试用例功能,具体如下:
  • 随机产生n组128比特明文,加密产生n组128比特密文,密钥不变;b)操作简单,界面美观。
  • 输入和输出要求:
  1. 用函数实现AES加密和解密
  2. 输入和输出的128比特明文和密文采用十六进制
  3. 要求至少有一组来自标准fips-197的测试。
  4. 随机产生的明文和加密后的明文(即密文)保存为txt文件,保存格式为每行记录一组明文和对应密文。如txt文件格式

明文:0xbce3 ……     密文:0x……

明文:0x……     密文:0x……

加密密钥为:0x……

  •  解密验证,从(4)中txt文件,获取任意一组密文和密钥,解密输出明文,并输出显示在屏幕上。

1.2运行环境和开发环境

  1. 运行环境:JVM;
  2. 开发环境:Windows10;JDK1.8;eclipse;

1.3密码算法原理简单介绍


美国国家标准技术研究所在2001年发布了高级加密标准(AES)。AES是一个对称分组密码算法,旨在取代DES成为广泛使用的标准。根据使用的密码长度,AES最常见的有3种方案,用以适应不同的场景要求,分别是AES-128、AES-192和AES-256。本课题设计主要对AES-128进行介绍,另外两种的思路基本一样,只是轮数会适当增加。

AES算法测试用例程序Java实现(密钥长度128比特)_第1张图片

■图1 AES算法流程图

如图1所示,AES加密过程涉及到4种操作:字节替(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和轮密钥(AddRoundKey)。解密过程分别为对应的逆操作。由于每一步操作都是可逆的,按照相反的顺序进行解密即可恢复明文。加解密中每轮的密钥分别由初始密钥扩展得到。算法中16字节的明文、密文和轮密钥都以一个4x4的矩阵表示。

2  程序各模块详细设计

2.1核心模块主要实现算法的流程


 

AES算法测试用例程序Java实现(密钥长度128比特)_第2张图片

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)


列混合变换是通过矩阵相乘来实现的,经行移位后的状态矩阵与固定的矩阵相乘,得到混淆后的状态矩阵,如下图的公式所示: 

AES算法测试用例程序Java实现(密钥长度128比特)_第3张图片

AES算法测试用例程序Java实现(密钥长度128比特)_第4张图片

■图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]是一个字,其值见下表。

AES算法测试用例程序Java实现(密钥长度128比特)_第5张图片

■图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手动输入明文和秘钥测试过程

AES算法测试用例程序Java实现(密钥长度128比特)_第6张图片

AES算法测试用例程序Java实现(密钥长度128比特)_第7张图片

 

3.2随机生成明文和秘钥测试过程

AES算法测试用例程序Java实现(密钥长度128比特)_第8张图片

AES算法测试用例程序Java实现(密钥长度128比特)_第9张图片

AES算法测试用例程序Java实现(密钥长度128比特)_第10张图片

3.3手动输入密文和秘钥测试过程

AES算法测试用例程序Java实现(密钥长度128比特)_第11张图片

AES算法测试用例程序Java实现(密钥长度128比特)_第12张图片

 

4  密码算法课程设计实践总结

  1. 通过本次密码算法课程设计对AES算法的理解更加深刻,并且掌握了各算法的代码实现,将所学的理论知识运用到实际的程序中,做到了学以致用。
  2. 本次课程设计的选题内容是AES加密算法的实现。在仔细看懂AES加密原理的基础上,真正明白了加密的精髓虽然是简单的扩散(diffusion)和混淆(confusion),但是却是很精妙的所在。加密过程依次经过读进明文、读进密钥、产生轮子密钥、轮密钥加、轮变换(包括字节替换、列变换、列混淆、轮密钥加)及最后轮变换得到密文,而解密过程则是加密过程的逆过程,依次经过读取密文、读取密钥、产生轮子密钥、轮密钥加、轮变换(包括逆列变换、逆字节替换、获得轮密钥、轮密钥加、逆列混淆)和最后一轮变换。
  3. 在编写代码的过程中同时也遇到了很多的问题,深刻地意识到自身的不足和在专业技能方面亟需提高。
  4. 在编写代码的过程中遇到的最棘手的问题是关于输入和输出的问题。输入需要16进制,且需要将该16进制放入byte数组中。通过查找大量资料和反复考量,将16进制作为String类型输入,再使用一个方法截取该字符串的某段转换成int类型,最终转换成byte类型装入数组。
  5. 在本次代码编写中有一个运用的比较巧妙的技巧,要求自动产生明文且采用16进制输入,这里巧妙地用到了UUID,UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,UUID的标准型式包含32个16进制数字,恰好符合本课题的输入要求。

5  参考文献

[1] 何林波 面向对象程序设计[M] 西安:西安电子科技大学出版社,2016.8

[2] 张仕斌. 应用密码学[M].西安:西安电子科技大学出版社,2017.1

[3] 张金全.信息安全数学基础[M]. 西安:西安电子科技大学出版社, 2015.12

你可能感兴趣的:(安全)