以下程式實作對檔案加密。使用了最簡單的XOR來完成。最近才知道,使用位運算的C程式設計稱之為:低級程式設計……汗,此“低級”非彼“低級”……
首先是最簡單的XOR加密實作。
以下代碼:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFSIZE BUFSIZ
/*START:使用XOR進行按位檔案加密*/
//使用者需保證所給密碼正确,否則源檔案将損壞
//密碼長度最多為BUFFSZIE-1
void Tidy(char[]);
void CodeProcess(FILE *,char[]);
int
main(void)
{
FILE *fp;
char buff[BUFFSIZE];
puts("The path of the file...");
fgets(buff,BUFFSIZE,stdin);
buff[strlen(buff)-1]='/0';
fp=fopen(buff,"rb+");
if(fp==NULL){
perror(buff);
exit(EXIT_FAILURE);
}
puts("input the code...");
fgets(buff,BUFFSIZE,stdin);
Tidy(buff);
puts("Processing...");
CodeProcess(fp,buff);
fclose(fp);
puts("task completed!");
return EXIT_SUCCESS;
}
/*START:整理密碼數組*/
//使之長度為BUFFSIZE
void
Tidy(char code[])
{
unsigned int i,j;
j=strlen(code)-1;
i=0;
while(j<BUFFSIZE)
code[j++]=code[i++];
}
/*END*/
/*START:加密/解密主程序*/
void
CodeProcess(FILE *fp,char code[])
{
char buff[BUFFSIZE];
unsigned int i;
size_t readnum;
long repos,rwpos;
rwpos=ftell(fp);
while(readnum=fread(buff,sizeof(buff[0]),BUFFSIZE,fp))
{
if(readnum==0) break;
for(i=0;i<readnum;i++)
buff[i]^=code[i];
repos=ftell(fp);
fseek(fp,rwpos,SEEK_SET);
fwrite(buff,sizeof(buff[0]),readnum,fp);
rwpos=ftell(fp);
fseek(fp,repos,SEEK_SET);
}
}
/*END*/
該程式是最簡單的版本。對某一檔案調用該程式,并給出密碼,即能完成程式設計。所給密碼和檔案路徑最長為BUFSIZ。若對某一已加密檔案使用該程式,即可完成解密。
但不幸的是,在解密時,若所給密碼錯誤,則檔案将損壞且無法修複……(貌似也可以修複,隻不過麻煩了許多)
我認為,如果密碼錯誤時,程式照樣運作,進而導緻檔案損壞,這是不應該的。當密碼錯誤時,程式應該終止運作而不是繼續動作。
于是,想到将密碼存儲于檔案中,并以某種方式标注該檔案為加密檔案,需要先給出密碼進行解碼。
于是又了以下程式:
先是頭檔案:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFSIZE BUFSIZ
#define EXTENDSIZE 6
int coded=0;
void MainProcess(char path[],char code[]);
void IsCoded(char path[]);
void Tidy(char code[]);
void CodedProcess(FILE *fp,char code[]);
void CodeProcess(FILE *fp,char code[]);
long CheckCode(FILE *fp,char code[]);
long SetCodeInfo(FILE *fp,char code[]);
void XorCode(FILE* fp,long repos,long rwpos,char code[]);
void ChangeName(char path[]);
接着是程式:
#include <Fatal.h>
#include <testhead.h>
/*START:使用XOR加密/解密檔案*/
//已加密檔案存儲密碼
//密碼長度最多為BUFFSZIE-1
int
main(void)
{
FILE *fp;
char path[BUFFSIZE];
char code[BUFFSIZE];
puts("input the filepath...");
fgets(path,BUFFSIZE,stdin);
path[strlen(path)-1]='/0';
fp=fopen(path,"rb+");
if(fp==NULL)
{
perror(path);
exit(EXIT_FAILURE);
}
fclose(fp);
puts("input the code...");
fgets(code,BUFFSIZE,stdin);
code[strlen(code)-1]='/0';
MainProcess(path,code);
puts("task completed!");
return EXIT_SUCCESS;
}
/*END*/
/*START:主例程*/
//參數為檔案路徑及所給密碼
void
MainProcess(char path[],char code[])
{
FILE *fp;
IsCoded(path);
Tidy(code);
fp=fopen(path,"rb+");
if(fp==NULL){
perror(path);
exit(EXIT_FAILURE);
}
if(coded==1)
CodedProcess(fp,code);
else
CodeProcess(fp,code);
fclose(fp);
ChangeName(path);
}
/*END*/
/*START:判斷該檔案是否已加密*/
void
IsCoded(char path[])
{
char extend[EXTENDSIZE+1]=".coded";
char *Ppath,*Pextend;
Pextend=&extend[EXTENDSIZE];
Ppath=&path[strlen(path)];
while(*--Pextend==*--Ppath)
{
if(Pextend==extend) break;
}
if(*Pextend==*Ppath) coded=1; //檔案已加密
}
/*END*/
/*START:整理密碼數組*/
//密碼長度為BUFFSIZE
void
Tidy(char code[])
{
unsigned int i,j;
j=strlen(code);
i=0;
while(j<BUFFSIZE)
code[j++]=code[i++];
}
/*END*/
/*START:解密驅動例程*/
void
CodedProcess(FILE *fp,char code[])
{
long repos,rwpos;
puts("The file was coded...");
repos=CheckCode(fp,code);
rewind(fp);
rwpos=ftell(fp);
XorCode(fp,repos,rwpos,code);
}
/*END*/
/*START:判斷所給密碼是否正确*/
//若不正确,則退出程式
long
CheckCode(FILE *fp,char code[])
{
char buff[BUFFSIZE];
register char *p,*q;
fread(buff,sizeof(buff[0]),BUFFSIZE,fp);
p=buff; q=code;
while(*p++==*q++
&&p<=&buff[BUFFSIZE-1])
;
if(p<=&buff[BUFFSIZE-1])
Error("The input code is invalid...");
return ftell(fp);
}
/*END*/
/*START:加密驅動例程*/
void
CodeProcess(FILE *fp,char code[])
{
long pos;
puts("The file will be coded...");
pos=SetCodeInfo(fp,code);
XorCode(fp,pos,pos,code);
}
/*END*/
/*START:将加密資訊寫入檔案*/
long
SetCodeInfo(FILE *fp,char code[])
{
fwrite(code,sizeof(code[0]),BUFFSIZE,fp);
return ftell(fp);
}
/*END*/
/*START:XOR加密/解密*/
void
XorCode(FILE* fp,long repos,long rwpos,char code[])
{
char buff[BUFFSIZE];
unsigned int i;
size_t readnum;
fseek(fp,repos,SEEK_SET);
while(readnum=fread(buff,sizeof(buff[0]),BUFFSIZE,fp))
{
if(readnum==0) break;
for(i=0;i<readnum;i++)
buff[i]^=code[i];
repos=ftell(fp);
fseek(fp,rwpos,SEEK_SET);
fwrite(buff,sizeof(buff[0]),readnum,fp);
rwpos=ftell(fp);
fseek(fp,repos,SEEK_SET);
}
}
/*END*/
/*START:改變檔案的名字*/
void
ChangeName(char path[])
{
char newname[BUFFSIZE+EXTENDSIZE];
char *p;
strcpy(newname,path);
if(coded==1){
p=strrchr(newname,'.');
*p='/0';
}
else
strcat(newname,".coded");
if(rename(path,newname)!=0)
Error("Failed to change the filename!");
}
/*END*/
程式相當長啊~~~
第一次寫完以後相當興奮。但是運作以下,卻發現不正确。當我對某檔案,如1.abc,加密後,源檔案變成1.abc.coded。但對其解密後,卻發現無法運作……這樣,不管如何,隻要經過加密的檔案都将損壞……
研究了半天,才終于弄明白。是fopen出了問題。在fopen那裡,使用的mode是"rb+"。我原以為,在解密時,當我把讀寫位置指定以後,從流頭開始寫入的話,就可以把原資料覆寫,就如同在數組中一樣……但流畢竟不是數組。使用"rb+"時,是不會覆寫原資料的,而類似于插入……這樣,已加密的檔案頭BUFSIZ的密碼資訊并沒有被覆寫以達到删除的目的,而是被移動到了檔案後部。這樣就造成了檔案損壞……
(補一句:我測試的是媒體檔案,貌似如果是TXT等檔案的話,不會損壞,但會多出資訊……汗……)
想了半天,沒有想到有什麼函數能讓我從流中删除部分資料……
于是,又改進了上面的程式。
chead.h:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFSIZE BUFSIZ
#define EXTENDSIZE 6
int coded=0;
void MainProcess(char path[],char code[]);
void IsCoded(char path[]);
void Tidy(char code[]);
void GetNewName(char [],char []);
void CodedProcess(FILE *,FILE *,char []);
void CodeProcess(FILE *,FILE *,char []);
void CheckCode(FILE *fp,char code[]);
void SetCodeInfo(FILE *fp,char code[]);
void XorCode(FILE* fpold,FILE *fpnew,char code[]);
cfile.c
#include "chead.h"
#include <Fatal.h>
/*START:使用XOR加密/解密檔案*/
int
main(void)
{
FILE *fp;
char path[BUFFSIZE];
char code[BUFFSIZE];
puts("input the filepath...");
fgets(path,BUFFSIZE,stdin);
path[strlen(path)-1]='/0';
fp=fopen(path,"rb+");
if(fp==NULL)
{
perror(path);
exit(EXIT_FAILURE);
}
fclose(fp);
puts("input the code...");
fgets(code,BUFFSIZE,stdin);
code[strlen(code)-1]='/0';
puts("Processing...");
MainProcess(path,code);
puts("task completed!");
return EXIT_SUCCESS;
}
/**END/
/*START:主例程*/
//參數為檔案路徑及所給密碼
void
MainProcess(char oldname[],char code[])
{
FILE *fpold,*fpnew;
char newname[BUFFSIZE+EXTENDSIZE];
IsCoded(oldname);
Tidy(code);
GetNewName(oldname,newname);
fpold=fopen(oldname,"rb+"); //MARK:old檔案應被删除;
if(fpold==NULL){
perror(oldname);
exit(EXIT_FAILURE);
}
fpnew=fopen(newname,"wb+");
if(fpnew==NULL)
Error("File can't be created!!!");
if(coded==1)
CodedProcess(fpold,fpnew,code);
else
CodeProcess(fpold,fpnew,code);
fclose(fpold);
fclose(fpnew);
remove(oldname);
}
/*END*/
/*START:判斷該檔案是否已加密*/
void
IsCoded(char path[])
{
char extend[EXTENDSIZE+1]=".coded";
register char *Ppath,*Pextend;
Pextend=&extend[EXTENDSIZE];
Ppath=&path[strlen(path)];
while(*--Pextend==*--Ppath)
{
if(Pextend==extend) break;
}
if(*Pextend==*Ppath) coded=1; //檔案已加密
}
/*END*/
/*START:整理密碼數組*/
//密碼長度為BUFFSIZE
void
Tidy(char code[])
{
unsigned int i,j;
j=strlen(code);
i=0;
while(j<BUFFSIZE)
code[j++]=code[i++];
}
/*END*/
/*START:改變檔案的名字*/
//并打開所傳路徑指向的檔案
void
GetNewName(char oldname[],char newname[])
{
char *p;
strcpy(newname,oldname);
if(coded==1){
p=strrchr(newname,'.');
*p='/0';
}
else
strcat(newname,".coded");
}
/*END*/
/*START:解密驅動例程*/
void
CodedProcess(FILE *fpold,FILE *fpnew,char code[])
{
//long repos; //MARK:此處可不可以不使用偏移?
puts("The file was coded...");
CheckCode(fpold,code);
XorCode(fpold,fpnew,code);
}
/*END*/
/*START:判斷所給密碼是否正确*/
//若不正确,則退出程式
void
CheckCode(FILE *fp,char code[])
{
char buff[BUFFSIZE];
register char *p,*q;
fread(buff,sizeof(buff[0]),BUFFSIZE,fp);
p=buff; q=code;
while(*p++==*q++
&&p<=&buff[BUFFSIZE-1])
;
if(p<=&buff[BUFFSIZE-1]){
Error("The input code is invalid...");
}
else
puts("The code is correct.../nProcessing...");
}
/*END*/
/*START:加密驅動例程*/
void
CodeProcess(FILE *fpold,FILE *fpnew,char code[])
{
//long pos; //MARK:此處是否可以不使用該語句?
puts("The file will be coded.../nProcessing");
SetCodeInfo(fpnew,code);
XorCode(fpold,fpnew,code);
}
/*END*/
/*START:将加密資訊寫入檔案*/
void
SetCodeInfo(FILE *fp,char code[])
{
fwrite(code,sizeof(code[0]),BUFFSIZE,fp);
}
/*END*/
/*START:XOR加密/解密*/
void
XorCode(FILE* fpold,FILE *fpnew,char code[])
{
char buff[BUFFSIZE];
unsigned int i;
size_t readnum;
while(readnum=fread(buff,sizeof(buff[0]),BUFFSIZE,fpold))
{
if(readnum==0) break;
for(i=0;i<readnum;i++)
buff[i]^=code[i];
fwrite(buff,sizeof(buff[0]),readnum,fpnew);
}
}
/*END*/
想了半天确實沒有想到有可以删除部分C流資料的函數,在網上找了下也确實沒有……于是使用了建立檔案,将特定内容從舊檔案複制至新檔案,最後删除舊檔案的方法。
其實,在上面的程式二,其本質也是進行檔案複制,不過當時為了避免使用新的流容器,是以使用了在一個檔案中來回定位的方法……這樣反而得不償失,空間是節省了,但時間效率降低了,由于要不停的來回定位,是以檔案流的目前位置要來回不停的變動……
在程式三的CheckCode中,将兩個指針聲明為register,大大的提高了效率。但奇怪的是,我将XorCode中的下标i聲明為register後,并沒有提高效率,不知道是為什麼……很想知道,在什麼情況下,将變量聲明為register會提高效率。。。。。
當合法使用者忘記了密碼時候會無法解密該程式。實際上,我認為加密隻是用于防範非法使用者的,對合法使用者來說,應該能不需要記住密碼就可以進行解密。。。。
慶幸的是,由于該加密算法簡單,是以可以滿足這一點。如果是合法使用者,則使用下面的程式來解密,這樣就無需記住密碼……
chead.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Fatal.h>
#define BUFFSIZE BUFSIZ
#define EXTENDSIZE 6
void MainProcess(char path[]);
void GetNewName(char [],char []);
void CodedProcess(FILE *,FILE *);
void XorCode(FILE* fpold,FILE *fpnew);
cfile.c
#include "chead.h"
/*START:使用XOR進行解密*/
//無需給出密碼
int
main(void)
{
FILE *fp;
char path[BUFFSIZE];
puts("input the filepath...");
fgets(path,BUFFSIZE,stdin);
path[strlen(path)-1]='/0';
fp=fopen(path,"rb+");
if(fp==NULL)
{
perror(path);
exit(EXIT_FAILURE);
}
fclose(fp);
puts("Processing...");
MainProcess(path);
puts("task completed!");
return EXIT_SUCCESS;
}
/*END*/
/*START:主例程*/
void
MainProcess(char oldname[])
{
FILE *fpold,*fpnew;
char newname[BUFFSIZE+EXTENDSIZE];
GetNewName(oldname,newname);
fpold=fopen(oldname,"rb+"); //MARK:old檔案應被删除;
if(fpold==NULL){
perror(oldname);
exit(EXIT_FAILURE);
}
fpnew=fopen(newname,"wb+");
if(fpnew==NULL)
Error("File can't be created!!!");
CodedProcess(fpold,fpnew);
fclose(fpnew);
fclose(fpold);
remove(oldname);
}
/*END*/
/*START:改變檔案的名字*/
//并打開所傳路徑指向的檔案
void
GetNewName(char oldname[],char newname[])
{
char *p;
strcpy(newname,oldname);
p=strrchr(newname,'.');
*p='/0';
}
/*END*/
/*START:解密驅動例程*/
void
CodedProcess(FILE *fpold,FILE *fpnew)
{
XorCode(fpold,fpnew);
}
/*END*/
/*START:XOR加密/解密*/
void
XorCode(FILE* fpold,FILE *fpnew)
{
char buff[BUFFSIZE];
char code[BUFFSIZE];
unsigned int i;
size_t readnum;
fread(code,sizeof(code[0]),BUFFSIZE,fpold);
while(readnum=fread(buff,sizeof(buff[0]),BUFFSIZE,fpold))
{
if(readnum==0) break;
for(i=0;i<readnum;i++)
buff[i]^=code[i];
fwrite(buff,sizeof(buff[0]),readnum,fpnew);
}
}
/*END*/
加密和解密實在是個很有趣的話題,以後有時間了再仔細研究吧。