AES加密与解密
1、在深入理解 AES 加密 /解密算法理论的基础上,设计一个 AES 加密 解密软件系统;
2、完成一个明文分组的加解密,明文和密钥是十六进制,长度都为 128 比特( 16 个16 进制数),输入明文和密钥,输出密文,进行加密后,能够进行正确的解密;
3、程序运行时,要求输出每一轮使用的密钥,以及每一轮加密或解密之后的 16 进制表示的值;
AES的加密公式为C = E(K,P),在加密函数E中,会执行一个轮函数,并且执行10次这个轮函数,这个轮函数的前9次执行的操作是一样的,只有第10次有所不同。也就是说,一个明文分组会被加密10轮。AES的核心就是实现一轮中的所有操作。
AES算法主要可以分为秘钥扩展、字节替换、行移位、列混合和轮秘钥加这5个步骤。
图1:AES的十轮原理图
图2:密钥扩展示意图
图3:字节代换示意图
3、行移位(ShiftRows):将数据矩阵的每一行循环移位一定长度。自上到下分别循环左移0,1,2,3位
图4:行移位示意图
4、列混合(MixColumns):将数据矩阵乘以一个固定的矩阵,增加混淆程度。
图5:列混合示意图
5、轮秘钥加(AddRoundKey):将数据矩阵与秘钥矩阵进行异或操作。
图6:密钥加示意图
四、算法设计
我具体实现的是固定输入下的AES演示,输入为一个4x4的十六进制矩阵,输出为每轮操作后的矩阵样式,及每轮的轮密钥。
密钥生成(KeyExpansions):先初始化Rcon[]数组和初始密钥,存储在44x4的Roundkey[]数组中,生成完成密钥之后,为方便操作,再将需要用到的密钥存储在新数组int[][] Roundkeynew中 。
在加密过程中,需要 Nr+1 个轮密钥,需要构造 4(Nr+1 )个 32 位字。首先将输入的 4 个字节直接复制到扩展密钥数组的前 4 个字中, 得到 W[0],W[1],W[2],W[3]; 然后每次用 4 个字填充扩展密钥数余下的部分。
具体生成步骤:移位-> S-Box变换 ->异或
//Roundkey[i-1][j] 先移位,再s-box变换
int s = Roundkey[i-1][(j+1)%4]; //移位
int k = sBox[s/16][s-s/16*16]; //s-box变换
Roundkey[i][j] = Roundkey[i-4][j]^Rcon[i/4-1][j]^k;
字节替换(SubBytes):直接创建有一个二维int数组sBox存储代换字节,代换时只需要将十六分位和个位直接变成数组的行列坐标,找出对应的十六进制数即为所求。
行移位(ShiftRows):第一行循环左移0位,第二行循环左移1位,第三行循环左移2位,第四行循环左移3位。
列混合(MixColumns):列混合其实就是对一个状态的每一列去乘一个矩阵,其中乘法是在有限域GF(2^8)内进行的,不可约多项式为x^8+x^4+x^2+x+1。这里重点是有限域GF(2^8)上的乘法。采用的算法的原理如下:
1、 GF(2^8)中任何数乘0x01都不变
2、 GF(2^8)中计算乘0x02,可以分两种情况考虑:
(1)、原数值小于(1000 0000)2,即0x80的时候,乘2后第8个比特不会溢出,那么结果就是原数值左移一位;
(2)、原数值大于(1000 0000)2,即0x80的时候,乘2后第8个比特会溢出,结果需要减去一个不可约多项式(x8+x4+x2+x+1),注意到GF(2^8)中的减法就是加法,那么结果就为原数值左移一位后(乘2)再与(0001 1011)2即0x1b进行异或(这里x8已经减掉了,只需要再减去x4+x2+x+1)。
if(content[p][j]>127) s=s^(content[p][j]*2-256)^0x1B;
if(content[p][j]<128) s=s^(content[p][j]*2);
轮秘钥加(AddRoundKey):就是简单地将数据矩阵与秘钥矩阵进行逐个异或操作。
重复十轮(第十轮无列混合)之后ToHexString输出
由于结果太长故将截图分条显示
具体代码:
public class Rijdael {
static int sBox[][] = {
{0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76},
{0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0},
{0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15},
{0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75},
{0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84},
{0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf},
{0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8},
{0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2},
{0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73},
{0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb},
{0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79},
{0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08},
{0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a},
{0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e},
{0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf},
{0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16},
};
public static void main(String[] args) {
//秘钥装填
int[][] Roundkey = new int[44][4];
Roundkey[0][0]=0x2b;Roundkey[1][0]=0x28;Roundkey[2][0]=0xab;Roundkey[3][0]=0x09;
Roundkey[0][1]=0x7e;Roundkey[1][1]=0xae;Roundkey[2][1]=0xf7;Roundkey[3][1]=0xcf;
Roundkey[0][2]=0x15;Roundkey[1][2]=0xd2;Roundkey[2][2]=0x15;Roundkey[3][2]=0x4f;
Roundkey[0][3]=0x16;Roundkey[1][3]=0xa6;Roundkey[2][3]=0x88;Roundkey[3][3]=0x3c;
int[][] Rcon = {
{0x01,0x00,0x00,0x00},
{0x02,0x00,0x00,0x00},
{0x04,0x00,0x00,0x00},
{0x08,0x00,0x00,0x00},
{0x10,0x00,0x00,0x00},
{0x20,0x00,0x00,0x00},
{0x40,0x00,0x00,0x00},
{0x80,0x00,0x00,0x00},
{0x1b,0x00,0x00,0x00},
{0x36,0x00,0x00,0x00}
};
//密钥生成
for(int i=4;i<44;i++) {
for(int j=0;j<4;j++) {
if(i%4==0) {
//Roundkey[i-1][j] 先移位,再s-box变换
int s = Roundkey[i-1][(j+1)%4]; //移位
int k = sBox[s/16][s-s/16*16]; //s-box变换
Roundkey[i][j] = Roundkey[i-4][j]^Rcon[i/4-1][j]^k;
}
if(i%4!=0) {
Roundkey[i][j] = Roundkey[i-4][j]^Roundkey[i-1][j];
}
}
}
int[][] Roundkeynew = new int[4][40];
//新数组
for(int i=0;i<4;i++) {
for(int j=0;j<40;j++) {
Roundkeynew[i][j]=Roundkey[j+4][i];
}
}
//初始信息
int [][] content = {
{0x19,0xa0,0x9a,0xe9},
{0x3d,0xf4,0xc6,0xf8},
{0xe3,0xe2,0x8d,0x48},
{0xbe,0x2b,0x2a,0x08}
};
int [][] contentnew ={
{0,0,0,0},
{0,0,0,0},
{0,0,0,0},
{0,0,0,0}
};
//10轮变换 第十轮没得列混淆
for(int n=0;n<10;n++) {
//1。字节代换
for(int i=0;i<4;i++) {
for(int j=0;j<4;j++) {
content[i][j] = sBox[content[i][j]/16][content[i][j]-content[i][j]/16*16];
}
}
System.out.println(n+1+"轮字节代换后:");
for(int i=0;i<4;i++) {
for(int j=0;j<4;j++) {
System.out.print(Integer.toHexString(content[i][j])+" ");
}
System.out.println();
}
//2。行移位 左移
int t;
t=content[1][0]; content[1][0]=content[1][1];
content[1][1]=content[1][2]; content[1][2]=content[1][3]; content[1][3]=t;
t=content[2][3]; content[2][3]=content[2][1]; content[2][1]=t;
t=content[2][2]; content[2][2]=content[2][0]; content[2][0]=t;
t=content[3][3]; content[3][3]=content[3][2]; content[3][2]=content[3][1];
content[3][1]=content[3][0]; content[3][0]=t;
System.out.println(n+1+"轮行移位后:");
for(int i=0;i<4;i++) {
for(int j=0;j<4;j++) {
System.out.print(Integer.toHexString(content[i][j])+" ");
}
System.out.println();
}
//3。列混淆
int [][] mix = {
{2,3,1,1},
{1,2,3,1},
{1,1,2,3},
{3,1,1,2}
};
if(n!=9) {
for(int j=0;j<4;j++) {//content的列
for(int i=0;i<4;i++) {//mix的行
int s=0;
for(int p=0;p<4;p++) {//mix的列
//content[p][j]*mix[i][j] (4个值xor之后是content[i][j])
if(mix[i][p]==1) s=s^content[p][j];
if(mix[i][p]==2) {
if(content[p][j]>127) s=s^(content[p][j]*2-256)^0x1B;
if(content[p][j]<128) s=s^(content[p][j]*2);
}
if(mix[i][p]==3) {
if(content[p][j]>127) s=s^(content[p][j]*2-256)^0x1B^content[p][j];
if(content[p][j]<128) s=s^(content[p][j]*2)^content[p][j];
}
}
contentnew[i][j]=s;
}
}
}
if(n==9) {
for(int i=0;i<4;i++) {
for(int j=0;j<4;j++) {
contentnew[i][j]=content[i][j];
}
}
}
//测试
System.out.println(n+1+"轮列混淆后:");
for(int i=0;i<4;i++) {
for(int j=0;j<4;j++) {
System.out.print(Integer.toHexString(contentnew[i][j])+" ");
}
System.out.println();
}
//4.密钥加
for(int i=0;i<4;i++) {
for(int j=0;j<4;j++) {
contentnew[i][j]=contentnew[i][j]^Roundkeynew[i][4*n+j];
}
}
//测试
System.out.println(n+1+"轮密钥加后:");
for(int i=0;i<4;i++) {
for(int j=0;j<4;j++) {
System.out.print(Integer.toHexString(contentnew[i][j])+" ");
}
System.out.println();
}
//更新content值
for(int i=0;i<4;i++) {
for(int j=0;j<4;j++) {
content[i][j]=contentnew[i][j];
}
}
}
//10论之后
for(int i=0;i<4;i++) {
for(int j=0;j<4;j++) {
System.out.print(Integer.toHexString(contentnew[i][j])+" \t");
}
System.out.println();
}
}
}