)AES在软体及硬件上都能快速地加解密,相对来说较易于实作,且只需要很少的记忆体。
作为一个新的加密标准,目前正被部署应用到更广大的范围.
2.系统设计
2.1系统主要目标
基本要求部分:
1.在深入理解AES加密/解密算法理论的基础上,设计一个AES加密/解密软件系统;
2.完成一个明文分组的加解密,明文和密钥是十六进制,长度都为64比特<16个16进制数),输入明文和密钥,输出密文,进行加密后,能够进行正确的解密;
3.程序运行时,要求输出每一轮使用的密钥,以及每一轮加密或解密之后的16进制表示的值;
4.要求提供所设计系统的报告及完整的软件。
较高要求部分:
1.如果明文不止一个分组,程序能完成分组,然后加密;最后一个分组长度不足时要求完成填充;密钥长度不足时能进行填充,过长则自动截取前面部分。
2.密钥采用ASCII码,明文要求输入信息可以是文字<可以是汉字或英文,信息量要求不止一个加密分组长度),任意字符,或者是文本文档,或者普通文件。
进行加密后,能够进行正确的解密;
3.程序代码有比较好的结构,模块划分合理,如用类进行封装,通过调用类的成员函数实现加密解密功能,函数的参数及返回值设置合理等;
4.界面友好,程序实现有新意。
2.2主要软件需求<运行环境)
本软件适用VC语言编写,编译成功后的EXE文件可以在装有windows系统的任何计算机上使用。
测试平台:
WindowsXPProfessional
使用软件:
VisualC++6.0
2.3功能模块与系统结构
主要功能模块如下:
1.字节替换SubByte
A0,0
A0,1
A0,2
A0,3
A1,0
A1,1
A1,2
A1,3
A2,0
A2,1
A2,2
A2,3
A3,0
A3,1
A3,2
A3,3
B0,0
B0,1
B0,2
B0,3
B1,0
B1,1
B1,2
B1,3
B2,0
B2,1
B2,2
B2,3
B3,0
B3,1
B3,2
B3,3
2.行移位ShiftRow
3.列混合MixColumn
4.轮密钥加AddRoundKey
5.逆字节替换
通过逆S盒的映射变换得到
6.逆行移位InvShiftRow
与加密时的行移位区别在于移位方向相反。
7.逆列混淆
加密与解密系统流程图如下所示,
3系统功能程序设计
3.1基本要求部分
3.1.1字节替换
SubBytes<)变换是一个基于S盒的非线性置换,它用于将输入或中间态的每一个字节通过一个简单的查表操作,将其映射为另一个字节。
映射方法是把输入字节的高四位作为S盒的行值,低四位作为列值,然后取出S盒中对应的行和列的元素作为输出。
unsignedcharsubbytes(unsignedcharstate[4][4]>
{printf("aftersubbyte:
\n">。
//取出中间态state映射到S盒中的值赋给中间态state
for(i=0。
i<4。
i++>
{for(j=0。
j<4。
j++>
state[i][j]=sbox[state[i][j]]。
}
for(i=0。
i<4。
i++>//输出到屏幕显示state
{for(j=0。
j<4。
j++>
printf("\t\t%02x",state[i][j]>。
printf("\n">。
}
printf("\n">。
return0。
}
3.1.2行移位
ShiftRows<)完成基于行的循环移位操作,变换方法是第0行不动,第一行循环左移一个字节,第二位循环左移两个字节,第三行循环左移三个字节。
unsignedcharshiftrows(unsignedcharstate[4][4]>
{printf("aftershiftrows:
\n">。
//在中间态的行上,
k=state[1][0]。
//第0行不变
state[1][0]=state[1][1]。
//第一行循环左移一个字节
state[1][1]=state[1][2]。
//第二行循环左移两个字节
state[1][2]=state[1][3]。
//第三行循环左移三个字节
state[1][3]=k。
k=state[2][0]。
state[2][0]=state[2][2]。
state[2][2]=k。
k=state[2][1]。
state[2][1]=state[2][3]。
state[2][3]=k。
k=state[3][0]。
state[3][0]=state[3][3]。
state[3][3]=state[3][2]。
state[3][2]=state[3][1]。
state[3][1]=k。
for(i=0。
i<4。
i++>//输出到屏幕显示state
{for(j=0。
j<4。
j++>
printf("\t\t%02x",state[i][j]>。
printf("\n">。
}
printf("\n">。
return0。
}
3.1.3列混合
MixColumns<)实现逐列混合,方法是s’(x>=c(x>*s(x>mod(x^4+1>
unsignedcharmixcolumns(unsignedcharstate[4][4]>
{printf("aftermixcolumns:
\n">。
//实现(02030101>与中间态state分别相乘后异或得相应值
for(i=0。
i<4。
i++>//(01020301>
{//(01010203>
k=state[0][i]。
//(03010102>
temp[0]=state[0][i]^state[1][i]^state[2][i]^state[3][i]。
temp[1]=state[0][i]^state[1][i]。
temp[1]=xtime(temp[1]>。
state[0][i]^=temp[1]^temp[0]。
temp[1]=state[1][i]^state[2][i]。
temp[1]=xtime(temp[1]>。
state[1][i]^=temp[1]^temp[0]。
temp[1]=state[2][i]^state[3][i]。
temp[1]=xtime(temp[1]>。
state[2][i]^=temp[1]^temp[0]。
temp[1]=state[3][i]^k。
temp[1]=xtime(temp[1]>。
state[3][i]^=temp[1]^temp[0]。
}
for(i=0。
i<4。
i++>//输出到屏幕显示state
{for(j=0。
j<4。
j++>
printf("\t\t%02x",state[i][j]>。
printf("\n">。
}
printf("\n">。
return0。
}
3.1.4轮密钥加
AddRoundKey(>用于将输入或中间态S的每一列与一个密钥字ki进行按位异或,每一个轮密钥由Nb个字组成。
unsignedcharaddroundkey(unsignedcharstate[4][4],unsignedcharw[4][4]>
{printf("addroundkey%d:
\n",round++>。
//将中间态state中的每一列与一个密钥字(w[4][4]中的一列>进行按位异或
for(i=0。
i<4。
i++>//完了又赋值给state
{for(j=0。
j<4。
j++>
state[i][j]^=w[i][j]。
}
for(i=0。
i<4。
i++>//输出到屏幕显示出来state
{for(j=0。
j<4。
j++>
printf("\t\t%02x",state[i][j]>。
printf("\n">。
}
printf("\n">。
return0。
}
3.1.5密钥扩展
通过生成器产生Nr+1个轮密钥,每个轮密钥由Nb个字组成,共有Nb在加密过程中,需要Nr+1个轮密钥,需要构造4首先将输入的4个字节直接复制到扩展密钥数组的前4个字中,得到W[0],W[1],W[2],W[3]。
然后每次用4个字填充扩展密钥数余下的部分。
//keyexpand
printf("afterkeyexpand:
\n">。
for(i=4。
i<8。
i++>
{
if(i%4==0>
{rotword[0]=w[1][i-1]。
rotword[1]=w[2][i-1]。
rotword[2]=w[3][i-1]。
rotword[3]=w[0][i-1]。
printf("rotword(>:
">。
for(j=0。
j<4。
j++>printf("%02x",rotword[j]>。
for(j=0。
j<4。
j++>
subword[j]=sbox[rotword[j]]。
printf("\nsubword(>:
">。
for(j=0。
j<4。
j++>printf("%02x",subword[j]>。
printf("\n\n">。
for(j=0。
j<4。
j++>
rcon[j]=subword[j]^Rcon[N][j]。
printf("after^Rcon(>:
">。
for(j=0。
j<4。
j++>printf("%02x",rcon[j]>。
printf("\n\n">。
for(j=0。
j<4。
j++>
w[j][i%4]=rcon[j]^w[j][i-4]。
printf("w[%d]:
",count>。
for(j=0。
j<4。
j++>printf("%02x",w[j][i%4]>。
count++。
}
else
{
for(j=0。
j<4。
j++>
w[j][i%4]=w[j][i%4]^w[j][(i%4>-1]。
printf("w[%d]:
",count>。
for(j=0。
j<4。
j++>printf("%02x",w[j][i%4]>。
count++。
}
printf("\n\n">。
}
printf("密钥扩展Roundkey:
\n">。
for(i=0。
i<4。
i++>
{for(j=0。
j<4。
j++>printf("\t\t%02x",w[i][j]>。
printf("\n">。
}
printf("\n">。
3.1.6逆字节替换
与字节代替类似,逆字节代替基于逆S盒实现。
unsignedcharInvSubbytes(unsignedcharstate[4][4]>
{for(i=0。
i<4。
i++>//基于逆S盒的映射替代
{for(j=0。
j<4。
j++>
{state[i][j]=rsbox[state[i][j]]。
}
}
printf("afterInvSubbyte:
\n">。
for(i=0。
i<4。
i++>
{for(j=0。
j<4。
j++>//输出到屏幕显示state
printf("\t\t%02x",state[i][j]>。
printf("\n">。
}
printf("\n">。
return0。
}
3.1.7逆行移位
与行移位相反,逆行移位将态state的后三行按相反的方向进行移位操作,即第0行保持不变,第1行循环向右移一个字节,第2行循环向右移动两个字节,第3行循环向右移动三个字节。
unsignedcharInvShiftRows(unsignedcharstate[4][4]>
{k=state[1][3]。
state[1][3]=state[1][2]。
//对中间态state进行移位操作
state[1][2]=state[1][1]。
//第0行保持不变
state[1][1]=state[1][0]。
//第1行循环右移一个字节
state[1][0]=k。
//第2行循环右移两个字节
//第3行循环右移三个字节
k=state[2][0]。
state[2][0]=state[2][2]。
state[2][2]=k。
k=state[2][1]。
state[2][1]=state[2][3]。
state[2][3]=k。
k=state[3][0]。
state[3][0]=state[3][1]。
state[3][1]=state[3][2]。
state[3][2]=state[3][3]。
state[3][3]=k。
printf("afterInvShiftRows:
\n">。
for(i=0。
i<4。
i++>//输出到屏幕显示state
{for(j=0。
j<4。
j++>
printf("\t\t%02x",state[i][j]>。
printf("\n">。
}
printf("\n">。
return0。
}
3.1.8逆列混合
逆列混淆的处理办法与MixColumns<)类似,每一列都通过与一个固定的多项式d(x>相乘进行交换。
unsignedcharInvMixColumns(unsignedcharstate[4][4]>
{printf("afterInvMixColumns:
\n">。
//实现(0e0b0d09>与中间态state分别相乘后异或得相应值
for(i=0。
i<4。
i++>//(090e0b0d>
{temp[0]=state[0][i]。
//(0d090e0b>
temp[1]=state[1][i]。
//(0b0d090e>
temp[2]=state[2][i]。
temp[3]=state[3][i]。
state[0][i]=Multiply(temp[0],0x0e>^Multiply(temp[1],0x0b>^Multiply(temp[2],0x0d>^Multiply(temp[3],0x09>。
state[1][i]=Multiply(temp[0],0x09>^Multiply(temp[1],0x0e>^Multiply(temp[2],0x0b>^Multiply(temp[3],0x0d>。
state[2][i]=Multiply(temp[0],0x0d>^Multiply(temp[1],0x09>^Multiply(temp[2],0x0e>^Multiply(temp[3],0x0b>。
state[3][i]=Multiply(temp[0],0x0b>^Multiply(temp[1],0x0d>^Multiply(temp[2],0x09>^Multiply(temp[3],0x0e>。
}
for(i=0。
i<4。
i++>//输出到屏幕显示state
{for(j=0。
j<4。
j++>
printf("\t\t%02x",state[i][j]>。
printf("\n">。
}
printf("\n">。
return0。
}
3.1.9加密
加密部分我分了两种情况,一种是自动检查加密程序的正确性,之前在程序里给明文和密钥赋上初值,运行程序检验结果是否正确;另一种是用户手动输入32位的十六进制数,进行加密,我是把每一具体项模块化,将功能在每个具体模块中实现,只需要直接调用,视觉效果强,一目了然。
下面是实现加密功能一些关键代码
voidAES_encrypt(unsignedcharState[][N],unsignedcharRoundKey[][N]>
{message[16]={0x32,0x43,0xf6,0xa8,0x88,0x5a,0x30,0x8d,0x31,0x31,0x98,0xa2,0xe0,0x37,0x07,0x34}。
key[16]={0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c}。
for(i=0。
i<4。
i++>{for(j=0。
j<4。
j++>//分别获取明文和密钥
{state[j][i]=message[m]。
w[j][i]=key[m]。
m++。
}
}.
addroundkey(state,w>。
for(round=2。
round<11。
round++>
{printf("第%d轮加密:
\n",round>。
subbytes(state>。
shiftrows(state>。
mixcolumns(state>。
keyexpand(w,round>。
addroundkey(state,w>。
}
subbytes(state>。
//最后一轮
shiftrows(state>。
keyexpand(w,10>。
addroundkey(state,w>。
}
3.1.10解密
AES解密我也是分成了两个部分,第一部分是在程序中对密文和密钥赋初值,通过与标准对照检查解密过程的正确性;第二部分是用户手动输入密文和密钥,程序对其进行解密,得到最后的明文。
解密过程基本如下:
1)获取输入的明文和密钥2)通过密钥扩展过程获取各轮密钥3)轮密钥加变换过程4)逆行移位5)逆字节替代6)轮密钥加变换7)逆列混淆
4—7步共9次循环,最后一轮实现4—6步,完成解密过程。
主要代码如下:
voidAES_decrypt(unsignedcharState[][N],unsignedcharw[][N]>
{key[16]={0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c}。
cipher[16]={0x39,0x25,0x84,0x1d,0x02,0xdc,0x09,0xfb,0xdc,0x11,0x85,0x97,0x19,0x6a,0x0b,0x32}。
printf("%02x",key[i]>。
printf("\n">。
/获取密文和密钥
for(i=0。
i<4。
i++>
{for(j=0。
j<4。
j++>
{state[j][i]=cipher[m]。
w[j][i]=key[m]。
m++。
}
}
Keyexpand(w,round>。
//获得密钥扩展列表
AddRoundKey(State,w>。
//首轮
for(i=9。
i>0。
i-->//1-9轮
{InvShiftRows(state>。
InvSubbytes(state>。
Keyexpand(w,round>。
AddRoundKey(State,w>。
InvMixColumns(State>。
}
InvShiftRows(State>。
//最后一轮
InvSubBytes(State>。
Keyexpand(w,0>。
AddRoundKey(State,w>。
}
4.测试报告
为检验程序的正确性,需要将结果与标准相对照。
参照老师课件里的AES运行程序的运行结果,与我的运行结果对照如下:
下面是我的运行界面:
上面的是标准,接着的是我的运行测试,下面的是我从终端中复制的过程:
密钥扩展Roundkey:
efa8b6db
4452710b
a55b25ad
417f3b00
addroundkey5:
e0c8d985
9263b1b8
7f6335be
e8c05001
第6轮加密:
afters