实验二: AES加密标准
一:算法说明
Rijndael加密算法是分组长度可变的分组密码。分组长度,密钥长度可以为128,192,256比特。
假设加密的中间结果为状态State。状态State可以用类似于元素为一字节的矩阵来表示,矩阵的行数为4,列数为Nb,Nb等于分组长度除以32。如图2-1所示。
表2-1State矩阵
A00 | A01 | A02 | A03 | A04 | A05 |
A10 | A11 | A12 | A13 | A14 | A15 |
A20 | A21 | A22 | A23 | A24 | A25 |
A30 | A31 | A32 | A33 | A34 | A35 |
表2-2密钥组数
K00 | K01 | K02 | K03 |
K10 | K11 | K12 | K13 |
K20 | K21 | K22 | K23 |
K30 | K31 | K32 | K33 |
加密算法输入的是明文,转换为字节状态,以顺序A00,A10,A20,A30,A01,A02,A03……加密结束的时候,输出的也是从状态字节中按相同的顺序提取。
加密的轮数用Nr表示,Nr和Nb(分组列数),Nk(密钥k的列数)的关系如图2-3
表2-3 Nr和Nb,Nk的关系表
Rejndael算法的核心是轮变换,它由以下四个不同的变换组成。
Rounds(State,RoundKey)
{
(1) ByteSub(State);
(2) ShiftRow9State);
(3) MixColumn(State);
(4) AddRoundKey(State,RoundKey);
}
最后轮变换
FinalRounds(State,RoundKey)
{
ByteSub(State);
ShiftRow9State);
AddRoundKey(State,RoundKey);
}
二:AES加密变换的流程图 图2-4
加密时如果明文长度不是分组长度的整数倍时,采用在开头叠加加密的方法,如下图所示:
三:过程函数介绍:
1:ByteSub变换
ByteSub变换即ByteSubstution是一个独立作用于状态字节的非线性变换。
2.ShiftRow变换
这是一个移位变换,在变化中状态State的最后三行循环移位的位数如下表
Nr Nb | C1 | C2 | C3 |
4 | 1 | 2 | 3 |
6 | 1 | 2 | 3 |
8 | 1 | 3 | 4 |
即第一行移动C1字节,第二行移动C2字节,第三行移动C3字节,C1,C2,C3由Nb,Nr决定。
3:MixColumn变换
在MixColumn作用下,State的列看作是域GF(2(8))的多项式,mod(X(4)+1)乘以c(x)的结果为c(x)=(03)x(3)+(01)x(2)+(01)x+(02).注:(03)等为十六进制数字。
四:AES的解密:
AES的解密过程和加密的过程十分相似,只是ShiftRow变换时时循环左移, ByteSub是做逆置换。MixColumn变换也只是乘项的多项式变了,具体请参看源代码。此外,解密时各轮子密钥和加密时生成方式相同但是使用顺序恰好相反。这里就不作介绍了,可以参看源代码。
//
#if !defined(AFX_AES1_H__180B4158_55C8_46FF_B0B8_BBA1D2C73FB5__INCLUDED_)
#define AFX_AES1_H__180B4158_55C8_46FF_B0B8_BBA1D2C73FB5__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class AES
{
private:
const static byte Log[256];
const static byte Log_1[256];
const static byte
const static byte S_BOX_1[256];
const static byte Rcon[10][4];
byte key[4][8];
byte w[8*15][4];//用来存放扩展后的子密钥;
void ReTransform(byte*state,int Nb,int Nr);
void Transform(byte*state,int Nb,int Nr);
void InvMixColumns(byte*state,int Nb);
void InvShiftRows(byte*state,int Nb);
void InvSubByte(byte*state,int Nb);
void AddRoundKey(byte*state,int Nb,int round);
void ShiftRows(byte*state,int Nb);
byte Ffmul(byte A,byte B);
void MixColumns(byte*state,int Nb);
void SubByte(byte*state,int Nb);
int GetRounds(int Nb,int Nk);
void SubWord(byte*A);
void RotWord(byte*A);
void KeyExpansion(CString m_Key,int Nb,int Nk,int Nr);
public:
long AES_Encrypt(CString OpenPath,CString SavePath,CString m_Key,int Nb,int Nk);
long AES_DeEncrypt(CString OpenPath,CString SavePath,CString m_Key,int Nb,int Nk);
AES();
virtual ~AES();
};
#endif // !defined(AFX_AES1_H__180B4158_55C8_46FF_B0B8_BBA1D2C73FB5__INCLUDED_)// // AES1.cpp: implementation of the AES class.
//说明: 本类实现AES加密解密;
//作者单位:华中科技大学信息安全0304班
//作者姓名:祝涛
//完成日期:2005年9月16日
//
#include "stdafx.h"
#include "AES.h"
#include "AES1.h"
#include <fcntl.h>
#include <io.h>
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//
// Construction/Destruction
//
//对数表用来处理多项式的乘法,基于生成元3构造;
const byte AES:: Log[256]={
0,0,25,1,50,2,26,198,75,199,27,104,51,238,223,3,100,4,
224,14,52,141,129,239,76,113,8,200,248,105,28,193,125,194,29,
181,249,185,39,106,77,228,166,114,154,201,9,120,101,47,138,
5,33,15,225,36,18,240,130,69,53,147,218,142,150,143,219,189,
54,208,206,148,19,92,210,241,64,70,131,56,102,221,253,48,
191,6,139,98,179,37,226,152,34,136,145,16,126,110,72,195,163,
182,30,66,58,107,40,84,250,133,61,186,43,121,10,21,155,159,
94,202,78,212,172,229,243,115,167,87,175,88,168,80,244,234,
214,116,79,174,233,213,231,230,173,232,44,215,117,122,235,
22,11,245,89,203,95,176,156,169,81,160,127,12,246,111,23,
196,73,236,216,67,31,45,164,118,123,183,204,187,62,90,251,96,
177,134,59,82,161,108,170,85,41,157,151,178,135,144,97,190,
220,252,188,149,207,205,55,63,91,209,83,57,132,60,65,162,
109,71,20,42,158,93,86,242,211,171,68,17,146,217,35,32,46,137,
180,124,184,38,119,153,227,165,103,74,237,222,197,49,254,
24,13,99,140,128,192,247,112,7,};
//反对数表,基于生成元3构造;
const byte AES:: Log_1[256]=
{1,3,5,15,17,51,85,255,26,46,114,150,161,248,19,53,95,225,
56,72,216,115,149,164,247,2,6,10,30,34,102,170,229,52,92,
228,55,89,235,38,106,190,217,112,144,171,230,49,83,245,4,12,
20,60,68,204,79,209,104,184,211,110,178,205,76,212,103,169,
224,59,77,215,98,166,241,8,24,40,120,136,131,158,185,208,
107,189,220,127,129,152,179,206,73,219,118,154,181,196,87,249,
16,48,80,240,11,29,39,105,187,214,97,163,254,25,43,125,135,
146,173,236,47,113,147,174,233,32,96,160,251,22,58,78,210,
109,183,194,93,231,50,86,250,21,63,65,195,94,226,61,71,201,
64,192,91,237,44,116,156,191,218,117,159,186,213,100,172,
239,42,126,130,157,188,223,122,142,137,128,155,182,193,88,232,
35,101,175,234,37,111,177,200,67,197,84,252,31,33,99,165,
244,7,9,27,45,119,153,176,203,70,202,69,207,74,222,121,139,
134,145,168,227,62,66,198,81,243,14,18,54,90,238,41,123,141,
140,143,138,133,148,167,242,13,23,57,75,221,124,132,151,162,
253,28,36,108,180,199,82,246,1};
//S盒置换
const byte AES:: S_BOX[256]=
{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,};
//S盒逆置换;
const byte AES:: S_BOX_1[256]={
82,9,106,213,48,54,165,56,191,64,163,158,129,243,215,251,
124,227,57,130,155,47,255,135,52,142,67,68,196,222,233,203,
84,123,148,50,166,194,35,61,238,76,149,11,66,250,195,78,8,
46,161,102,40,217,36,178,118,91,162,73,109,139,209,37,114,
248,246,100,134,104,152,22,212,164,92,204,93,101,182,146,108,
112,72,80,253,237,185,218,94,21,70,87,167,141,157,132,144,
216,171,0,140,188,211,10,247,228,88,5,184,179,69,6,208,44,
30,143,202,63,15,2,193,175,189,3,1,19,138,107,58,145,17,65,
79,103,220,234,151,242,207,206,240,180,230,115,150,172,116,
34,231,173,53,133,226,249,55,232,28,117,223,110,71,241,26,
113,29,41,197,137,111,183,98,14,170,24,190,27,252,86,62,75,
198,210,121,32,154,219,192,254,120,205,90,244,31,221,168,51,
136,7,199,49,177,18,16,89,39,128,236,95,96,81,127,169,25,181,
74,13,45,229,122,159,147,201,156,239,160,224,59,77,174,42,
245,176,200,235,187,60,131,83,153,97,23,43,4,126,186,119,214,
38,225,105,20,99,85,33,12,125,};
const byte AES::Rcon[10][4]={
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};
AES::AES()
{
}
AES::~AES()
{
}
//功能: AES加密
//入口参数:m_Key是用户加密密钥;
// fp1是要加密的文件指针;
// fp2是加密后保存密文的文件指针;
// Nb是加密时明文的分组长度(以32bit为单位);
// Nk是密钥的长度(以32bit为单位);
long AES::AES_Encrypt(CString OpenPath,CString SavePath,CString m_Key,int Nb,int Nk)
{
int handle= open(OpenPath, O_CREAT); //得到要加密的文件的长度;
long Length=filelength(handle);
if(Length==0)return 0;
int leave=Length%(4*Nb); //求剩余的字块的字节数;
long rounds=Length/(4*Nb); //得到整块的加密轮数;
if(leave!=0)rounds++;
long copy_rounds=rounds;
FILE*fp1=fopen(OpenPath,"rb");//以二进制读的方式打开要加密的文件;
FILE*fp2=fopen(SavePath,"wb");//以二进制写的方式打开保存密文的文件;
byte state[4*8]; //作为加密时存放要加密的明文块;
byte copy[4*8]; //用来进行短块处理时的缓存区;
int Nr=GetRounds(Nb,Nk); //得到加密的轮数;
KeyExpansion(m_Key,Nb,Nk,Nr); //生成各轮子密钥;
if(copy_rounds==1&&rounds==1)
{
if(leave==0)fread(state,4,Nb,fp1);//明文的长度恰好等于分组长度;
else
{
fread(state,1,leave,fp1);//明文的长度小于八个字符;
for(int i=leave;i<4*Nb;i++)
state[i]=0; //后面用空格补齐;
}
Transform(state,Nb,Nr); //加密变换;
fwrite(state,4,Nb,fp2); //将加密后的密文块写入目标文件;
rounds--;
}
else if(copy_rounds>1&&leave!=0)//如果明文的长度大于分组长度且字符数不是分组长度的整数倍
{ //时,需要进行短块处理;
fread(state,4,Nb,fp1);
Transform(state,Nb,Nr); //先加密最前面的一块;
fwrite(state,1,leave,fp2);//仅将余数个字符存入文件,而将后部分密文
//与后面的明文合在一起加密;
int j=0;
for(int i=leave;i<4*Nb;i++)
copy[j++]=state[i];
fread(copy+j,1,leave,fp1);
Transform(copy,Nb,Nr);//将copy中的Nb个字节进行加密
fwrite(copy,4,Nb,fp2);//将加密后的明文写入文件;
rounds-=2;
}
while(rounds)//以下处理的明文是分组的整数倍的情况;
{
fread(state,4,Nb,fp1);//读取Nb个字节到state中
Transform(state,Nb,Nr);//将state中的Nb个字节进行加密;
fwrite(state,4,Nb,fp2);//将加密后的明文写入文件;
rounds--;
}
fclose(fp1);//关闭源文件和目标文件;
fclose(fp2);
return ((copy_rounds-1)*4*Nb+leave);//返回文件长度;
}
//功能: 实现AES的解密
//入口参数:m_Key是用户加密密钥;
// fp1是要解密的文件指针;
// fp2是解密后保存明文的文件指针;
// Nb是解密时密文的分组长度(以32bit为单位);
// Nk是密钥的长度(以32bit为单位);
//注意了, 解密时和加密时的分组长度要一致;
long AES::AES_DeEncrypt(CString OpenPath,CString SavePath,CString m_Key, int Nb, int Nk)
{
int handle= open(OpenPath, O_CREAT);
long Length=filelength(handle);
if(Length==0)return 0;
int leave=Length%(4*Nb);//求剩余的字块的字节数;
long rounds=Length/(4*Nb);//得到整块的加密轮数;
if(leave!=0)rounds++;
long copy_rounds=rounds;
FILE*fp1=fopen(OpenPath,"rb");//以二进制读的形式打开要解密的文件;
FILE*fp2=fopen(SavePath,"wb");//以二进制写的形式打开保存解密后明文的文件;
byte state[4*8]; //解密时存放密文块;
int Nr=GetRounds(Nb,Nk); //得到解密时循环轮数;
KeyExpansion(m_Key,Nb,Nk,Nr); //生成各轮子密钥
byte copy[32];
if(leave!=0)//需要进行短块处理
{
fread(copy,1,leave,fp1);//先把余数个密文字符保存;
fread(state,4,Nb,fp1);//读取紧接着的一个密文块;
ReTransform(state,Nb,Nr); 解密;
int j=0;
for(int i=leave;i<4*Nb;i++) //把解密后的明文和前面的余数个合在一起组成一块,
copy[i]=state[j++]; //一起解密;
ReTransform(copy,Nb,Nr);
fwrite(copy,4,Nb,fp2);//将解密后的明文写入目标文件;
fwrite(state+j,1,leave,fp2);//将余数个明文写入目标文件;
rounds-=2; //已经完成了两轮解密所以减二;
}
while(rounds)//对后面是分组长度的整数倍的密文块解密;
{
fread(state,4,Nb,fp1);//读取密文块;
ReTransform(state,Nb,Nr); //解密变换;
fwrite(state,4,Nb,fp2);//将解密后的明文写入目标文件;
rounds--; //轮数减一;
}
fclose(fp1);//关闭源文件和目标文件;
fclose(fp2);
return ((copy_rounds-1)*4*Nb+leave);//返回文件长度
}
//功能:将数组A中的四个字节循环左移一个字节;
void AES::RotWord(byte *A)
{
byte temp;
temp=*A;//将第一个先缓存到temp;
*A=*(A+1);//将第二个拷贝到第一个中;
*(A+1)=*(A+2);//将第三个拷贝到第二个;
*(A+2)=*(A+3);//将第四个拷贝到第三个;
*(A+3)=temp;//将存在temp中的内容拷贝到第四个中;
}
//功能: 密钥扩展的时候进行S盒替换;
//入口参数:A是存放四个字节的数组;
void AES::SubWord(byte *A)
{
for(int i=0;i<4;i++)
*(A+i)=S_BOX[*(A+i)];//查表得到结果;
}
// 功能:返回加密的轮数;
//入口参数:Nb以32bit为单位的待加密明文的长度;
// Nk是以32bit为单位的初始密钥的长度;
//出口参数:返回加密轮数(Nr);
int AES::GetRounds(int Nb, int Nk)
{
switch(Nb)
{//选行,可对照加密轮数矩阵;
case 4:switch(Nk)
{//行选定后选列;
case 4:return 10;
case 6:return 12;
case 8:return 14;
default:return 0;
}
case 6:switch(Nk)
{
case 4:
case 6:return 12;
case 8:return 14;
default:return 0;
}
case 8:switch(Nk)
{
case 4:
case 6:
case 8:return 14;
default:return 0;
}
default:return 0;
}
}
//入口参数:Nb以32bit为单位的待加密明文的长度;
// Nk是以32bit为单位的初始密钥的长度;
// Nr是加密的轮数;
// m_Key是用户的密钥;
//出口参数:扩展后的子密钥存放在数组w中;
void AES::KeyExpansion(CString m_Key,int Nb, int Nk, int Nr)
{
for(int i=0;i<4;i++)
for(int j=0;j<Nk;j++)
key[i][j]=m_Key[i*4+j];
i=0;
while(i<Nk)
{//将key中存放的密钥拷贝到w中
w[i][0]=key[i][0];
w[i][1]=key[i][1];
w[i][2]=key[i][2];
w[i][3]=key[i][3];
i++;
}
i=Nk;
while(i<Nb*(Nr+1))
{
byte temp[4];
temp[0]=w[(i-1)][0];temp[1]=w[(i-1)][1];
temp[2]=w[(i-1)][2];temp[3]=w[(i-1)][3];
if((i%Nk)==0)
{
RotWord(temp);
SubWord(temp);
for(int j=0;j<4;j++)
temp[j]^=Rcon[(i-1)/Nk][j];//与Rcon异或;
}
else if(Nk==8&&i%Nk==4)
SubWord(temp);
w[i][0]=w[(i-Nk)][0]^temp[0];
w[i][1]=w[(i-Nk)][1]^temp[1];
w[i][2]=w[(i-Nk)][2]^temp[2];
w[i][3]=w[(i-Nk)][3]^temp[3];
i++;
}
}
//功能: 实现对明文加密的时候得S盒置换
//入口参数:Nb为以32bit为单位的明文块的大小;
// state为明文块;
void AES::SubByte(byte *state,int Nb)
{
for(int i=0;i<4*Nb;i++)
*(state+i)=S_BOX[*(state+i)];//查表得到替换后的值;
}
//功能: 加密对明文块进行移位运算;
//入口参数:state是明文块;
// Nb是以32比特为单位的明文块的大小;
void AES::ShiftRows(byte *state, int Nb)
{
byte t[8];
for( int r=0;r<4;r++)
{
for(int c=0;c<Nb;c++)t[c]=*(state+Nb*r+(r+c)%Nb);
for(c=0;c<Nb;c++)
*(state+Nb*r+c)=t[c];
}
}
//功能:加密时对明文块进行列混合变换;
//入口参数:state是明文块;
// Nb是以32比特为单位的明文块的大小;
void AES::MixColumns(byte *state, int Nb)
{
byte t[4];
for( int c=0;c<Nb;c++)
{
for(int r=0;r<4;r++)t[r]=*(state+Nb*r+c);
for( r=0;r<4;r++)
*(state+Nb*r+c)=Ffmul(0x02,t[r])^Ffmul(0x03,t[(r+1)%4])
^t[(r+2)%4]^t[(r+3)%4];
}
}
//功能:返回两个域元素A,B的积;
byte AES::Ffmul(byte A, byte B)
{
//查对数表;
if(A==0||B==0)return 0;
A=Log[A];
B=Log[B];
A=(A+B)%0xff;
//查反对数表;
A=Log_1[A];
return A;
}
// 功能: 轮密钥加变换;
//入口参数: state明文块
// w为子密钥,Nb为明文块的大小,round为当前加密的轮数;
void AES::AddRoundKey(byte *state, int Nb,int round)
{
for(int c=0;c<Nb;c++,round++)
for(int r=0;r<4;r++)
*(state+r*Nb+c)=*(state+r*Nb+c)^w[round][r];
}
void AES::Transform(byte *state, int Nb,int Nr)
{
AddRoundKey(state,Nb,0);
for(int round=1;round<Nr;round++)
{
实现对明文加密的时候得S盒置换
ShiftRows(state,Nb);//移位变换
MixColumns(state,Nb);//列混合变换
AddRoundKey(state,Nb,round*Nb);//与轮变换密钥异或
}
SubByte(state,Nb);//末圈对明文加密的时候得S盒置换
ShiftRows(state,Nb);//移位变换
AddRoundKey(state,Nb,round*Nb);//与轮变换密钥异或
}
void AES::ReTransform(byte *state, int Nb,int Nr)
{
AddRoundKey(state, Nb,Nr*Nb);
for(int round=Nr-1;round>=1;round--)
{
InvShiftRows(state,Nb);//解密的移位变换
InvSubByte(state,Nb);//解密时的字节替换
AddRoundKey(state,Nb,round*Nb);//与轮密钥异或;
InvMixColumns(state,Nb);//列混合变换
}
InvShiftRows(state,Nb);//末圈移位变换
InvSubByte(state,Nb);//末圈的字节替换
AddRoundKey(state,Nb,0);//末圈与轮秘要异或;
}
//功能:解密时的S盒逆置换;
//入口参数:state为密文块;
// Nb为密文块的大小;
void AES::InvSubByte(byte *state, int Nb)
{
for(int i=0;i<4*Nb;i++)
*(state+i)=S_BOX_1[*(state+i)];
}
//解密的时候的右移位变换;
//入口参数:state为密文块;
// Nb为密文块的大小;
void AES::InvShiftRows(byte *state, int Nb)
{
byte t[8];
for( int r=0;r<4;r++)
{
for(int c=0;c<Nb;c++)
t[(c+r)%Nb]=*(state+r*Nb+c);
for(c=0;c<Nb;c++)
*(state+r*Nb+c)=t[c];
}
}
//功能:解密时的列混合变换;
//入口参数:state为密文块;
// Nb为密文块的大小;
void AES::InvMixColumns(byte *state, int Nb)
{
byte t[4];
for( int c=0;c<Nb;c++)
{
for(int r=0;r<4;r++)t[r]=*(state+Nb*r+c);
for( r=0;r<4;r++)
{
*(state+Nb*r+c)=Ffmul(0x0e,t[r])^Ffmul(0x0b,t[(r+1)%4])
^Ffmul(0x0d,t[(r+2)%4])^Ffmul(0x09,t[(r+3)%4]);
}
}
}
以下为事件处理部分的代码:
void CAESDlg::OnButtonOpenFile()
{
UpdateData(true);//得到编辑框中的数据
//打开文件对话框;
CFileDialog fileselect(TRUE,NULL,NULL,OFN_HIDEREADONLY|
OFN_OVERWRITEPROMPT,"*.txt||",NULL);
fileselect.DoModal();//选择文件对话框;
m_OpenPath=fileselect.GetPathName(); //得到文件的长度;
UpdateData(false);
}
void CAESDlg::OnButtonSaveFile()
{
// TODO: Add your control notification handler code here
UpdateData(true);
CFileDialog fileselect(FALSE,NULL,NULL,OFN_HIDEREADONLY|
OFN_OVERWRITEPROMPT,"*.txt||",NULL);
fileselect.DoModal();
m_SavePath=fileselect.GetPathName();
UpdateData(false);
}
void CAESDlg::OnButtonAES_Encrypt()
{
// TODO: Add your control notification handler code here
UpdateData(true);
CTime Time1 = CTime::GetCurrentTime() ;//得到加密开始的时间;
int t_min1=Time1.GetMinute();
int t_sec1=Time1.GetSecond();
m_BlockSize=4+m_BlockSize*2;
m_KeySize=4+m_KeySize*2;
if(m_Key.GetLength()!=m_KeySize*4)
{
MessageBox("请确认你输入的密钥长度是否和选择的一致!");
return;
}
MessageBox("开始加密了!");
UpdateData(false);
AES p;
long Length=p.AES_Encrypt(m_OpenPath,m_SavePath,m_Key,m_BlockSize,m_KeySize);
m_FileLength=Length/1000.0;
CTime Time2 = CTime::GetCurrentTime() ;//得到加密结束的时间;
int t_min2=Time2.GetMinute();
int t_sec2=Time2.GetSecond();
m_time=60*(t_min2-t_min1)+(t_sec2-t_sec1);//计算加密用的时间;
UpdateData(false);
MessageBox("哈哈,加密成功了!");
}
void CAESDlg::OnButtonAES_Deencrypt()
{
// TODO: Add your control notification handler code here
UpdateData(true);
CTime Time1 = CTime::GetCurrentTime() ;//得到解密开始的时间;
int t_min1=Time1.GetMinute();
int t_sec1=Time1.GetSecond();
m_BlockSize=4+m_BlockSize*2;
m_KeySize=4+m_KeySize*2;
if(m_Key.GetLength()!=m_KeySize*4)
{
MessageBox("请确认你输入的密钥长度是否和选择的一致!");
return;
}
MessageBox("开始解密了!");
AES p;
long Length=p.AES_DeEncrypt(m_OpenPath,m_SavePath,m_Key,m_BlockSize,m_KeySize);
m_FileLength=Length/1000.0;
CTime Time2 = CTime::GetCurrentTime() ;//得到解密结束的时间;
int t_min2=Time2.GetMinute();
int t_sec2=Time2.GetSecond();
m_time=60*(t_min2-t_min1)+(t_sec2-t_sec1);//计算解密用的时间;
UpdateData(false);
MessageBox("哈哈,解密成功了!如果解密后有问题,请确认你是不是输错密钥了!");
}
void CAESDlg::OnButtonClear()
{
// TODO: Add your control notification handler code here
m_Key.Empty();
m_OpenPath.Empty();
m_SavePath.Empty();
UpdateData(false);
}
试验总结
这两个密码算法的结构性很好,而且加密和解密具有很多相似的地方,这使得它们的实现变得十分容易。因此,实现DES和AES我只用了一天时间,不过都是整整的一天一晚上。
在实现DES的时候,由于涉及到位操作,我一开始打算用汇编和C++来嵌套编写的,但是发现这样一来程序很难看,而且汇编做图形界面对我来说很困难。后来我想用bitset这个类来实现位运算,但是感觉很麻烦,而且不划算,后来我用bool型的数组来存储要加密的明文,用数组来转换,这样容易多了。而且占用的空间也多不了多少,几个bit而已嘛。速度估计也不会比用C++的类库里面的函数慢。
相比之下AES的实现要容易一些,因为AES虽然算法中涉及到了很多数学知识,但是我采用了查表的方式解决了一些复杂数学运算,而且大大的提高了加解密的速度。
这两个程序中的一个麻烦的地方是当文件的长度不是分组长度的整数倍时的断块处理。一开始我采用的是在文件的尾部进行短块处理,采用叠加加密的方式解决了这一问题,但是后来又改在前面进行短块处理,因为我发现这样程序可以减少一个分支,速度会稍快。
在程序的优化过程当中,我对程序的结构进行了较大的调整,有时候为了让程序更加明了,我花了更多的代码,而不是在循环里面套循环。在DES中为了减少程序的分支,我不得不把加密和解密分开来写,虽然多花了几十行代码,但是我发现程序更加清晰了,效率当然要高一些。以前我总是喜欢把代码写的很简短,认为这样很牛,而且很少加注释,以至于现在自己看以前写的程序都很难看懂。看了软件工程后,我知道了程序中注释的重要性。
在这两个程序设计的过程中我考虑了一些很重要的问题:
1> 用户界面的交互性要强,界面要漂亮,为此我修改了好多次界面。
2> 程序的模块化程度要很好,要便于移植。这样我把这两个加密程序的事项部分全部封装在两个类中,相当于两个黑盒子。而用户只需要向这个黑盒子输入要求的参数,如文件路径和保存路径,用户密钥分组长度密钥长度等信息。而不需要考虑其具体实现过程。
3> 程序要符合软件工程的基本要求,因此我给出了比较详细的注释。
4> 在减少空间消耗的同时尽量提高程序的运行效率。有人总是喜欢把文件一次读到字符数组里面,这样占用了太大的内存空间,而且对于较大的文件是无能为力的,我则是边读取边加密,这样大大节省了空间。一开始写出来的时候,DES加密的速度大概是100kb/s,AES200kb/s,但是我在网上找到了一位武汉大学一个同学写的AES他的加密速度是2-3Mb/s(注:我的电脑CPU赛扬1.3GHz,主存256兆……)。这让我大感震惊,为什么同样是查表的方实现的,速度相差既然这么大呢?我知道我的程序还有很多地方需要改进。于是我开始对代码进行了优化。首先是要知道从那些地方去优化,我想了想,当然应该是运算次数高的代码了。这样我就先对IO进行了优化,这样一来,速度竟然提高了一倍。这让我兴奋不已。也增强了我的信心。后来我又尽量采用高速的运算符,减少参数调用,减少程序的分支结构,这样下来我的两个程序的速度都有了较大的提高。目前为止,DES的速度是500-600Kb/S,AES是600Kb/S左右(电脑不运行别的大程序时),比DES稍快,但是和别人的相比还是有较大的差距。
只可惜我没有那位大哥的源代码,给他发邮件他回了但是给我的还是可执行文件。根据他的建议我将小表换成大表,速度提高了%10左右。但是后来想了很多办法速度都没有什么提高。