Carewhose的文章

  • 基于Crypto++库的AES对称加密实现对数据的加解密

    背景写了一个基于Crypto++加密库中AES对称加密算法实现的对数据加密和解密的一个小程序,Crypto++加密库就不详细介绍了,这个库提供了很多知名的加解密算法,直接调用就好了,使用起来还是比较方便的。
    写这篇文章,就是分享自己的学习心得。自己的密码学部分的知识学得不怎么好,还好有Crypto++开源库可以使用,弥补了对加解密部分的不足。现在,向大家分享使用Crypto++中的AES对称加密算法实现对数据的加密解密方面的知识。
    程序编译设置注意事项首先,先要下载Crypto++库的开源代码,然后,自己编译得到Crypto++的库文件。下载链接还有具体的编译步骤,可以参考平台上其他用户写的分享文章“使用VS2013编译Crypto++加密库”,里面有详细介绍。
    在导入Crypto++的库文件到自己的工程项目的时候,要对自己的工程项目进行编译设置。主要一点就是:项目工程的属性中的“运行库”设置,要与编译Crypto++的库文件时设置的“运行库”选项要对应一致,否则程序会编译不过的。也就是要检查LIB库工程和本测试工程的:属性 —> C/C++ —> 代码生成 —> 运行库 是否统一。
    如果编译出错,报告XX重复定义等错误,同样,要检查LIB库工程和本测试工程的:属性 —> C/C++ —> 代码生成 —> 运行库 是否统一。
    程序设计原理1. AES加密原理AES对称加密就是对16byte(128bit)数据进行加密的过程,也就是把这128位数据通过一系列的变化变成另一个128位数据。
    由于,我们加密的数据大小长度都是未知的,也不都是128位数据对齐。所以,在加密过程的时候,要对自己的数据进行填充,以128位大小对齐,本文的例子是以0作为填充数填充的。
    加密实现的核心部分,即使用Crypto++库中提供的AES加密器 AESEncryption 来进行实现的。数据加密主要分成三个步骤:

    声明AES加密器
    设置AES加密密钥
    对数据进行加密,返回加密结果

    // 声明AES加密器AESEncryption aesEncryptor;… …(省略)// 设置AES加密密钥aesEncryptor.SetKey(pAESKey, dwAESKeySize);… …(省略)// 对数据进行加密,返回加密结果aesEncryptor.ProcessAndXorBlock(inBlock, xorBlock, outBlock);… …(省略)
    对于本文介绍的程序,它可以对任意数据长度的数据进行加密,所以,和上面介绍的加密步骤相比,会多两个步骤:

    声明AES加密器
    将加密数据用0填充,按128位大小进行对齐
    设置AES加密密钥
    获取128位数据,并对数据进行加密,返回128位加密结果
    继续获取下一个128位数据,重复第 4 步操作,直到数据获取完毕

    2. AES解密原理解密部分的原理和加密部分原理是一样的,因为AES是对称加密,所以原理相同。
    解密实现的核心部分,即使用Crypto++库中提供的AES解密器 AESDecryption来进行实现的。数据加密主要分成三个步骤:

    声明AES加密器
    设置AES加密密钥
    对数据进行加密,返回加密结果

    // 声明AES解密器AESDecryption aesDecryptor;… …(省略)// 设置AES解密密钥aesDecryptor.SetKey(pAESKey, dwAESKeySize);… …(省略)// 对数据进行解密,返回解密结果aesDecryptor.ProcessAndXorBlock(inBlock, xorBlock, outBlock);… …(省略)
    理论上,解密的密文长度是按128位长度对齐的。但是,为了程序容错性更好,所以,在解密之前,还是和加密一样,先对解密的数据以0填充,按128位数据大小对齐。如果输入的密文长度正确,那么做这一步操作是不影响的;如果输入的密文长度不正确,那么这一步操作可以确保程序不出现错误。
    那么,解密部分的解密流程和加密流程是一样的:

    声明AES解密器
    将解密数据用0填充,按128位大小进行对齐
    设置AES解密密钥
    获取128位数据,并对数据进行解密,返回128位解密结果
    继续获取下一个128位数据,重复第 4 步操作,直到数据获取完毕

    编程实现1. 导入Crypto++库文件//*************************************************// crypt++加密库的头文件和静态库//*************************************************#include "crypt\\include\\aes.h"using namespace CryptoPP; // 命名空间#ifdef _DEBUG #ifdef _WIN64 #pragma comment(lib, "crypt\\lib\\x64\\debug\\cryptlib.lib") #else #pragma comment(lib, "crypt\\lib\\x86\\debug\\cryptlib.lib") #endif#else #ifdef _WIN64 #pragma comment(lib, "crypt\\lib\\x64\\release\\cryptlib.lib") #else #pragma comment(lib, "crypt\\lib\\x86\\release\\cryptlib.lib") #endif#endif//*************************************************
    2. AES加密实现// 加密// 输入:原文内容、原文内容长度、密钥内容、密钥内容长度// 输出:密文内容、密文内容长度BOOL AES_Encrypt(BYTE *pOriginalData, DWORD dwOriginalDataSize, BYTE *pAESKey, DWORD dwAESKeySize, BYTE **ppEncryptData, DWORD *pdwEncryptData){ // 加密器 AESEncryption aesEncryptor; // 加密原文数据块 unsigned char inBlock[AES::BLOCKSIZE]; // 加密后密文数据块 unsigned char outBlock[AES::BLOCKSIZE]; // 必须设定全为0 unsigned char xorBlock[AES::BLOCKSIZE]; DWORD dwOffset = 0; BYTE *pEncryptData = NULL; DWORD dwEncryptDataSize = 0; // 计算原文长度, 并按 128位 即 16字节 对齐, 不够则 填充0 对齐 // 商 DWORD dwQuotient = dwOriginalDataSize / AES::BLOCKSIZE; // 余数 DWORD dwRemaind = dwOriginalDataSize % AES::BLOCKSIZE; if (0 != dwRemaind) { dwQuotient++; } // 申请动态内存 dwEncryptDataSize = dwQuotient * AES::BLOCKSIZE; pEncryptData = new BYTE[dwEncryptDataSize]; if (NULL == pEncryptData) { return FALSE; } // 设置密钥 aesEncryptor.SetKey(pAESKey, dwAESKeySize); do { // 置零 ::RtlZeroMemory(inBlock, AES::BLOCKSIZE); ::RtlZeroMemory(xorBlock, AES::BLOCKSIZE); ::RtlZeroMemory(outBlock, AES::BLOCKSIZE); // 获取加密块 if (dwOffset <= (dwOriginalDataSize - AES::BLOCKSIZE)) { ::RtlCopyMemory(inBlock, (PVOID)(pOriginalData + dwOffset), AES::BLOCKSIZE); } else { ::RtlCopyMemory(inBlock, (PVOID)(pOriginalData + dwOffset), (dwOriginalDataSize - dwOffset)); } // 加密 aesEncryptor.ProcessAndXorBlock(inBlock, xorBlock, outBlock); // 构造 ::RtlCopyMemory((PVOID)(pEncryptData + dwOffset), outBlock, AES::BLOCKSIZE); // 更新数据 dwOffset = dwOffset + AES::BLOCKSIZE; dwQuotient--; } while (0 < dwQuotient); // 返回数据 *ppEncryptData = pEncryptData; *pdwEncryptData = dwEncryptDataSize; return TRUE;}
    3. AES解密实现// 解密// 输入:密文内容、密文内容长度、密钥内容、密钥内容长度// 输出:解密后明文内容、解密后明文内容长度BOOL AES_Decrypt(BYTE *pEncryptData, DWORD dwEncryptData, BYTE *pAESKey, DWORD dwAESKeySize, BYTE **ppDecryptData, DWORD *pdwDecryptData){ // 解密器 AESDecryption aesDecryptor; // 解密密文数据块 unsigned char inBlock[AES::BLOCKSIZE]; // 解密后后明文数据块 unsigned char outBlock[AES::BLOCKSIZE]; // 必须设定全为0 unsigned char xorBlock[AES::BLOCKSIZE]; DWORD dwOffset = 0; BYTE *pDecryptData = NULL; DWORD dwDecryptDataSize = 0; // 计算密文长度, 并按 128位 即 16字节 对齐, 不够则填充0对齐 // 商 DWORD dwQuotient = dwEncryptData / AES::BLOCKSIZE; // 余数 DWORD dwRemaind = dwEncryptData % AES::BLOCKSIZE; if (0 != dwRemaind) { dwQuotient++; } // 申请动态内存 dwDecryptDataSize = dwQuotient * AES::BLOCKSIZE; pDecryptData = new BYTE[dwDecryptDataSize]; if (NULL == pDecryptData) { return FALSE; } // 设置密钥 aesDecryptor.SetKey(pAESKey, dwAESKeySize); do { // 置零 ::RtlZeroMemory(inBlock, AES::BLOCKSIZE); ::RtlZeroMemory(xorBlock, AES::BLOCKSIZE); ::RtlZeroMemory(outBlock, AES::BLOCKSIZE); // 获取解密块 if (dwOffset <= (dwDecryptDataSize - AES::BLOCKSIZE)) { ::RtlCopyMemory(inBlock, (PVOID)(pEncryptData + dwOffset), AES::BLOCKSIZE); } else { ::RtlCopyMemory(inBlock, (PVOID)(pEncryptData + dwOffset), (dwEncryptData - dwOffset)); } // 解密 aesDecryptor.ProcessAndXorBlock(inBlock, xorBlock, outBlock); // 构造 ::RtlCopyMemory((PVOID)(pDecryptData + dwOffset), outBlock, AES::BLOCKSIZE); // 更新数据 dwOffset = dwOffset + AES::BLOCKSIZE; dwQuotient--; } while (0 < dwQuotient); // 返回数据 *ppDecryptData = pDecryptData; *pdwDecryptData = dwDecryptDataSize; return TRUE;}
    程序测试我们在main函数中,调用上面封装好的加解密函数,对数据进行加解密进行测试。main函数为:
    int _tmain(int argc, _TCHAR* argv[]){ BYTE *pEncryptData = NULL; DWORD dwEncryptDataSize = 0; BYTE *pDecryptData = NULL; DWORD dwDecryptDataSize = 0; char szOriginalData[] = "CDIY - www.coderdiy.com - 专注计算机技术交流分享"; char szAESKey[] = "DemonGanDemonGan"; BOOL bRet = FALSE; // 加密 bRet = AES_Encrypt((BYTE *)szOriginalData, (1 + ::lstrlen(szOriginalData)), (BYTE *)szAESKey, ::lstrlen(szAESKey), &pEncryptData, &dwEncryptDataSize); if (FALSE == bRet) { return 1; } // 解密 bRet = AES_Decrypt(pEncryptData, dwEncryptDataSize, (BYTE *)szAESKey, ::lstrlen(szAESKey), &pDecryptData, &dwDecryptDataSize); if (FALSE == bRet) { return 2; } // 显示 printf("原文数据:\n"); ShowData((BYTE *)szOriginalData, (1 + ::lstrlen(szOriginalData))); printf("密文数据:\n"); ShowData(pEncryptData, dwEncryptDataSize); printf("解密后数据:\n"); ShowData(pDecryptData, dwDecryptDataSize); // 释放内存 delete[]pEncryptData; pEncryptData = NULL; delete[]pDecryptData; pDecryptData = NULL; system("pause"); return 0;}
    测试的结果为:

    通过上述结果,测试成功。
    总结本身使用Crypto++库中的AES加解密不是很复杂,也容易理解。其中,需要注意一点就是,如果实现对任意长度数据的加密,那么就必须要对数据填充,按128位大小进行对齐,填充的数据任意。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2018-12-20 12:25:40
  • DLL延迟加载

    背景我们在开发程序的时候,通常会使用第三方库,但是并不是所有的第三方库都会提供静态库文件,而是提供动态库文件。这样,我们的程序就需要相应的DLL文件才能加载启动。
    现在,我们想大家介绍一种DLL延迟加载技术,使用延迟加载方式编译链接可执行文件。这样,可执行程序就可以先加载执行,所依赖的DLL在正式调用时才被加载进来。
    这样做的好处就是,我们可以把必需的DLL文件以资源的形式包含到我们的程序中,并使用DLL延迟加载技术延迟加载必需的DLL。这样,在正式调用必需的DLL之前,程序都是可以正常执行的,我们可以在程序运行的时候,就把在资源中的DLL释放到本地,所以,程序所有功能就可以正常执行了。我们只需把EXE文件发送给用户,而不需要附加DLL文件了,也不需要担心程序会丢失DLL文件。
    实现原理DLL延迟加载原理本程序以加载第三方库——skin++库为例子进行讲解演示。我们在导入库文件,编码完成后,对程序编译链接等到可执行文件。我们使用PEview.exe查看可执行文件的导入表,就可知道可执行文件所必需的DLL文件。

    上面可以知道,导入表中有SkinPPWTL.dll文件,也就是说,程序加载运行的时候,SkinPPWTL.dll文件必需存在,否则程序不能正常加载启动。
    DLL延迟加载技术的原理,其实就是在导入表中去掉SkinPPWTL.dll这一项,等到DLL被正式调用的时候,才会加载DLL文件。这样,程序在正式调用DLL之前,都是可以正常执行的。
    DLL延迟加载实现DLL延迟加载的实现,并不需要任何编码,只需要设置下开发环境的链接选项即可。本程序使用的是VS2013开发环境,还是继续上述skin++皮肤库的例子进行讲解。DLL延迟加载实现的步骤为:
    属性—>链接器—>输入—>延迟加载的DLL—>输入:SkinPPWTL.dll

    这样,DLL延迟加载就完成了。这时,我们编译链接,再用PEview.exe查看可执行程序的导入表信息:

    可见,导入表中没有SkinPPWTL.dll文件了。所以,程序在正式调用该DLL之前,程序都是可以加载执行的。
    接下来,我们就把SkinPPWTL.dll作为资源导入,在加载皮肤之前就释放DLL到本地上,这样程序正常加载执行了。
    编码实现皮肤加载// 加载界面皮肤void LoadSkin(){ // 加载皮肤 // 获取当前目录 char szCurrentPath[MAX_PATH] = { 0 }; GetCurrentPath(szCurrentPath, MAX_PATH); // 构造路径 ::lstrcat(szCurrentPath, "\\Skins\\"); ::lstrcat(szCurrentPath, "dogmax2.ssk"); // 加载皮肤 ::skinppLoadSkin(szCurrentPath);}
    资源释放// 释放资源BOOL FreeMyResource(UINT uiResouceName, char *lpszResourceType, char *lpszSaveFileName){ HRSRC hRsrc = ::FindResource(NULL, MAKEINTRESOURCE(uiResouceName), lpszResourceType); if (NULL == hRsrc) { FreeRes_ShowError("FindResource"); return FALSE; } DWORD dwSize = ::SizeofResource(NULL, hRsrc); if (0 >= dwSize) { FreeRes_ShowError("SizeofResource"); return FALSE; } HGLOBAL hGlobal = ::LoadResource(NULL, hRsrc); if (NULL == hGlobal) { FreeRes_ShowError("LoadResource"); return FALSE; } LPVOID lpVoid = ::LockResource(hGlobal); if (NULL == lpVoid) { FreeRes_ShowError("LockResource"); return FALSE; } FILE *fp = NULL; fopen_s(&fp, lpszSaveFileName, "wb+"); if (NULL == fp) { FreeRes_ShowError("LockResource"); return FALSE; } fwrite(lpVoid, sizeof(char), dwSize, fp); fclose(fp); return TRUE;}
    程序测试我们直接运行EXE程序,程序正常执行,DLL成功释放,皮肤加载成功:

    总结该程序实现并不难,只是多了个设置链接选项。这样,程序就会更加简洁,而且方便易用。这算是程序开发的一个小技巧吧,希望能对大家有所帮助。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2018-12-20 12:31:21

发送私信

你总是喊着努力努力,可是又坚持了多久

13
文章数
14
评论数
eject