探索黑客技术攻防,实战研究与安全创新

导航菜单

MD5算法设计与编程实现

加密技术与我们的日常生活息息相关,在信息社会更是凸显重要。本文将主要就MD5算法及密码学算法实现做一些相关探讨。MD5的全称是message-digestalgorithm5(信息-摘要算法)。由于其使用不需要支付任何版权费用,安全性好,所以MD5成为了当今非常流行的优秀的典型Hash加密技术。为了使自己能够对MD5加深理解,本文将利用VC++编程实现MD5加密过程,在软件实现上,提供了友好的界面,能够实现字符串和文件加密。

算法描述

算法输入一个字节串,每个字节8个bit,算法的执行分为以下几个步骤:

第一步,补位。

MD5算法先对输入的数据进行补位,使得数据的长度(以byte为单位)对64求余的结果是56。即数据扩展至LEN=K*64+56个字节,K为整数。

补位方法:补一个1,然后补0至满足上述要求。相当于补一个0x80的字节,再补值为0的字节。这一步总共补充的字节数为0~63个。

第二步,附加数据长度。

用一个64位的整数表示数据的原始长度(以bit为单位),将这个数字的8个字节按低位在前高位在后的顺序附加在补位后的数据后面。此时,数据被填补后的总长度为:LEN=K*64+56+8=(K+1)*64Bytes。注意,64位整数是输入数据的原始长度,而不是填充字节后的长度。

第三步,初始化MD5参数。

有4个32位整数变量(A、B、C、D)用来计算信息摘要,每一个变量被初始化成以下以十六进制数表示的数值,低位字节在前面。

wordA:01234567

wordB:89abcdef

wordC:fedcba98

wordD:76543210

注意,低位字节在前面指的是LittleEndian平台上内存中字节的排列方式,而在程序中书写时,要写成。

A=0x67452301

B=0xefcdab89

C=0x98badcfe

D=0x10325476

第四步,定义4个MD5基本的按位操作函数。

X、Y、Z为32位整数。

F(X,Y,Z)=(XandY)or(not(X)andZ)

G(X,Y,Z)=(XandZ)or(Yandnot(Z))

H(X,Y,Z)=XxorYxorZ

I(X,Y,Z)=Yxor(Xornot(Z))

再定义4个分别用于四轮变换的函数。

设Mj表示消息的第j个子分组(从0到15),s表示循环左移s位,则四种操作为:

