天天看點

基于XOR的加密程式

以下程式實作對檔案加密。使用了最簡單的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*/

加密和解密實在是個很有趣的話題,以後有時間了再仔細研究吧。