最近做了一个项目,里面有一个小需求就是对处理过的文件进行加密,加密之后无法打开。我最先想到的是异或加密,因为需要速度,并且对加密的安全性要求不高。

1、异或加密原理

异或密码(simple XOR cipher)是密码学中一种简单的加密算法,是指对信息进行异或操作来达到加密和解密目的。按这种逻辑,文本串行的每个字符可以通过与给定的密钥进行按位异或运算来加密。如果要解密,只需要将加密后的结果与密钥再次进行按位异或运算即可。

说的通俗一点,就是一个字符异或一个密钥字符进行加密,解密的时候,则用加密后的字符再次异或密钥字符则能够还原。

异或加密的原理就是使用了异或运算符的运算特点!例如异或密钥key是10101010,待加密的字符s1的二进制表示为01010101,则进行加密处理(s1 ^ key)后的结果s2为 11111111,如果要解密还原,则使用密钥key再次异或处理(s2 ^ key)就能够还原为s1。

2、文件加密流程

对文件进行加密的流程就比较简单了,步骤如下:

  1. 用二进制方式读取文件
  2. 将文件读到缓冲区
  3. 每次从缓冲区读取一个字符
  4. 对读取的字符进行异或加密
  5. 加密结果保存到结果缓冲区
  6. 读取下一个字符进行加密(循环执行)
  7. 加密结束后保存到加密文件

         那么,解密的流程呢?

        其实解密和加密的流程一模一样,因为加密和解密的密钥是一样的!

这里有一些地方是可以进行优化改进的,例如:

  • 大文件的情况:大文件可能导致超过内存大小
  • 效率问题:每个字符都进行了加密
  • 安全问题:单个字符作为密钥比较容易破解
  • 无法区分文件是否已经加密

        以上问题咱们放在第4部分再进行讨论!

3、C++实现

实现部分用标准C++,支持跨平台的使用。这里我封装了一个处理类,大家直接看代码吧!

注意:代码中的方法是对pdf文件进行加解密操作,并不表示无法对其他文件进行操作。因为,所有的文件(不管pdf还是其他的文件)都是保存为二进制的,所以使用我提供的方法一样可以进行加解密操作!

所以这个加密方案就是一个通用万能的加解密方案!

h文件:

#ifndef __MasterEncoder_H__
#define __MasterEncoder_H__
#include <mutex>
#include <string>
using namespace std;
class MasterEncoder
{
public:static MasterEncoder* getInstance();MasterEncoder();~MasterEncoder();void setSignAndKey(char* sign, int signLen, unsigned char key);void decode(unsigned char* data, long size);void encode(unsigned char* data, long size);void writePDF(const string& filePath, unsigned char* data, long size);unsigned char* readPDF(const string& filepath, long& size);public:void encodePDF(const string& pdfPath, const string& savePath);void decodePDF(const string& pdfPath, const string& savePath);unsigned char* decode(const string& pdfPath, long& size);private:static MasterEncoder* _instance;static mutex _mtx;unsigned char _codeKey;char* _sign;int _signLen;};#endif // !MasterEncoder

cpp文件:

