上一篇文章写完以后,收到了很多朋友的私信,希望我能写出一个较为完善的利用C语言简单加密文件的代码,今天花了点时间终于调试完了,相比于上一篇文章的代码的话这里做了一些改进:
- 1.加密文件的数据改为原文件数据的十分之一,这样将摆脱文件大小的限制
- 2.使用用户口令对需加密数据进行循环异或操作,增强了加密的安全性
- 3.为用户完善了任意路径文件的加密解密操作,增强用户体验
看过我上一篇文章的朋友应该知道,上一次我们只对文件头的前20个字节的数据进行加密,其实这个加密的范围是会受到文件大小的影响的,这一次我们加密文件数据的十分之一,将可以不受任意文件大小的影响,并且上一次的代码中我们让用户输入123,并使每个文件数据与123进行异或,这一次我们允许用户输入10个以内的任意字符(例如:password),并且使文件的第一个数据与‘p’所对应的ASCII码进行异或,第二个数据与‘a’所对应的ASCII码进行异或,并以此类推,将所需加密的数据与用户加密口令进行循环异或,这将极大的增强加密文件的安全性。
需要了解我上一篇文章的朋友可以看:C语言简单加密文件
以下是具体实现的代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
char file_to_encrypto[100]; //需要加密的文件路径及名称
char file_to_decrypto[100]; //需要解密的文件路径及名称
char encrypted_file[50]="encrypted";//加密后的部分文件名
char decrypted_file[50]="decrypted";//解密后的部分文件名
char password[10]; //用户输入的密码
char temp; //非加密部分的临时数据
unsigned int length; //储存原文件长度
void encrypto()
{
printf("请输入您需要加密文件的完整路径:\n");
scanf("%100s",file_to_encrypto); //输入需加密文件的完整路径及名称
FILE *fp1=fopen(file_to_encrypto,"rb"),*fp2=NULL; //以rb方式打开需加密的文件
if(fp1==NULL)
{
printf("打开文件失败!请检查您的文件路径是否正确\n");
}else{
fseek(fp1,0,2); //使光标移动到文件末尾
length=ftell(fp1); //返回文件的大小
unsigned char* data=(unsigned char*)malloc(length/10); //我们这里加密原文件数据的十分之一
int i,j;
for(i=strlen(file_to_encrypto)-1;i>0;i--) //这里进行一个文件格式(后缀名)的截取
{
if(file_to_encrypto[i]=='.') //从文件名末尾往前查找到第一个'.'号,将其后的文件后缀名添加到加密文件民的末尾
{ //若需加密文件本身没有文件后缀名,则加密后文件名也没有后缀名,即文件名为 encrypted
int p=0;
for(j=i;j<strlen(file_to_encrypto);j++,p++) //文件后缀名的截取
{
encrypted_file[9+p]=file_to_encrypto[j];
}
break;
}
}
rewind(fp1); //放回文件光标到文件头
for(i=0;i<length/10;i++) //读取原文件十分之一的数据到data
{
fread(&data[i],sizeof(unsigned char),1,fp1);
}
printf("请输入您的加密口令(不超过10位):\n");
scanf("%10s",password);
for(i=0;i<(length/10)/strlen(password)+1;i++) //将加密口令的每一位与需加密数据的每一位循环异或,保证加密的安全性
{
for(j=0;j<strlen(password) && i*strlen(password)+j<(length/10);j++)
{
data[i*strlen(password)+j]^=password[j];
}
}
fp2=fopen(encrypted_file,"wb"); //以wb方式打开二进制文件,注意不可以用w方式!
if(fp2==NULL)
{
printf("创建加密文件失败!\n");
}else{
for(i=0;i<length/10;i++) //首先将加密后的数据写入文件
{
fwrite(&data[i],sizeof(unsigned char),1,fp2);
}
rewind(fp1);
fseek(fp1,length/10,0); //使文件光标移动到文件大小的十分之一处
while(i<length) //将未加密的文件数据写入加密文件,i从length/10开始,所以不需要重置i的值
{
fread(&temp,sizeof(unsigned char),1,fp1); //循环写入数据
fwrite(&temp,sizeof(unsigned char),1,fp2);
i++;
}
printf("加密成功!加密文件(encrypted)已保存到您当前目录下\n");
printf("请牢记您的加密口令!\n");
free(data);
fclose(fp1);
fclose(fp2);
getchar();
getchar();
}
}
}
void decrypto()
{
printf("请输入您需要解密文件的完整路径:\n");
scanf("%100s",file_to_decrypto); //输入需要解密的文件路径及名称
FILE *fp1=fopen(file_to_decrypto,"rb"),*fp2=NULL;
if(fp1==NULL)
{
printf("打开文件失败!请检查您的文件路径是否正确\n");
}else{
fseek(fp1,0,2);
length=ftell(fp1); //取得文件长度储存在length中
unsigned char* data=(unsigned char*)malloc(length/10);//开辟动态内存
int i,j;
for(i=strlen(file_to_decrypto)-1;i>0;i--) //截取文件的后缀名
{
if(file_to_decrypto[i]=='.')
{
int p=0;
for(j=i;j<strlen(file_to_decrypto);j++,p++)
{
decrypted_file[9+p]=file_to_decrypto[j];
}
break;
}
}
rewind(fp1); //读取需解密的文件数据,即原文件数据长度的十分之一
for(i=0;i<length/10;i++)
{
fread(&data[i],sizeof(unsigned char),1,fp1);
}
printf("请输入您的加密口令(不超过10位):\n");
scanf("%10s",password);
for(i=0;i<(length/10)/strlen(password)+1;i++) //将需解密的数据与用户口令循环异或
{
for(j=0;j<strlen(password) && i*strlen(password)+j<(length/10);j++)
{
data[i*strlen(password)+j]^=password[j];
}
}
fp2=fopen(decrypted_file,"wb"); //创建解密文件
if(fp2==NULL)
{
printf("创建解密文件失败!\n");
}else{
for(i=0;i<length/10;i++) //首先写入解密后的数据
{
fwrite(&data[i],sizeof(unsigned char),1,fp2);
}
rewind(fp1);
fseek(fp1,length/10,0);//使光标移动到文件数据的十分之一处
while(i<length) //将未解密的数据写入
{
fread(&temp,sizeof(unsigned char),1,fp1);
fwrite(&temp,sizeof(unsigned char),1,fp2);
i++;
}
printf("解密成功!解密文件(decrypted)已保存到您当前目录下\n");
free(data);
fclose(fp1);
fclose(fp2);
getchar();
getchar();
}
}
}
int main()
{
int choice=0;
while(1){
system("color 0a");
system("cls");
printf("欢迎使用文件加密系统!\n");
printf("请输入您的选择\n");
printf("1.加密文件 2.解密文件 3.退出系统\n");
scanf("%d",&choice);
while(choice != 1 && choice != 2 && choice !=3)
{
printf("输入选择有误!请重新输入:");
scanf("%d",&choice);
}
switch (choice)
{
case 1: encrypto();
break;
case 2: decrypto();
break;
case 3: printf("非常感谢您的使用!期待与您下次相见\n");
system("taskkill -f -im 文件加解密系统.exe");
break;
}
}
return 0;
}
这里需要提醒的是,退出系统为另附功能,若您需要使用该功能,需要将system(“taskkill -f -im 文件加解密系统.exe”);中的“文件加解密系统.exe”换成您自己的程序名称,否则将导致退出程序失败。
我们这里以加解密当前路径下的一张图片为例:
我们首先查看这三张图片的属性:
可以看到三张图片的大小一致,说明数据不存在丢失的情况
然后我们利用winhex查看文件数据
可以看到加密后的文件数据已经难以识别,而解密后的文件数据与原文件一致,我们这里验证我们的异或算法,将原文件的第一个字节0x42拿去和加密文件的第一个字节0x36进行异或,并以此将原文件的第二个字节(0x4d)第三个字节(0xa2)第四个字节(0xee)分别与加密文件的第二、三、四个字节进行异或,可以得到
他们分别就是我们输入的加密口令"test",有兴趣的朋友可以验证更多的数据位,会发现异或出的结构是test的循环,因此若我们只得到加密文件,在没有我们的加密算法的情况下,几乎无法修复原文件,保证了加密的安全性。
我这里也在本地多次实验,加密了zip,rar,doc等多种文件,测试均没有问题。
以上代码仅供参考,大家可以根据自己的需要进行修改和补充,希望能为各位抛砖引玉。
若还有存在疑惑的地方,欢迎留言或评论。