摘 要:本文通过对微软基础类MFC基本框架的深入分析,利用文档类与视类之间交换数据的渠道,最大限度地利用了MFC为我们提供的功能,用一种简洁的方法实现了加密编辑器,同时对于广大使用MFC编程的人来说也有很好的借鉴作用。
关键词:微软 MFC 加密 编辑器
一、自己编写的的必要性
现代社会大量的事物都由计算机来处理,社会财富和机密已经越来越集中到计算机中,安全就显得尤为重要,对于广大使用个人电脑的人来说在计算机中一般也存有自己的秘密(日记等)。虽然现在的编辑软件都具有加密功能,如WPS、WORD,但是这些软件过于庞大,加密效果也不见得好,如WORD对大量连续明文加密效果不好,WPS的DOS版竟然还留有后门,加密功能如同虚设,WINDOWS版可能也存在。因此我们使用自己的加密算法,编写自己的加密编辑器还是很有必要的。
二、更好的安全性
对于一个加密产品而言它有三个要素:算法,加密密钥,解密密钥。要解密的人知道这三个因素越多,解密就越容易,如果自己编写加密产品,则三个因素都可以做到是保密的。这对破译者无疑增加了很大的难度。本文主要论述使用MFC实现的方法,加加密算法只是作了很简单的异或运算,读者可以自行修改加入自己的加密算法(最好是基于随机数的,以对付字符频率的分析)。
三、实现原理
但是完全依靠自己的力量编写一个编辑器谈何容易,要做的工作实在是太多了,好在有了微软的MFC类,可以为我们完成一些基本工作,只要我们深入理解MFC有关类的相互关系和作用(特别是数据的串行化),灵活应用,就可以很快地编写一个自己的编辑器了。
我们使用VC的Application Wizard生成的应用程序框主要是由视类(CView)和文档类(CDocument)构成的。视类主要用于显示数据和实现与用户之间的交互,而文档类则用于对数据的处理,文档处理的数据和用户的输入都是通过数据的串行化(serialize)来实现视类与文档信息双向交流的。其中视类又有很多子类以适应不同的应用需要,通常默认的选择为CView,这里我们应该选择其衍生类CEditView来构成我们的视类,此类封装了很多编辑器需要的功能,如查找、块操作、UNDO等。这样我们的程序就已经是一个编辑器了。
有了基本的程序框架后,关键就是如何将自己的加密算法无缝地嵌入到此框架中去,这里我们必须理解Serialize的功能,它是一个对存储介质进行读写的进程,包含在CObject类中,而MFC几乎所有的类均是从CObject类中继承而来的,因此绝大部的MFC类都支持Serialize过程。Serialize过程对类CArchive进行操作,而CArchive类又都有一个CFile对象,通过串行化,就将要操作的数据抽象化,只需要对CArchive类进行操作而不必须关心被串行化对象的具体特征。CArchive类不但可以对字符进行操作而且还可以对二进制数据进行操作。因此Serialize是文档类与视类之间联系的纽带,我们可以将自己对数据进行加密处理的功能加到文档类中,再将处理后的数据封装在一个CArchive类中通过Serialize传到视类中去显示,编辑,编辑后的数据再通过一个CArchive类由Serialize送回文档类进行保存。需要注意的是,虽然作了加密处理,但是视类处理的必须是明文,所以它操作的文件与文档类操作的文件是不同的,一个是明文文件一个是密文文件(设计时包含了一个512字节的文件头,用于存入密码和有关的信息),为了增加安全性将明文文件作为内存文件,这样在磁盘上就没有任何明文的痕迹了。
四、一个实例
首先生成一个基于单文档的程序,在第6步改视类从CView 为CEditView(如果选择CRichEditView,则生成的加密编辑器还可以加入图形等对象),以下是主要的程序片段:
重载文档类对文件打开的成员函数
# define CIPHER 0xa6 //一个简单的加密密钥
BOOL CDreadDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
//原来的处理函数必须屏蔽,因为这里处理的是密文
/* if (!CDocument::OnOpenDocument(lpszPathName)) return FALSE; */
// TODO: Add your specialized creation code here
DWORD dwFileLength;
unsigned int i,j;
CString PathFile;//store the filename
char mBuff[1024];//buffer that transfer content from disk file to the memory file
unsigned char pAttach[60000];//the buffers attached to the CFile of MY OWN CArchive
POSITION pos = GetFirstViewPosition();
CView* pFirstView;//The CView attached this CDocment
CInputpsw InputPassword;//dialog for input password
char tmp[11];
unsigned int iProperty;// property of dwj file
//above all is data defined
pFirstView=GetNextView( pos );//get the view of this docment
PathFile=lpszPathName;//get the file name opened
CFile HisFile(PathFile,CFile::modeRead);//HisFile is The disk file will be opened
dwFileLength=HisFile.GetLength()-512;
//subtract the length of head file
HisFile.Read(cCipherWrite,512);//read the head of file
//对文件头进行求和校验
iProperty=0;
for(i=0;i<182;i++)
{
iProperty+=cCipherWrite[i];
}//calculate the check sum of head file
if(iProperty!=0x3521)//not the dwj file
{
pFirstView->MessageBox("This is not a dwj file!");
return FALSE;
}
for(i=0;i<10;i++)//convert the password from ciphertext to plaintext
{
tmp[i]=cCipherWrite[189+i]^CIPHER;
}
tmp[10]=0;
mPassword=tmp;
mPassword.TrimRight();
InputPassword.DoModal();
if(mPassword!=InputPassword.m_psw)
//the password inputed is not corresponding to the saved
{
pFirstView->MessageBox("The Password is not correct!");
return FALSE;
}
CMemFile MyFile; //一个内存文件,通过Serialize传给视类进行显示编辑
MyFile.Open("\temp.txt",CFile::modeCreate|CFile::modeWrite);
//首先以写的形式打开内存文件,这样才能将解密后的密文写入内存文件
MyFile.Attach(pAttach,dwFileLength);
//将密文的磁盘文件内容经过解密后读入内存文件
for(i=0;i<dwFileLength/1024;i++)
{
HisFile.Read(mBuff,1024);
for(j=0;j<1024;j++) //decode
{
mBuff[j]^=CIPHER;
}
MyFile.Write(mBuff,1024);
}
HisFile.Read(mBuff,dwFileLength%1024);
for(j=0;j<dwFileLength%1024;j++) //uncode
{
mBuff[j]^=CIPHER;
}
MyFile.Write(mBuff,dwFileLength%1024);
//transfer the data from HisFile to MyFile
HisFile.Close();
MyFile.Close();
MyFile.Open("\temp.txt",CFile::modeRead);
//再次以读的方式打开内存文件,因为从OnOpenDocument中调用的Serialize函//数只能接收读属性的文件
if(dwFileLength>=59999)
{
pFirstView->MessageBox("The length of file is too large to load!");
*pAttach=0;
MyFile.Attach(pAttach,1024,1);
}
else
{
MyFile.Attach(pAttach,dwFileLength);
}
//生成内存文件的CArchive对象ar,显式地调用Serialize函数将它传给视类
CArchive ar(&MyFile,CArchive::load);
Serialize(ar);//throw my memory file to CEditView
return TRUE;
}
另一个需要重载的函数是文档类的保存文件函数
BOOL CDreadDoc::OnSaveDocument(LPCTSTR lpszPathName)
{
// TODO: Add your specialized code here and/or call the base class
CString PathFile;//store the filename saved
unsigned char pAttach[60000];//the buffers attached to the CFile of CArchive
CMemFile MyFile;//The memory file that will retrive the content of CEditView
CFile DesFile;//The destination disk file saved
DWORD dwFileLength;
unsigned int i,j;
unsigned char mBuff[1024];//buffer that transfer content from memory file to destination file
POSITION pos = GetFirstViewPosition();
CView* pFirstView;//CView attached this CDocment
CPasswrd mInputPsw;
//varialbes defined above
pFirstView=GetNextView( pos );//get the CView of this Docment
if(mPassword=="")
//there is not password in this file that will be saved
{
mInputPsw.DoModal();//input the password
mPassword=mInputPsw.m_psw;//get the first password
}
PathFile=lpszPathName;//get the saved file name
MyFile.Open("\temp.txt",CFile::modeCreate | CFile::modeWrite);
for(i=0;i<60000;i++) pAttach[i]=0;//clear the memory file buffers
MyFile.Attach(pAttach,60000);
CArchive ar(&MyFile,CArchive::store);
//显式地调用Serialize函数,从视类中取得编辑后内容存入内存文件
Serialize(ar);//get the data of docment to my memory file
dwFileLength=strlen((char *)pAttach);
if(dwFileLength>=59999)
{
pFirstView->MessageBox("The length of file is too large to save entirely!");
}
DesFile.Open(PathFile,CFile::modeCreate | CFile::modeWrite);
strcpy((char*)cCipherWrite,(LPCTSTR)mVersion);//mVersion是存入在文件头
//中的版本信息
//retrive the version information
mBuff[0]=0x1a;
mBuff[1]=0x00;
strcat((char*)cCipherWrite,(char*)mBuff);
//write the end flag of text file
for(i=0;i<(unsigned int)(mPassword.GetLength());i++)
{
cCipherWrite[189+i]=mPassword.GetAt(i);
}
for(i=0;i<(unsigned int)(10-mPassword.GetLength());i++)
{
cCipherWrite[189+mPassword.GetLength()+i]=0x20;
}//the spare room is filled by space
for(i=0;i<10;i++)//convert the password from plaintext to ciphertext
{
cCipherWrite[189+i]^=CIPHER;
}
//fill with password
DesFile.Write(cCipherWrite,512);//write the head of file
//handle the head of file above
//将存入内存文件中的明文经过加密后存入磁盘文件中
MyFile.SeekToBegin();
for(i=0;i<dwFileLength/1024;i++)
{
MyFile.Read(mBuff,1024);
for(j=0;j<1024;j++) //encode
{
mBuff[j]^= CIPHER;
}
DesFile.Write(mBuff,1024);
}
MyFile.Read(mBuff,dwFileLength%1024);
for(j=0;j<dwFileLength%1024;j++) //encode
{
mBuff[j]^= CIPHER;
}
DesFile.Write(mBuff,dwFileLength%1024);
DesFile.Close();
//transfer the data from HisFile to MyFile
SetModifiedFlag(FALSE);
return TRUE;
//return CDocument::OnSaveDocument(lpszPathName);
}
当然你还需要在你的程序中加入接收用户输入密码的对话框,笔者在这里就不赘述了。
|