之前存在的弱点
- 文件虽然打包成一个文件,但是对于打包后的文件内容依然是明文的,不安全
常用加密算法
AES
密码学中的高级加密标准(Advanced Encryption Standard,AES),又称高级加密标准Rijndael加密法,是美国联邦政府采用
的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。XOR加密
同一值去异或两次就可以恢复出原来的值,因此它是最简单的可逆的操作,也是最简单的加密算法自定义算法
NewPackage.h
#ifndef NEWPACKAGE_H_ #define NEWPACKAGE_H_ #include <stdio.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <vector> #include <string> class CFileBuffer { public: CFileBuffer():m_pBuffer(nullptr),m_nSize(0) { } ~CFileBuffer() { Destroy(); } //创建一块内存 bool Create(size_t Size) { Destroy(); m_pBuffer = malloc(Size); if(!m_pBuffer) { return false; } m_nSize = Size; return true; } //销毁一块内存 void Destroy() { if(m_pBuffer) { free(m_pBuffer); m_pBuffer = nullptr; } m_nSize = 0; } //获取申请内存地址 void* GetBuffer()const { return m_pBuffer; } //获取申请内存大小 size_t GetSize()const { return m_nSize; } //将内存写入到磁盘文件 bool Save(const char* szFileName) { //检查m_pBuffer是否为空,如果为空,表示当前指针没有指向任何数据,不需要写入 if(!m_pBuffer) return false; //以二进制写的方式打开文件,准备写入文件 FILE* fp = fopen(szFileName, "wb"); if(!fp) { fprintf(stderr, "fopen %s error : %s \n",szFileName, strerror(errno)); } //开始写入文件 if(m_nSize != fwrite(m_pBuffer, 1, m_nSize, fp)) { fclose(fp); return false; } fclose(fp); return true; } private: void* m_pBuffer; size_t m_nSize; }; struct NewPackageItem { size_t FileNameLen; //文件名的长度 size_t FileSize; //文件的大小 size_t OffsetPackage; //文件在包裹的偏移位置 char FileName[0]; //可变文件名 }; //我们定义一个标识,表示这个是我们的包裹格式 static const char MY_PACKAGE_FLAG[] = "NewPackage"; static const size_t MY_ENCRYPT_METHOD_XOR = 0; //xor加密 struct PackageInfo { size_t EncryptMethod; //加密算法 }; class CNewPackage { public: CNewPackage(); ~CNewPackage(); //重置一个包裹 void ResetPackage( ); //读取一个包裹 bool OpenPackage( const char*szPackageName ); //添加一个文件,忽略大小写 bool AddFile( const char*szFileName ); //删除一个文件,忽略大小写 bool RemoveFile( const char*szFileName ); //重复性检查,忽略大小写 bool HasFile( const char*szFileName ); //保存包裹到指定文件名 bool SavePackage( const char*szPackageName ); //导出文件数据到FileBuffer里面 bool ExportPackageItem( const char*szFileName, CFileBuffer & FileBuffer ); private: FILE* m_fpPackage; //包裹文件指针 std::vector<std::string> m_AddFiles; //待添加到包裹的文件 std::vector<NewPackageItem*> m_PackageItems; //已经打包到包裹的文件信息 }; #endif // !NEWPACKAGE_H_
NewPackage.cpp
#include "NewPackage.h" /* ┌------------┬-------------┬-------------┬------┬--------┬-------┬-------┐ ├ size_t n │ Item │ Item │. . . │ FILE1 │ FILE1 │ . . . │ └------------┴-------------┴-------------┴------┴--------┴-------┴-------┘ ┌-----------┬-----------┬--------┬-----------┬-----------┬------┬--------┬-------┬-------┐ ├ size_t n │PackageItem│FileName│PackageItem│ FileName │. . . │ FILE1 │ FILE1 │ . . . │ └-----------┴-----------┴--------┴-----------┴-----------┴------┴--------┴-------┴-------┘ */ struct FileItemForWrite { FILE *fp; //要打包的文件的文件指针 size_t FileOffset; //在文件中的偏移位置 size_t FileSize; //要读取的文件的大小 std::string FileName; //要读取的文件的文件名 NewPackageItem *pPackageItem; //要读取的文件基本信息 }; CNewPackage::CNewPackage() { m_fpPackage = nullptr; } CNewPackage::~CNewPackage() { ResetPackage(); } void CNewPackage::ResetPackage() { for (size_t i = 0; i < m_PackageItems.size(); i++) { NewPackageItem *pItem = m_PackageItems[i]; free(pItem); } //clear是为了清除vector内部的index,size;如果不清除,这两个值会一直增加 m_PackageItems.clear(); //关闭包裹文件指针 if (m_fpPackage) { fclose(m_fpPackage); m_fpPackage = nullptr; } //清理待添加的文件容器 m_AddFiles.clear(); } bool CNewPackage::AddFile(const char *szFileName) { //如果有同名的,做替换处理 RemoveFile(szFileName); m_AddFiles.push_back(szFileName); return true; } bool CNewPackage::RemoveFile(const char *szFileName) { //从待添加的文件查找 for (size_t i = 0; i < m_AddFiles.size(); i++) { if (strcasecmp(m_AddFiles[i].c_str(), szFileName) == 0) { m_AddFiles.erase(m_AddFiles.begin() + i); return true; } } //从包裹文件中查找 for (size_t i = 0; i < m_PackageItems.size(); i++) { NewPackageItem *pItem = m_PackageItems[i]; if (strcasecmp(pItem->FileName, szFileName)) { m_PackageItems.erase(m_PackageItems.begin() + i); return true; } } } bool CNewPackage::HasFile(const char *szFileName) { //从待添加的文件查找 for (size_t i = 0; i < m_AddFiles.size(); i++) { if (strcasecmp(m_AddFiles[i].c_str(), szFileName) == 0) { return true; } } //从包裹文件中查找 for (size_t i = 0; i < m_PackageItems.size(); i++) { NewPackageItem *pItem = m_PackageItems[i]; if (strcasecmp(pItem->FileName, szFileName)) { return true; } } } //用xor的方式写入 inline size_t NewPackageXORWrite( const void*pBuffer, size_t Size, FILE*fp ) { unsigned char*pByteBuffer = (unsigned char*)malloc( Size ); if( !pByteBuffer ) { return 0; } unsigned char*pSrcBuffer = (unsigned char*)pBuffer; for( size_t i=0; i<Size; i++ ) { pByteBuffer[i] = ~pSrcBuffer[i]; } size_t WriteSize = fwrite( pByteBuffer, 1, Size, fp ); free( pByteBuffer ); return WriteSize; } //用xor的方式读取 inline size_t NewPackageXORRead( void*pBuffer, size_t Size, FILE*fp ) { size_t ReadSize = fread( pBuffer, 1, Size, fp ); unsigned char*pByteBuffer = (unsigned char*)pBuffer; for( size_t i=0; i<ReadSize; i++ ) { pByteBuffer[i] = ~pByteBuffer[i]; } return ReadSize; } bool CNewPackage::SavePackage(const char *szPackageName) { FILE *fpPackage = fopen(szPackageName, "wb"); if (!fpPackage) { fprintf(stderr, " fopen %s error %s \n", szPackageName, strerror(errno)); return false; } //写入文件头标识 if( sizeof( MY_PACKAGE_FLAG ) != NewPackageXORWrite( MY_PACKAGE_FLAG, sizeof( MY_PACKAGE_FLAG ), fpPackage ) ) { fprintf(stderr, " 写入文件头信息失败 error %s \n", strerror(errno)); return false; } PackageInfo Info; Info.EncryptMethod = MY_ENCRYPT_METHOD_XOR; if( sizeof( Info ) != NewPackageXORWrite( &Info, sizeof( Info ), fpPackage ) ) { fprintf(stderr, " 写入文件信息 error %s \n", strerror(errno)); return false; } //将包裹中原有的文件 和 待添加的文件 全部放入这个WriteItems中 std::vector<FileItemForWrite> WriteItems; //预留一个sizeof(size_t)的大小,TotalHeadIndexSize是n个PackageItem所占的字节数 size_t TotalHeadIndexSize = sizeof(size_t); //处理待添加的文件 for (size_t i = 0; i < m_AddFiles.size(); i++) { FILE *fpRead = fopen(m_AddFiles[i].c_str(), "rb"); if (!fpRead) { fprintf(stderr, "fopen %s error %s \n", m_AddFiles[i].c_str(), strerror(errno)); return false; } fseek(fpRead, 0, SEEK_END); //填充FileItemForWrite信息 FileItemForWrite Item; Item.fp = fpRead; Item.FileOffset = 0; Item.FileSize = ftell(fpRead); Item.FileName = m_AddFiles[i]; size_t FileNameLen = Item.FileName.length() + 1; //申请一块内存存放文件的头部信息 NewPackageItem *pNewItem = (NewPackageItem *)malloc(sizeof(NewPackageItem) + FileNameLen); if (!pNewItem) { continue; } Item.pPackageItem = pNewItem; //填充文件的头部信息 pNewItem->FileNameLen = FileNameLen; pNewItem->FileSize = Item.FileSize; strcpy(pNewItem->FileName, m_AddFiles[i].c_str()); //更新TotalHeadIndexSize的大小 TotalHeadIndexSize += sizeof(NewPackageItem) + FileNameLen; //将待写入的文件添加到容器中 WriteItems.push_back(Item); } //处理原来包裹中已经存在的文件 for (size_t i = 0; i < m_PackageItems.size(); i++) { NewPackageItem *pItem = m_PackageItems[i]; FileItemForWrite Item; Item.fp = m_fpPackage; Item.FileOffset = pItem->OffsetPackage; Item.FileSize = pItem->FileSize; Item.FileName = pItem->FileName; size_t FileNameLen = Item.FileName.length(); NewPackageItem *pNewItem = (NewPackageItem *)malloc(sizeof(NewPackageItem) + FileNameLen); if (!pNewItem) { continue; } Item.pPackageItem = pNewItem; pNewItem->FileNameLen = FileNameLen; pNewItem->FileSize = Item.FileSize; strcpy(pNewItem->FileName, Item.FileName.c_str()); TotalHeadIndexSize += sizeof(NewPackageItem) + pNewItem->FileNameLen; WriteItems.push_back(Item); } //有多少个PackageItem size_t TotalPackageItems = WriteItems.size(); //开始写入文件 if( sizeof( TotalPackageItems ) != NewPackageXORWrite( &TotalPackageItems, sizeof( TotalPackageItems ), fpPackage ) ) { fprintf(stderr, "fwrite %lu error %s \n ", TotalPackageItems, strerror(errno)); return false; } //记录当前的文件指针位置 size_t IndexOffset = ftell(fpPackage); //预留TotalHeadIndexSize的空间供索引写入 fseek(fpPackage, TotalHeadIndexSize, SEEK_SET); //先写入文件 bool bError = false; size_t CurOffset = TotalHeadIndexSize; for (size_t i = 0; i < WriteItems.size(); i++) { FileItemForWrite &Item = WriteItems[i]; if (bError == false) { //填充偏移信息 Item.pPackageItem->OffsetPackage = CurOffset; //更新偏移信息 CurOffset += Item.FileSize; //将要写入的文件的文件指针移动到文件头 fseek(Item.fp, Item.FileOffset, SEEK_SET); char szBuffer[65536]; size_t LeftSize = Item.FileSize; while (1) { //实际要读取的大小 size_t ReadSize = LeftSize; //如果大于缓冲区,我们就只读取缓冲区大小的内容,剩余的下次读取 if (ReadSize > sizeof(szBuffer)) { ReadSize = sizeof(szBuffer); } //读取文件 size_t nReadBytes = fread(szBuffer, 1, ReadSize, Item.fp); //ReadSize必须要等于nReadBytes if (ReadSize != nReadBytes) { fprintf(stderr, "读取 %s 失败 %s\n",Item.FileName.c_str(), strerror(errno)); bError = true; break; } //写入包裹 if( nReadBytes != NewPackageXORWrite( szBuffer, nReadBytes, fpPackage ) ) { fprintf(stderr, "读取 %s 失败 %s\n",szPackageName, strerror(errno)); bError = true; break; } LeftSize -= nReadBytes; //如果剩余大小为0,就读取完成了 if (LeftSize == 0) { break; } } } //关闭需要打包的文件 if (Item.fp != m_fpPackage) { fclose(Item.fp); Item.fp = NULL; } } //如果没错误,写入索引 if (!bError) { fseek(fpPackage, IndexOffset, SEEK_SET); for (size_t i = 0; i < WriteItems.size(); i++) { FileItemForWrite &Item = WriteItems[i]; NewPackageItem *pItem = Item.pPackageItem; if (!bError) { //写入的长度应该是 sizeof(PackageItem) + 文件名的长度 size_t WriteSize = sizeof(NewPackageItem) + pItem->FileNameLen; if( WriteSize != NewPackageXORWrite( pItem, WriteSize, fpPackage ) ) { fprintf(stderr, "写入 %s 失败 %s\n",szPackageName,strerror(errno)); bError = true; } } free(pItem); } } fclose(fpPackage); return !bError; } bool CNewPackage::OpenPackage(const char *szPackageName) { ResetPackage(); m_fpPackage = fopen(szPackageName, "rb"); if (!m_fpPackage) { fprintf(stderr, "打开 %s 失败 %s\n",szPackageName, strerror(errno)); return false; } //读入文件头标识 char FileFlag[ sizeof( MY_PACKAGE_FLAG ) ]; if( sizeof( MY_PACKAGE_FLAG ) != NewPackageXORRead( FileFlag, sizeof( FileFlag ), m_fpPackage ) ) { fprintf(stderr, "读取文件头标识失败 %s\n", strerror(errno)); return false; } //判断一下我们读取到的文件头是否是我们定义的,做个最简单的格式判断 if( memcmp( FileFlag, MY_PACKAGE_FLAG, sizeof( FileFlag ) ) != 0 ) { fprintf( stderr, "包裹%s格式错误\n", szPackageName ); return false; } PackageInfo Info; if( sizeof( Info ) != NewPackageXORRead( &Info, sizeof( Info ), m_fpPackage ) ) { fprintf( stderr, "读取%s失败 %s\n", szPackageName, strerror(errno)); return false; } if( Info.EncryptMethod != MY_ENCRYPT_METHOD_XOR ) { fprintf( stderr, "无法识别%s的加密格式\n", szPackageName ); return false; } //先读取包裹中Item的个数 size_t PackageItemCount = 0; if( sizeof( PackageItemCount ) != NewPackageXORRead( &PackageItemCount, sizeof( PackageItemCount ), m_fpPackage ) ) { fprintf(stderr, "读取%s失败 %s\n", szPackageName, strerror(errno)); return false; } //根据读到的Item的个数遍历,把所有包裹中的文件放入m_PackageItems中 for (size_t i = 0; i < PackageItemCount; i++) { //先读取文件名的长度,因为文件名是变长的,方便后面申请内存 size_t FileNameLen = 0; if( sizeof( FileNameLen ) != NewPackageXORRead( &FileNameLen, sizeof( FileNameLen ), m_fpPackage ) ) { fprintf(stderr, "读取%s失败 %s\n", szPackageName, strerror(errno)); return false; } char *pBuffer = (char *)malloc(sizeof(NewPackageItem) + FileNameLen); if (!pBuffer) { fprintf(stderr, "读取%s失败 %s\n", szPackageName, strerror(errno)); return false; } NewPackageItem *pItem = (NewPackageItem *)pBuffer; pItem->FileNameLen = FileNameLen; //我们已经读取FileNameLen了,所以剩余读取的部分减掉FileNameLen size_t ReadSize = sizeof(NewPackageItem) + FileNameLen - sizeof(FileNameLen); if( ReadSize != NewPackageXORRead( pBuffer + sizeof( FileNameLen ), ReadSize, m_fpPackage ) ) { fprintf(stderr, "读取%s失败 %s\n", szPackageName, strerror(errno)); return false; } m_PackageItems.push_back(pItem); } return true; } bool CNewPackage::ExportPackageItem(const char *szFileName, CFileBuffer &FileBuffer) { //先从待添加的文件查找 for (size_t i = 0; i < m_AddFiles.size(); i++) { if (strcasecmp(m_AddFiles[i].c_str(), szFileName) == 0) { FILE *fp = fopen(szFileName, "rb"); if (!fp) { return false; } fseek(fp, 0, SEEK_END); size_t Size = ftell(fp); fseek(fp, 0, SEEK_SET); //申请内存 if (!FileBuffer.Create(Size)) { fclose(fp); return false; } //把文件读入内存中 if (Size != fread(FileBuffer.GetBuffer(), 1, Size, fp)) { fclose(fp); return false; } return true; } } //再从包裹文件中查找 for (size_t i = 0; i < m_PackageItems.size(); i++) { NewPackageItem *pItem = m_PackageItems[i]; if (strcasecmp(pItem->FileName, szFileName) == 0) { if (!FileBuffer.Create(pItem->FileSize)) { return false; } fseek(m_fpPackage, pItem->OffsetPackage, SEEK_SET); if( pItem->FileSize != NewPackageXORRead( FileBuffer.GetBuffer(), pItem->FileSize, m_fpPackage ) ) { return false; } return true; } } return false; }
main.cpp
#include "NewPackage.h" int main(void) { CNewPackage pkg; pkg.AddFile( "test1.txt" ); pkg.AddFile( "test2.txt" ); pkg.AddFile( "test3.txt" ); pkg.RemoveFile( "test2.txt" ); pkg.SavePackage( "test.pkg" ); pkg.OpenPackage( "test.pkg" ); CFileBuffer bf; pkg.ExportPackageItem( "test3.txt", bf ); bf.Save( "testout.txt" ); return 0; }