之前存在的弱点
- 文件虽然打包成一个文件,但是对于打包后的文件内容依然是明文的,不安全
常用加密算法
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;
}