FF(a,b,c,d,Mj,s,ti)表示a=b+((a+(F(b,c,d)+Mj+ti)s)

GG(a,b,c,d,Mj,s,ti)表示a=b+((a+(G(b,c,d)+Mj+ti)s)

HH(a,b,c,d,Mj,s,ti)表示a=b+((a+(H(b,c,d)+Mj+ti)s)

II(a,b,c,d,Mj,s,ti)表示a=b+((a+(I(b,c,d)+Mj+ti)s)

第五步,对输入数据作变换。

处理数据,N是总的字节数,以64个字节为一组,每组作一次循环,每次循环进行四轮操作。要变换的64个字节用16个32位的整数数组M[0...15]表示,而数组T[1...64]表示一组常数,T[i]为4294967296*abs(sin(i))的32位整数部分,i的单位是弧度,i的取值从1到64。

设计任务

在设计任务中,加密软件具有以下功能:

A:加密软件具有良好的用户界面;

B:能够对字符串进行加密;

C:能够对文件进行加密;

D:对于文件加密大于10M的文件能够显示进度条;

F:在没有选择加密类型时,必须先选择才能进行加密;

H:界面要简练,输出输入表示要明确,尽量做到通俗易懂。

部分代码说明

本程序主要由ZMD5.cpp和MyMD5Dlg.cpp文件构成,前者是MD5算法的实现,后者用于界面的控制和文件选择。以下是这两个文件的关键代码。

1.ZMD5.cpp关键代码


unsignedintZMD5::ROTATE_LEFT(unsignedintx,unsignedintn)
{
return(((x)(n))|((x)(32-(n))));
}
unsignedintZMD5::F(unsignedintx,unsignedinty,unsignedintz)
{
return((xy)|((~x)z));
}
unsignedintZMD5::G(unsignedintx,unsignedinty,unsignedintz)
{
return((xz)|(y(~z)));
}
unsignedintZMD5::H(unsignedintx,unsignedinty,unsignedintz)
{
returnx^y^z;
}
unsignedintZMD5::I(unsignedintx,unsignedinty,unsignedintz)
{
return(y^(x|(~z)));
}
voidZMD5::FF(unsignedinta,unsignedintb,unsignedintc,unsignedint
d,unsignedintx,ints,unsignedintac)
{
(a)+=F((b),(c),(d))+(x)+(ac);
(a)=ROTATE_LEFT((a),(s));
(a)+=(b);
}
voidZMD5::GG(unsignedinta,unsignedintb,unsignedintc,unsignedint
d,unsignedintx,ints,unsignedintac)
{
(a)+=G((b),(c),(d))+(x)+(ac);
(a)=ROTATE_LEFT((a),(s));
(a)+=(b);
}
voidZMD5::HH(unsignedinta,unsignedintb,unsignedintc,unsignedint
d,unsignedintx,ints,unsignedintac)
{
}
(a)+=H((b),(c),(d))+(x)+(ac);
(a)=ROTATE_LEFT((a),(s));
(a)+=(b);
voidZMD5::II(unsignedinta,unsignedintb,unsignedintc,unsignedint
d,unsignedintx,ints,unsignedintac)
{
(a)+=I((b),(c),(d))+(x)+(ac);
(a)=ROTATE_LEFT((a),(s));
(a)+=(b);
}
voidZMD5::Init()
{
S11=7;S21=5;S31=4;S41=6;
S12=12;S22=9;S32=11;S42=10;
S13=17;S23=14;S33=16;S43=15;
S14=22;S24=20;S34=23;S44=21;
A=0x67452301;//inmemory,thisis0x01234567
B=0xEFCDAB89;//inmemory,thisis0x89ABCDEF
C=0x98BADCFE;//inmemory,thisis0xFEDCBA98
D=0x10325476;//inmemory,thisis0x76543210
}
voidZMD5::Append(unsignedintMsgLen)
{
//计算要补位的字节数
intm=MsgLen%64;
if(m==0)
m_AppendByte=56;
elseif(m56)
m_AppendByte=56-m;
else
m_AppendByte=64-m+56;
//截取传入长度的高十六位和低十六位
inthWord=(MsgLen0xFFFF0000)16;
intlWord=MsgLen0x0000FFFF;
//将低十六位和高十六位分别乘以八(1byte=8bit)
inthDiv=hWord*8;
intlDiv=lWord*8;
m_MsgLen[0]=lDiv0xFF;
m_MsgLen[1]=(lDiv8)0xFF;
m_MsgLen[2]=((lDiv16)0xFF)|(hDiv0xFF);
m_MsgLen[3]=(hDiv8)0xFF;
m_MsgLen[4]=(hDiv16)0xFF;
m_MsgLen[5]=(hDiv24)0xFF;
m_MsgLen[6]=0;
m_MsgLen[7]=0;
}
voidZMD5::Transform(unsignedcharBlock[64])
{
//将64字节位转换为16个字节
unsignedlongx[16];
for(inti=0,j=0;j64;i++,j+=4)
x[i]=Block[j]|Block[j+1]8|Block[j+2]16|Block[j+3]24;
//初始化临时寄存器变量
unsignedinta,b,c,d;
a=A;b=B;c=C;d=D;
//第一轮计算
FF(a,b,c,d,x[0],S11,0xD76AA478);//1
FF(d,a,b,c,x[1],S12,0xE8C7B756);//2
FF(c,d,a,b,x[2],S13,0x242070DB);//3
FF(b,c,d,a,x[3],S14,0xC1BDCEEE);//4
FF(a,b,c,d,x[4],S11,0xF57C0FAF);//5
FF(d,a,b,c,x[5],S12,0x4787C62A);//6
FF(c,d,a,b,x[6],S13,0xA8304613);//7
FF(b,c,d,a,x[7],S14,0xFD469501);//8
FF(a,b,c,d,x[8],S11,0x698098D8);//9
FF(d,a,b,c,x[9],S12,0x8B44F7AF);//10
FF(c,d,a,b,x[10],S13,0xFFFF5BB1);//11
FF(b,c,d,a,x[11],S14,0x895CD7BE);//12
FF(a,b,c,d,x[12],S11,0x6B901122);//13
FF(d,a,b,c,x[13],S12,0xFD987193);//14
FF(c,d,a,b,x[14],S13,0xA679438E);//15
FF(b,c,d,a,x[15],S14,0x49B40821);//16
//第二轮计算
GG(a,b,c,d,x[1],S21,0xF61E2562);//17
GG(d,a,b,c,x[6],S22,0xC040B340);//18
GG(c,d,a,b,x[11],S23,0x265E5A51);//19
GG(b,c,d,a,x[0],S24,0xE9B6C7AA);//20
GG(a,b,c,d,x[5],S21,0xD62F105D);//21
GG(d,a,b,c,x[10],S22,0x2441453);//22
GG(c,d,a,b,x[15],S23,0xD8A1E681);//23
GG(b,c,d,a,x[4],S24,0xE7D3FBC8);//24
GG(a,b,c,d,x[9],S21,0x21E1CDE6);//25
GG(d,a,b,c,x[14],S22,0xC33707D6);//26
GG(c,d,a,b,x[3],S23,0xF4D50D87);//27
GG(b,c,d,a,x[8],S24,0x455A14ED);//28
GG(a,b,c,d,x[13],S21,0xA9E3E905);//29
GG(d,a,b,c,x[2],S22,0xFCEFA3F8);//30
GG(c,d,a,b,x[7],S23,0x676F02D9);//31
GG(b,c,d,a,x[12],S24,0x8D2A4C8A);//32
//第三轮计算
HH(a,b,c,d,x[5],S31,0xFFFA3942);//33
HH(d,a,b,c,x[8],S32,0x8771F681);//34
HH(c,d,a,b,x[11],S33,0x6D9D6122);//35
HH(b,c,d,a,x[14],S34,0xFDE5380C);//36
HH(a,b,c,d,x[1],S31,0xA4BEEA44);//37
HH(d,a,b,c,x[4],S32,0x4BDECFA9);//38
HH(c,d,a,b,x[7],S33,0xF6BB4B60);//39
HH(b,c,d,a,x[10],S34,0xBEBFBC70);//40
HH(a,b,c,d,x[13],S31,0x289B7EC6);//41
HH(d,a,b,c,x[0],S32,0xEAA127FA);//42
HH(c,d,a,b,x[3],S33,0xD4EF3085);//43
HH(b,c,d,a,x[6],S34,0x4881D05);//44
HH(a,b,c,d,x[9],S31,0xD9D4D039);//45
HH(d,a,b,c,x[12],S32,0xE6DB99E5);//46
HH(c,d,a,b,x[15],S33,0x1FA27CF8);//47
HH(b,c,d,a,x[2],S34,0xC4AC5665);//48
//第四轮计算
II(a,b,c,d,x[0],S41,0xF4292244);//49
II(d,a,b,c,x[7],S42,0x432AFF97);//50
II(c,d,a,b,x[14],S43,0xAB9423A7);//51
II(b,c,d,a,x[5],S44,0xFC93A039);//52
II(a,b,c,d,x[12],S41,0x655B59C3);//53
II(d,a,b,c,x[3],S42,0x8F0CCC92);//54
II(c,d,a,b,x[10],S43,0xFFEFF47D);//55
II(b,c,d,a,x[1],S44,0x85845DD1);//56
II(a,b,c,d,x[8],S41,0x6FA87E4F);//57
II(d,a,b,c,x[15],S42,0xFE2CE6E0);//58
II(c,d,a,b,x[6],S43,0xA3014314);//59
II(b,c,d,a,x[13],S44,0x4E0811A1);//60
II(a,b,c,d,x[4],S41,0xF7537E82);//61
II(d,a,b,c,x[11],S42,0xBD3AF235);//62
II(c,d,a,b,x[2],S43,0x2AD7D2BB);//63
II(b,c,d,a,x[9],S44,0xEB86D391);//64
//保存当前寄存器结果
A+=a;B+=b;C+=c;D+=d;
}
stringZMD5::ToHex(boolUpperCase)
{
stringstrResult;
intResultArray[4]={A,B,C,D};
charBuf[33]={0};
for(inti=0;i4;i++)
{
memset(Buf,0,3);
sprintf(Buf,%02x,ResultArray[i]0x00FF);
strResult+=Buf;
memset(Buf,0,3);
sprintf(Buf,%02x,(ResultArray[i]8)0x00FF);
strResult+=Buf;
memset(Buf,0,3);
sprintf(Buf,%02x,(ResultArray[i]16)0x00FF);
strResult+=Buf;
memset(Buf,0,3);
sprintf(Buf,%02x,(ResultArray[i]24)0x00FF);
strResult+=Buf;
}
if(UpperCase)CharUpper((char*)strResult.c_str());
returnstrResult;
}
stringZMD5::GetMD5OfString(stringInputMessage,boolUpperCase)
{
//初始化MD5所需常量
Init();
//计算追加长度
Append(InputMessage.length());
//对原始信息进行补位
for(inti=0;im_AppendByte;i++)
{
if(i==0)InputMessage+=(unsignedchar)0x80;
elseInputMessage+=(unsignedchar)0x0;
}
//将原始信息长度附加在补位后的数据后面
for(inti=0;i8;i++)InputMessage+=m_MsgLen[i];
//位块数组
unsignedcharx[64]={0};
//循环,将原始信息以64字节为一组拆分进行处理
for(inti=0,Index=-1;iInputMessage.length();i++)
{
x[++Index]=InputMessage[i];
if(Index==63)
{
Index=-1;
Transform(x);
}
}
returnToHex(UpperCase);
}
stringZMD5::GetMD5OfFile(conststringFileName,boolUpperCase)
{
//定义读取文件的缓冲区
char*ReadBuf=newchar[FILE_BUFFER_READ+1];
memset(ReadBuf,0,FILE_BUFFER_READ);
try
{
//检查文件是否存在
if((_access(FileName.c_str(),0))==-1)return;
//二进制方式读取文件
if(m_pFile=fopen(FileName.c_str(),rb),m_pFile==NULL)return;
m_FileOpen=true;
//获取文件大小
unsignedlongFileSize=0xFFFF;
WIN32_FIND_DATAwin32_find_data;
HANDLEhFile;
if((hFile=FindFirstFile(FileName.c_str(),win32_find_data))!=INVALID_HANDLE
_VALUE)
if(hFile==NULL)return;
if(FileSize=win32_find_data.nFileSizeLow,FileSize==0xFFFF
||
FileSize==0)return;
FindClose(hFile);
//初始化MD5所需常量
Init();
//通过文件长度计算追加长度
Append(FileSize);
//位块数组
unsignedcharx[64]={0};
//本次读取字节数
intReadSize=fread(ReadBuf,1,FILE_BUFFER_READ,m_pFile);
//读取次数
intReadCount=0;
while(ReadSize==FILE_BUFFER_READ)
{
/*


如果用户开启了另一个线程调用此函数,则允许用户从外部结束此函数。

为安全起见,没有在这个类的内部开启线程,可以最大限度的保证文件安全关闭。


*/
if(!m_FileOpen)
{
}
fclose(m_pFile);
return;
//将处理进度返回给用户
ReadCount++;
OnProcessing((int)(FILE_BUFFER_READ*ReadCount/(FileSize/
100)));
//将原始信息以64字节为一组拆分进行处理
for(inti=0,Index=-1;iFILE_BUFFER_READ;i++)
{
x[++Index]=ReadBuf[i];
if(Index==63)
{
Index=-1;
Transform(x);
}
}
memset(ReadBuf,0,FILE_BUFFER_READ);//重置缓冲区
ReadSize=fread(ReadBuf,1,FILE_BUFFER_READ,m_pFile);
}//endwhile
/*处理不能被整除的剩余部分数据,此时要对剩余部分数据进行补位及长原始信息长度追加。如果最后一次读取数据的长度为零,说明文件已被读完,则直接将补位数据及原信息长度送入Transform处理。*/
if(ReadSize==0)
{
stringstrData;
for(inti=0;im_AppendByte;i++)
{
if(i==0)strData+=(unsignedchar)0x80;
elsestrData+=(unsignedchar)0x0;
}
for(inti=0;i8;i++)strData+=m_MsgLen[i];
for(inti=0,Index=-1;istrData.length();i++)
{
x[++Index]=strData[i];
if(Index==63)
{
Index=-1;
Transform(x);
}
}
}
else//将剩余数据处理完再补位
{
for(inti=0,Index=-1;iReadSize+m_AppendByte+8;i++)
{
//将原始信息以64字节为一组,进行拆分处理
if(iReadSize)
x[++Index]=ReadBuf[i];
elseif(i==ReadSize)
x[++Index]=(unsignedchar)0x80;
elseif(iReadSize+m_AppendByte)
x[++Index]=(unsignedchar)0x0;
elseif(i==ReadSize+m_AppendByte)
x[++Index]=m_MsgLen[0];
elseif(i==ReadSize+m_AppendByte+1)
x[++Index]=m_MsgLen[1];
elseif(i==ReadSize+m_AppendByte+2)
x[++Index]=m_MsgLen[2];
elseif(i==ReadSize+m_AppendByte+3)
x[++Index]=m_MsgLen[3];
elseif(i==ReadSize+m_AppendByte+4)
x[++Index]=m_MsgLen[4];
elseif(i==ReadSize+m_AppendByte+5)
x[++Index]=m_MsgLen[5];
elseif(i==ReadSize+m_AppendByte+6)
x[++Index]=m_MsgLen[6];
elseif(i==ReadSize+m_AppendByte+7)
x[++Index]=m_MsgLen[7];
if(Index==63)
{
Index=-1;
Transform(x);
}
}
}
OnProcessing(100);//处理进度百分之百
//关闭文件
fclose(m_pFile);
m_FileOpen=false;//文件打开状态为false
delete[]ReadBuf;//释放动态申请的内存
}
catch(...)
{
if(m_FileOpen)
fclose(m_pFile);
//关闭文件
m_FileOpen=false;//文件打开状态为false
delete[]ReadBuf;//释放动态申请的内存
return;
returnToHex(UpperCase);
}
}
voidZMD5::GetMD5OfFile_Terminate()
{
if(m_FileOpen)m_FileOpen=false;
}
2.MyMD5Dlg.cpp关键代码
voidCMyMD5Dlg::OnBtnCalculation()
{
//取“计算”按钮文字,如果上面的文字为“停止”,则停止当前信息的MD5计算CStringstrButton;
intselected;
selected=GetCheckedRadioButton(IDC_RADIO_1,IDC_RADIO_2);
GetDlgItemText(IDC_BTN_CALCULATION,strButton);
if(strButton==停止)
{
SetDlgItemText(IDC_EDIT_RESULT,);
md5.GetMD5OfFile_Terminate();
SetDlgItemText(IDC_BTN_CALCULATION,计算);
((CProgressCtrl*)GetDlgItem(IDC_PROGRESS))-ShowWindow(SW_HIDE);
return;
}
//清空结果文本框
SetDlgItemText(IDC_EDIT_RESULT,);
//获取待计算MD5的文件数据
CStringstrInput;
GetDlgItemText(IDC_EDIT_SOURCE,strInput);
//获取待计算MD5的字符串数据
CStringstrInput2;
GetDlgItemText(IDC_EDIT_SOURCE2,strInput2);
//如果原信息类型为字符串
if(selected==IDC_RADIO_1)
{
SetDlgItemText(IDC_EDIT_RESULT,
md5.GetMD5OfString((LPSTR)(LPCTSTR)strInput2,true).c_str());
}
//如果原信息类型为文件
elseif(selected==IDC_RADIO_2)
{
//如果文件为空,显示选择文件对话框
if(m_File.empty())
{
OnBtnSelect();
return;
}
//检查文件是否存在
elseif((_access((char*)(LPCTSTR)strInput,0))==-1)
{
AfxMessageBox(文件不存在!);
return;
}
else
{
//计算文件大小
unsignedlongFileSize=0xFFFFFFFF;
WIN32_FIND_DATAwin32_find_data;
HANDLEhFile;
if((hFile=FindFirstFile((char*)(LPCTSTR)strInput,win32_find_data))!=INVALI
D_HANDLE_VALUE)
{
FindClose(hFile);
if(FileSize=win32_find_data.nFileSizeLow,FileSize==0xFFFFFFFF||FileSize==0)
return;
}
//文件大于等于10M,显示处理进度条
if(FileSize=10485760)
{
//显示处理进度条
m_Processing.SetPos(0);
((CProgressCtrl*)GetDlgItem(IDC_PROGRESS))-ShowWindow(SW_SHOW);
//将“计算”按钮上的文字变成“停止”
SetDlgItemText(IDC_BTN_CALCULATION,停止);
//开启一个线程用于计算MD5,以免造成假死现像
hThread=CreateThread(NULL,0,
(LPTHREAD_START_ROUTINE)m_thunk.CallBack(this,
CMyMD5Dlg::ProcessingThread,ZThunk::THISCALL),
0,0,dwThreadId);
}
//文件小于10M,直接进行MD5计算,不开启线程,因为计算时间很短
else
{
}
SetDlgItemText(IDC_EDIT_RESULT,
md5.GetMD5OfFile((LPSTR)(LPCTSTR)strInput,true).c_str());
}
}
else
AfxMessageBox(请选择输入类型,“字符串”或“文件”!);
}
voidCMyMD5Dlg::OnBtnAbout()
{
CAboutDlgdlg;
dlg.DoModal();
}
//处理WM_RETURN(回车)和WM_VK_ESCAPE(取消)
BOOLCMyMD5Dlg::PreTranslateMessage(MSG*pMsg)
{
if(pMsg-message==WM_KEYDOWN)
{
switch(pMsg-wParam)
{
caseVK_ESCAPE:
returnTRUE;
caseVK_RETURN:
returnTRUE;
default:
break;
}
}
returnCDialog::PreTranslateMessage(pMsg);
}
DWORDCMyMD5Dlg::ProcessingThread(LPVOIDlpParameter)
{
CStringstrInput;
GetDlgItemText(IDC_EDIT_SOURCE,strInput);
SetDlgItemText(IDC_EDIT_RESULT,
md5.GetMD5OfFile((LPSTR)(LPCTSTR)strInput,true).c_str());
return1;
}
LRESULTCMyMD5Dlg::OnProcessing(WPARAMwParam,LPARAMlParam)
{
m_Processing.SetPos((int)wParam);
if((int)wParam==100)
{
}
//隐藏CProcessCtrl控件
((CProgressCtrl*)GetDlgItem(IDC_PROGRESS))-ShowWindow(SW_HIDE);
//将“停止”按钮上的文字变成“计算”
SetDlgItemText(IDC_BTN_CALCULATION,计算);
return1;
}
//选择字符串加密功能
1voidCMyMD5Dlg::OnRadio_1()
{
//TODO:Addyourcontrolnotificationhandlercodehere
UpdateData(TRUE);
GetDlgItem(IDC_OPEN)-EnableWindow(false);
GetDlgItem(IDC_EDIT_SOURCE)-EnableWindow(false);
GetDlgItem(IDC_EDIT_SOURCE2)-EnableWindow(true);
SetDlgItemText(IDC_EDIT_SOURCE2,);
}
//选择文件加密功能
voidCMyMD5Dlg::OnRadio_2()
{
//TODO:Addyourcontrolnotificationhandlercodehere
UpdateData(TRUE);
GetDlgItem(IDC_OPEN)-EnableWindow(true);
GetDlgItem(IDC_EDIT_SOURCE)-EnableWindow(true);
GetDlgItem(IDC_EDIT_SOURCE2)-EnableWindow(false);
SetDlgItemText(IDC_EDIT_SOURCE,);
}
//文件打开按钮
voidCMyMD5Dlg::OnOpen()
{
//TODO:Addyourcontrolnotificationhandlercodehere
ZFileDialogfileDlg;
dequestringdqSelectFile=fileDlg.GetOpenFileName(FALSE,所有文件
(*.*)\0*.*\0\0);
if(dqSelectFile.size()!=0)
{
m_File=dqSelectFile[0];
SetDlgItemText(IDC_EDIT_SOURCE,m_File.c_str());
}
}


系统运行说明

本文件不需要安装,直接运行文件夹里面的MyMD5.exe文件,就会打开如图1所示的界面,新手可以先看说明,如果对本程序已经了解,可以直接运行程序。接下来要做的就是选择输入的类型,当选择文件加密并且大小大于10M的时候,会出现如图2所示的界面(其他两种:如字符串和文件小于10M的请自己体验),最终的计算结果如图3所示。

020.png

021.png

022.png

系统测试

1.正确性测试

1)字符串计算

输入字符串:123456789

计算结果为:25F9E794323B453885F5181F1B624D0B

解密结果:123456789

证明本程序能够正确的计算字符串的摘要。

2)文件计算

在记事本中输入字符串“123456789”,保存为:“1.txt”。

计算该文件结果为:25F9E794323B453885F5181F1B624D0B。

2.性能测试

本程序能较快的完成摘要计算功能,对100M大小的文件,计算时间大约为3秒钟,对系统资源占用较小。