完整的AES分组与文件的加解密功能程序实现
[在此处输入文章标题]
完整的AES分组与文件的加解密功能程序实现
1 前言
本报告论述在论述AES加密/解密算法理论的基础上,设计一个AES加密/解密软件系统。AES,密码学中的高级加密标准(Advanced Encryption Standard,AES),又称 Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。AES在软体及硬件上都能快速地加解密,相对来说较易于实作,且只需要很少的记忆体。作为一个新的加密标准,目前正被部署应用到更广大的范围。AES具有以下可变密钥长为128bit、192bit、256bit三种,可变分组长为 128bit、192bit、256bit,同时它的强度高,能够抗所有已知的攻击。
2 程序实现要点(编程语言与开发环境、构件实现核心方法与程序设计技巧)
编写语言:java
开发环境:windows 8,MyEclipse,
用MyEclipse编写,生产jar文件,进而通过exe4j转化为exe程序,该可以在装有 windows 系统的计算机上使用。
3 程序实算展示
3.1 字节替换
SubWord()变换是一个基于S盒的非线性置换,它用于将输入或中间态的每一个字节通过一个简单的查表操作,将其映射为另一个字节。映射方法是把输入字节的高四位作为S盒的行值,低四位作为列值,然后取出S盒中对应的行和列的元素作为输出。
public void SubWord(char []A)//字节替代,用于密钥扩展;
{
for(int i=0;i<4;i++)
A[i]=S_BOX[A[i]];
}
// S盒置换
public static char [] S_BOX = {
99,124,119,123,242,107,111,197,48,1,103,43,254,215,171,
118,202,130,201,125,250,89,71,240,173,212,162,175,156,164,
114,192,183,253,147,38,54,63,247,204,52,165,229,241,113,216,
49,21,4,199,35,195,24,150,5,154,7,18,128,226,235,39,178,117,
9,131,44,26,27,110,90,160,82,59,214,179,41,227,47,132,83,209,
0,237,32,252,177,91,106,203,190,57,74,76,88,207,208,239,
170,251,67,77,51,133,69,249,2,127,80,60,159,168,81,163,64,
143,146,157,56,245,188,182,218,33,16,255,243,210,205,12,19,236,
95,151,68,23,196,167,126,61,100,93,25,115,96,129,79,220,
34,42,144,136,70,238,184,20,222,94,11,219,224,50,58,10,73,6,
36,92,194,211,172,98,145,149,228,121,231,200,55,109,141,213,
78,169,108,86,244,234,101,122,174,8,186,120,37,46,28,166,
180,198,232,221,116,31,75,189,139,138,112,62,181,102,72,3, 246,14,97,53,87,185,134,193,29,158,225,248,152,17,105,217,142,
148,155,30,135,233,206,85,40,223,140,161,137,13,191,230,
66,104,65,153,45,15,176,84,187,22,};
3.2行移位
ShiftRows()完成基于行的循环移位操作,变换方法是第0行不动,第一行循环左移一个字节,第二位循环左移两个字节,第三行循环左移三个字节。
/*
* 行移位
*/
public void ShiftRows(char []state, int Nb,JTextArea showProcess)
{
String STR="";
String output="";
char[] t = new char [8];
for( int r=0;r<4;r++)
{
for(int c=0;c<Nb;c++)t[c]= state[Nb*r+(r+c)%Nb];
for(int c=0;c<Nb;c++){
state[Nb*r+c]=t[c];
STR+=state[Nb*r+c];
}
}
for (int i=0;i<STR.length();i++)
{
int ch = (int)STR.charAt(i);
String s4 = Integer.toHexString(ch);
output = output+" 0x" + s4;
} //0x表示十六进制
showProcess.append("\n经过行移位后变为:"+output);
}
3.3列混合
MixColumns()实现逐列混合,方法是s’(x)=c(x)*s(x)mod(x^4+1)
/*
* 列混合
*/
public void MixColumns(char[]state, int Nb,JTextArea showProcess)
{
String STR="";
String output="";
int [] t = new int[4];
for( int c=0;c<Nb;c++)
{
for(int r=0;r<4;r++)t[r] = state[Nb*r+c];
for(int r=0;r<4;r++)
{
state[Nb*r+c] = (char)(Ffmul(0x02,t[r])^Ffmul(0x03,t[(r+1)%4])
^t[(r+2)%4]^t[(r+3)%4]);
STR+=state[Nb*r+c];
}
}
for (int i=0;i<STR.length();i++)
{
int ch = (int)STR.charAt(i);
String s4 = Integer.toHexString(ch);
output = output+" 0x" + s4;
} //0x表示十六进制
showProcess.append("\n经过列混合后变为"+output);
}
public int Ffmul(int A, int B)
{
//查对数表;
if(A==0||B==0)return 0;
A = Log[A];
B = Log[B];
A =(A+B)%0xff;
//查反对数表;
A = Log_1[A];
return A;
}
3.4轮密钥加
AddRoundKey()用于将输入或中间态S的每一列与一个密钥字ki进行按位异或,每一个轮密钥由Nb个字组成。
/*
* 轮密钥加
*/
public void AddRoundKey(char[]state, int Nb,int round,JTextArea showProcess)
{
String Kkey="";
String STR="";
String output="";
for(int c=0;c<Nb;c++,round++)
for(int r=0;r<4;r++){
state[r*Nb+c] = (char)(state[r*Nb+c]^w[round*4+r]);
Kkey+=w[round*4+r];
STR+=state[Nb*r+c];
}
for (int i=0;i<Kkey.length();i++)
{
int ch = (int)Kkey.charAt(i);
String s4 = Integer.toHexString(ch);
output = output+" 0x" + s4;
} //0x表示十六进制
showProcess.append(output);
for (int i=0;i<STR.length();i++)
{
int ch = (int)STR.charAt(i);
String s4 = Integer.toHexString(ch);
output = output+" 0x" + s4;
} //0x表示十六进制
showProcess.append("\n经过轮密钥加后变为:"+output);
}
3.5密钥扩展
通过生成器产生Nr+1个轮密钥,每个轮密钥由Nb个字组成,共有Nb(Nr+1)个字。在加密过程中,需要Nr+1个轮密钥,需要构造4(Nr+1)个32位字。首先将输入的4个字节直接复制到扩展密钥数组的前4个字中,然后每次用4个字填充扩展密钥数余下的部分。
/*
* 密钥扩展
*/
public byte[] Transform(char[]state, int Nb,int Nr,JTextArea showProcess)
{
int round=1;
int l=0;//计算轮数;
showProcess.append("\n初始密钥为:");
AddRoundKey(state,Nb,0,showProcess);
for(;round<Nr;round++)
{
l++;
SubChar(state,Nb,showProcess);//字节代替
ShiftRows(state,Nb,showProcess);//行移位
MixColumns(state,Nb,showProcess);//列混合
showProcess.append("\n第"+l+"轮子密钥为:");
AddRoundKey(state,Nb,round*Nb,showProcess);//轮密钥加
}
SubChar(state,Nb,showProcess);
ShiftRows(state,Nb,showProcess);
AddRoundKey(state,Nb,round*Nb,showProcess);
return CharToByte(state);
}
public byte[] ReTransform(char []state, int Nb,int Nr,JTextArea showProcess)
{
int l=0;
showProcess.append("\n初始密钥为:");
AddRoundKey(state, Nb,Nr*Nb,showProcess);
for(int round=Nr-1;round>=1;round--)
{
l++;
InvShiftRows(state,Nb,showProcess);
InvSubint(state,Nb,showProcess);
showProcess.append("\n初始密钥为:");
AddRoundKey(state,Nb,round*Nb,showProcess);
showProcess.append("\n第"+l+"轮子密钥为:");
InvMixColumns(state,Nb,showProcess);
}
InvShiftRows(state,Nb,showProcess);
InvSubint(state,Nb,showProcess);
AddRoundKey(state,Nb,0,showProcess);
return CharToByte(state);
}
3.6逆字节替换
与字节代替类似,逆字节代替基于逆S盒实现。
/*
* 逆字节替换
*/
public void InvSubint(char []state, int Nb,JTextArea showProcess)
{
for(int i=0;i<4*Nb;i++)
state[i] = S_BOX_1[state[i]%256];
}
3.7逆行移位
与行移位相反,逆行移位将态state的后三行按相反的方向进行移位操作,即第0行保持不变,第1行循环向右移一个字节,第2行循环向右移动两个字节,第3行循环向右移动三个字节。
/*
* 逆行移位
*/
public void InvShiftRows(char[]state, int Nb,JTextArea showProcess)
{
char [] t = new char[8];
for( int r=0;r<4;r++)
{
for(int c=0;c<Nb;c++)
t[(c+r)%Nb] = state[r*Nb+c];
for(int c=0;c<Nb;c++)
state[r*Nb+c]=t[c];
}
}
3.8逆列混合
逆列混淆的处理办法与InvMixColumns()类似,每一列都通过与一个固定的多项式d(x)相乘进行交换。
/*
* 逆列混合
*/
public void InvMixColumns(char []state, int Nb,JTextArea showProcess)
{
char []t = new char[4];
for( int c=0;c<Nb;c++)
{
for(int r=0;r<4;r++)t[r] = state[Nb*r+c];
for(int r=0;r<4;r++)
{
state[Nb*r+c] = (char)(Ffmul(0x0e,t[r])^Ffmul(0x0b,t[(r+1)%4])
^Ffmul(0x0d,t[(r+2)%4])^Ffmul(0x09,t[(r+3)%4]));
}
}
}
3.9 加密
加密部分分了两种情况,一种是自动检查加密程序的正确性,之前在程序里给明文和密钥赋上初值,运行程序检验结果是否正确;另一种是用户手动输入32位的十六进制数,进行加密,我是把每一具体项模块化,将功能在每个具体模块中实现,只需要直接调用,视觉效果强,一目了然。
下面是实现加密功能一些关键代码
/*
* 加密
*/
public long AES_Encrypt(String OpenPath,String SavePath,String m_Key,int Nb,int Nk,JTextArea T)
throws IOException
{
showProcess=T;
showProcess.append("\n加密文件...");
//以二进制读的方式打开要加密的文件;
//以二进制写的方式打开保存密文的文件;
FileInputStream fp1 = new FileInputStream(OpenPath);
FileOutputStream fp2 = new FileOutputStream(SavePath,true);
int Length = fp1.available();//得到要加密的文件的长度,单位bit;
if(Length==0)return 0;
int leave = Length%(4*Nb); //求剩余的字块的字节数;
long rounds = Length/(4*Nb); //得到整块的加密轮数;
if(leave!=0)rounds++;
long copy_rounds = rounds;//rounds可以变化不影响copy_rounds的值
showProcess.append("\n正在加密"+rounds+"个明文组");
byte[] state = new byte[4*8]; //作为加密时存放要加密的明文块,字节类型数组;
byte[] copy = new byte[4*8]; //用来进行短块处理时的缓存区;
int Nr=GetRounds(Nb,Nk); //得到加密的轮数,Nb为密钥矩阵的列数;
KeyExpansion(m_Key,Nb,Nk,Nr); //生成各轮子密钥;
if(copy_rounds==1&&rounds==1)
{
if(leave==0) fp1.read(state,0,4*Nb);//明文的长度恰好等于分组长度;
else
{
fp1.read(state,0,leave);//明文的长度小于八个字符,每个字符一字节;
for(int i=leave;i<4*Nb;i++)
state[i]=0; //后面用空格补齐;
}
state = Transform(ByteToChar(state),Nb,Nr,showProcess); //加密变换;
fp2.write(state,0,4*Nb);//将加密后的密文块写入目标文件;
rounds--;
}
else if(copy_rounds>1&&leave!=0)//如果明文的长度大于分组长度且字符数不是分组长度的整数倍
{ //时,需要进行短块处理;
fp1.read(state,0,4*Nb);
state = Transform(ByteToChar(state),Nb,Nr,showProcess);//先加密最前面的一块;
fp2.write(state,0,leave);//将余数个数那么多的密文前面部分字符存入文件;
int j=0;
for(int i=leave;i<4*Nb;i++)
copy[j++]=state[i];//将最先加密的密文的后部分放在短块前面,长度为分组长度减去leave;
fp1.read(copy,j,leave);//将剩余个数那么多的明文放在后面(是第二组明文块中的明文),组成一组完整加密明文块;
copy = Transform(ByteToChar(copy),Nb,Nr,showProcess);
fp2.write(copy,0,4*Nb);
rounds-=2;//这就加密了2组了,实质是一组多;
}
while(rounds>0)//以下处理的明文是分组的整数倍的情况;
{
fp1.read(state,0,4*Nb);
state = Transform(ByteToChar(state),Nb,Nr,showProcess);
fp2.write(state,0,4*Nb);
rounds--;
}
fp1.close();//关闭源文件和目标文件;
fp2.close();
return ((copy_rounds-1)*4*Nb+leave);//返回文件长度;
}
3.10 解密
AES解密我也是分成了两个部分,第一部分是在程序中对密文和密钥赋初值,通过与标准对照检查解密过程的正确性;第二部分是用户手动输入密文和密钥,程序对其进行解密,得到最后的明文。
解密过程基本如下:
1)获取输入的明文和密钥
2)通过密钥扩展过程获取各轮密钥
3)轮密钥加变换过程
4)逆行移位
5)逆字节替代
6)轮密钥加变换
7)逆列混淆
4—7步共9次循环,最后一轮实现4—6步,完成解密过程。
主要代码如下:
public long AES_DeEncrypt(String OpenPath,String SavePath,String m_Key, int Nb, int Nk,JTextArea showProcess)
throws IOException
{
showProcess.append("\n解密文件...");
//以二进制读的方式打开要解密密的文件;
//以二进制写的方式打开保存解开的密文文件;
FileInputStream fp1= new FileInputStream(OpenPath);
FileOutputStream fp2= new FileOutputStream(SavePath,true);
int Length = fp1.available();//得到要解密的文件的长度;
if(Length==0)return 0;
int leave=Length%(4*Nb);//求剩余的字块的字节数;
long rounds=Length/(4*Nb);//得到整块的解密轮数;
if(leave!=0)rounds++;
long copy_rounds=rounds;
showProcess.append("\n正在解密"+rounds+"个密文组");
byte []state = new byte[4*8]; //解密时存放密文块;
int Nr = GetRounds(Nb,Nk); //得到解密时循环轮数;
KeyExpansion(m_Key,Nb,Nk,Nr); //生成各轮子密钥
byte[] copy = new byte[32];
if(leave!=0)//需要进行短块处理
{
fp1.read(copy,0,leave);//先把余数个密文字符保存进数组;
fp1.read(state,0,4*Nb);//读取紧接着的下一个密文块;
state = ReTransform(ByteToChar(state),Nb,Nr,showProcess); //解密;
int j=0;
for(int i=leave;i<4*Nb;i++) //把解密后的明文前部分和前面的余数个合在一起组成一块,
copy[i]=state[j++]; //一起解密;
copy = ReTransform(ByteToChar(copy),Nb,Nr,showProcess);
//将解密后的明文写入目标文件;
fp2.write(copy,0,4*Nb);
fp2.write(state,j,leave);//将余数个明文写入目标文件;
rounds-=2; //已经完成了两轮解密所以减二;
}
while(rounds>0)//对后面是分组长度的整数倍的密文块解密;
{
fp1.read(state,0,4*Nb);//读取密文块;
copy = ReTransform(ByteToChar(state),Nb,Nr,showProcess); //解密变换;
fp2.write(copy,0,4*Nb);//将解密后的明文写入目标文件;
rounds--; //轮数减一;
}
fp1.close();//关闭源文件和目标文件;
fp2.close();
return ((copy_rounds-1)*4*Nb+leave);//返回文件长度
}
3.11 效果截图
图1 软件软件效果图
图2 加密前的源文件数据:
图3 加密过程显示结果图
图4 加密过程显示结果图
图 5 加密后效果图:
4 结语
经过这次《信息安全技术》的课程学习和项目实践设计,我个人得到了不少的收获,一方面加深了我对课本理论的认识,另一方面也提高了实践操作能力。
我的此次课程作业中我主要关于编写一个加密解密程序,这也使得我更加扎实的掌握了有关于信息安全密码学的相关方面的知识。在设计过程中虽然遇到了一些困难,但经过一次又一次的思考,一遍又一遍的检查终于找出了问题所在,也暴露出了前期我在这方面的知识欠缺和经验不足。实践出真知,通过亲自动手编写开发,使我们掌握的知识得以实践。
在课程作业过程中,我们不断发现错误,不断改正,不断领悟,不断获取。在设计过程中遇到了很多问题,最后在不懈的努力下,终于迎刃而解。在今后社会的发展和学习实践过程中,一定要不懈努力,不能遇到问题就想到要退缩,一定要不厌其烦的发现问题所在,然后一一进行解决,只有这样,才能成功的做成想做的事,才能在今后的道路上劈荆斩棘,而不是知难而退,那样永远不可能收获成功,收获喜悦,也永远不可能得到社会及他人对你的认可!
个人认为,在这本次的的课程作业中,不仅培养了独立思考、动手操作的能力,在各种其它能力上也都有了提高。更重要的是,在课程作业过程中,我们学会了很多学习的方法。而这是日后最实用的,真的是受益匪浅。要面对社会的挑战,只有不断的学习、实践,再学习、再实践。这对于我们的将来也有很大的帮助。以后,不管有多苦,我想我们都能变苦为乐,找寻有趣的事情,发现其中珍贵的事情。回顾起此课程作业,从理论到实践,可以说得是苦多于甜,但是可以学到很多很多的东西,同时不仅可以巩固了以前所学过的知识,而且学到了很多在书本上所没有学到过的知识。通过这次课程作业使我懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的,只有把所学的理论知识与实践相结合起来,从理论中得出结论,才能真正为社会服务,从而提高自己的实际动手能力和独立思考的能力。
总之,认真对待每一个学习的机会,珍惜过程中的每一分一秒,学到最多的知识和方法,锻炼自己的能力,这个是我在本次信息安全技术课程作业中学到的最重要的、受用终身的知识!
可以下载原来文件:http://download.csdn.net/detail/zhoujn90/8369289