#include "MasterEncoder.h"
#include <fstream>
#include <string>
#include <cstring>
#include <iostream>
#include <thread>
using namespace std;MasterEncoder* MasterEncoder::_instance = nullptr;
mutex MasterEncoder::_mtx;MasterEncoder* MasterEncoder::getInstance() {if (_instance == nullptr) {_mtx.lock();if (_instance == nullptr)_instance = new MasterEncoder();_mtx.unlock();}return _instance;
}MasterEncoder::MasterEncoder(): _codeKey{100}, _sign{nullptr}, _signLen{0}
{}MasterEncoder::~MasterEncoder() {}void MasterEncoder::setSignAndKey(char* sign, int signLen, unsigned char key) {if (sign && signLen){_signLen = signLen;_sign = new char[signLen];if (_sign) {memcpy(_sign, sign, signLen);}else {cout << "setSignAndKey Failed!\n";}}_codeKey = key;
}void MasterEncoder::encode(unsigned char* data, long size) {for (long i = 0; i < size; i++){data[i] ^= _codeKey;}
}void MasterEncoder::decode(unsigned char* data, long size) {for (long i = 0; i < size; i++){data[i] ^= _codeKey;}
}unsigned char* MasterEncoder::readPDF(const string& filepath, long& size)
{ifstream ifs;ifs.open(filepath, ios::in | ios::binary);if (!ifs.is_open()){cout << "文件打开失败" << endl;return nullptr;}ifs.seekg(0, std::ios_base::end);size = ifs.tellg();ifs.seekg(0, std::ios_base::beg);unsigned char* data = new unsigned char[size];long count = 0;while (count < size){ifs.read((char*)&data[count], sizeof(unsigned char));//ifs >> data[count];//字符串模式//printf("%c", data[count]);count++;}cout << "read PDF size = " << size << endl;ifs.close();return data;
}void MasterEncoder::writePDF(const string& filePath, unsigned char* data, long size) {ofstream ofs;ofs.open(filePath, ios::out | ios::trunc | ios::binary);if (!ofs.is_open()){cout << "文件打开失败" << endl;return;}long count = 0;while (count < size) {//ofs << data[count];ofs.write((char*)&data[count], sizeof(unsigned char));count++;}cout << "save PDF size = " << size << endl;ofs.close();}void MasterEncoder::encodePDF(const string& pdfPath, const string& savePath) {long size = 0;unsigned char* data = readPDF(pdfPath, size);//判断是否已经加密过if (memcmp(data, _sign, _signLen) == 0) {cout << "加密过了, 直接保存!" << endl;writePDF(savePath, data, size);}else {encode(data, size);if (_signLen > 0) {unsigned char* p = new unsigned char[size + _signLen];memcpy(p, _sign, _signLen);memcpy(p + _signLen, data, size);writePDF(savePath, p, size + _signLen);delete[] p;}else {writePDF(savePath, data, size);}}delete[] data;
}void MasterEncoder::decodePDF(const string& pdfPath, const string& savePath) {long size = 0;unsigned char* data = readPDF(pdfPath, size);if (_signLen == 0) {decode(data, size);writePDF(savePath, data, size);}else if (memcmp(data, _sign, _signLen) == 0) {unsigned char* p = (data + _signLen);decode(p, size - _signLen);writePDF(savePath, p, size - _signLen);}else {cout << "解密失败!" << endl;}delete[] data;
}unsigned char* MasterEncoder::decodePDF(const string& pdfPath, long& size) {unsigned char* data = readPDF(pdfPath, size);if (_signLen == 0) {decode(data, size);return data;}else if (memcmp(data, _sign, _signLen) == 0) {size -= _signLen;unsigned char* p = new unsigned char[size];memcpy(p, data+_signLen, size);decode(p, size);delete[] data;return p;}else {return data;}
}

代码中已经对多线程进行了处理,并且已使用sign来标识加密和解密文件。

        使用的方式就非常简单了,主要是两个方法:

  • encodePDF :MS::getInstance()->encodePDF(input, output);
  • decodePDF :MS::getInstance()->decodePDF(input, output);

使用的示例如下:(提供了命令行的方式来操作,可以直接拿走使用)

#include <fstream>
#include <string>
#include "MasterEncoder.h"using namespace std;
using MS = MasterEncoder;
int main(int argc, char* argv[]) {system("COLOR 0A");const char* sign = "master";MS::getInstance()->setSignAndKey((char*)sign, strlen(sign), 100);char* input = nullptr;char* output = nullptr;printf("argc = %d\n", argc);if (argc >= 2) {if (strcmp(argv[1], "-help") == 0) {printf("   -encode 加密\n");printf("   -decode 解密\n");printf("   -i 输入文件\n");printf("  -o 输出文件\n");printf("  example: main.exe -encode -i test.pdf -o test1.pdf \n");}if (strcmp(argv[1], "-encode") == 0) {//加密if (strcmp(argv[2], "-i") == 0)input = argv[3];if (strcmp(argv[4], "-o") == 0)output = argv[5];if (input && output)MS::getInstance()->encodePDF(input, output);}else if (strcmp(argv[1], "-decode") == 0) {//解密if (strcmp(argv[2], "-i") == 0)input = argv[3];if (strcmp(argv[4], "-o") == 0)output = argv[5];if(input && output)MS::getInstance()->decodePDF(input, output);}else {printf("请输入操作类型!");}if (input == nullptr || output == nullptr)printf("请输入正确参数!");}system("pause");return 0;
}

4、方案改进

这里的方案我只针对我自己需要处理的PDF文件,所以很多东西没有考虑。例如文件大小等等,下面来一一解答。也欢迎大家留言讨论其他情况!

4.1 大文件的情况

大文件(好几G的文件)可能导致超过内存大小,一次读到内存中可能会撑爆内存!

解决方案:

每次读取若干个字节(例如1000个)进行加密处理,然后将处理完的结果保存到输出文件中,如此循环下去,直到所有文件处理结束!

4.2 效率问题:

上面的方案中,我对每个字符都进行了加密。如果文件过大或者对效率要求特别高,怎么提高效率呢?

其实这个没有标准答案,异或处理是非常非常简单的加密逻辑了,效率也是非常非常高的!如果你还行提升加密性能,那只能够在处理流程上下手!

解决方案:

隔若干个字符做一次加密,其他字符不进行加密处理。

例如每隔50个字符加密一个字符,当然,这个间隔字符的数量可以自行设定!甚至你可以设计一个取值公式来替代固定的间隔值。

此外,还有一个方案就是使用 《4.3节》 中的逻辑,同样可以提升效率!

这里我只是让PDF文件无法再被pdf阅读器打开,所以这个方案是可行的!

也就是咱们再讨论具体方案的时候一定要结合实际需求,抛开需求谈方案往往是不切实际的,甚至可能讨论出的方案最终无法满足需求!

4.3 安全问题:

单个字符作为密钥比较容易破解,密钥最多只有2^8种,很容易通过枚举来破解!

解决方案:

使用多个字节来作为密钥,每次加密的时候一次加密多个字节即可。

例如密钥使用8个字节,这样会2^64种情况,每次加密处理的时候对8个字节进行加密操作!这样不仅更加安全,效率也会更高!

4.4 无法区分文件是否已经加密

加密后的文件无法区分是否已经加密,这样你在进行解密的时候可能做的是加密操作,而加密操作可能是解密操作!

例如:加密的时候传入了已经加过密的文件,则输出的是解密文件,因为异或操作2次就能还原,所以加密和解密的逻辑是一模一样的。

所以,我们要明确知道你的文件是否已经加过密,否则,你做的操作可能是相反的!

这个标识逻辑我在代码中已经实现了,大家看看代码就能明白!

5、总结

方案在设计的时候要考虑实际需求,例如我这里的需求只要求pdf文件加密后无法再打开。

还有一点就是加密的安全性的问题,例如我这里的加密密钥(_codeKey)是存在代码中的,这个是很容易被反编译破解的。

那么有没有必要再对加密逻辑这一块进行加固呢?这就完全取决于你的需求!这也不再本文讨论范围内,这里只是做一个提示!

如果您觉得文章有帮助到你,欢迎打赏!

您的支持是我不断创作的动力

C++实现通用的文件(万能)加密方案——包含源码相关推荐

  1. 2022最新在线PHP文件SG11加密平台系统源码

    正文: 2022最新PHP文件加密系统_在线SG11加密平台系统源码 PHP需安装sg11扩展使用 程序: wwmrg.lanzouq.com/iDoM20abrw5g 图片:

  2. php微擎万能门店小程序_万能门店独立版小程序V2.0.2,基于ThinkPHP5框架开发的万能门店小程序源码...

    源码介绍 基于ThinkPHP5框架开发的万能门店小程序源码,是微擎上很火的万能门店小程序的独立版,万能门店小程序DIY建站无限独立版非微擎应用,独立版是基于国内很火的ThinkPHP5.0.10框架 ...

  3. srs可以用java开发吗,为SRS流媒体服务器添加HLS加密功能(附源码)

    #为SRS流媒体服务器添加HLS加密功能(附源码)# 之前测试使用过nginx的HLS加密功能,会使用到一个叫做nginx-rtmp-module的插件,但此插件很久不更新了,网上搜索到一个中国制造的 ...

  4. PHP加密技术 附源码

    点击上方" 码农编程进阶笔记 ",选择"置顶或者星标" 文末有干货,每天定时与您相约! PHP 加密后的代码能运行在 PHP 5+ 以上版本. 跨平台,Wind ...

  5. 基于新唐M0的XXTEA加密解密算法源码

    源:基于新唐M0的XXTEA加密解密算法源码 /*--------------------------------------------------------------------------- ...

  6. 程序实现php文件上传,PHP实例:实现文件上传的程序源码_php

    以下为引用的内容: 文件上传界面 http://www.gaodaima.com/48380.htmlphp实例:实现文件上传的程序源码_php if($UploadAction){ $UploadA ...

  7. 基础IO(文件接口、安装内核源码超详细步骤图解、静态库与动态库)

    基础IO C语言的文件操作接口 fopen fclose fread fwrite fseek 系统调用文件接口 open close read write lseek 安装内核源码 文件描述符&am ...

  8. 2012年5月后QQ空间最新登录密码加密方式破源码

    最新2012年5月后的登录QQ空间加密新算法源码,需要开发QQ外挂的可以参考参考! 下载地址:http://download.csdn.net/detail/web_boy/4369470

  9. VC++设置文件最后修改时间(附源码)

      VC++开发常用功能一系列文章 (欢迎订阅,持续更新...) 第21章:VC++设置文件最后修改时间(附源码) 源代码demo已上传到百度网盘:永久生效  ,代码实现了设置文件最后修改时间 上一篇 ...

最新文章

  1. VC6解决托盘菜单不消失
  2. in use 大学英语4word_考研英语真题干货 | run on
  3. 【激活函数】Mish激活函数详解
  4. C++ VS2012 内存泄露检测
  5. 方格图片轮换JS特效
  6. 聊聊微服务架构及分布式事务解决方案!
  7. 小程序下划线和删除线的操作
  8. html怎样把两张图片重叠6,两张照片重叠成一张-如何将两张相片重叠放在一起?...
  9. 如何建立个人网站服务器篇
  10. 金多多配资盘面预测收益大于风险
  11. 腾讯优图TFace正式开源,更可信的人脸识别!
  12. 自学前端第一天:认识前端工程与网页
  13. 同一局域网内手机访问电脑本地localhost网页
  14. html5 主标题副标题,word如何设置正副标题
  15. Qt Quick实现九宫格划指锁屏视图
  16. 安卓浮动分组手机联系人快速定位demo
  17. 多模态 Generalized Visual Language Models
  18. 【Linux:CentOS7】查看JDK版本信息报错解决
  19. AH8316可同时输出两路USB一路5V/3A另一路5V 2.4A
  20. 男子杀死情妇丈夫后分尸 起因系情妇要分手

热门文章

  1. C51单片机的电子时钟(数码管显示)
  2. JavaScript下雨效果
  3. 一文看懂:Android-Q版本在安全方面进行了哪些系统性改进
  4. R语言波士顿房价分析
  5. Think Pad E570重装Win10系统没有外放喇叭声音
  6. 设置文字样式并写入CAD
  7. 一加6点击五下出来Android10,一加6T现已推送Android 10.0公测版更新
  8. webservice概述及cxf在Java开发中应用(三) cxf客户端开发
  9. java拷贝远程服务器上文件,java拷贝远程服务器上文件
  10. Python中的numpy.cumsum()