背景
写了一个基于Crypto++加密库中RSA非对称加密算法实现的对数据加密和解密的一个小程序,Crypto++加密库就不详细介绍了,这个库提供了很多知名的加解密算法,直接调用就好了,使用起来还是比较方便的。
写这篇文章,就是分享自己的学习心得。自己的密码学部分的知识学得不怎么好,还好有Crypto++开源库可以使用,弥补了对加解密部分的不足。现在,向大家分享使用Crypto++中的RSA非对称加密算法实现对数据的加密解密方面的知识。
程序编译设置注意事项
首先,先要下载Crypto++库的开源代码,然后,自己编译得到Crypto++的库文件。下载链接还有具体的编译步骤,可以参考这个平台上其他用户写的分享文章“使用VS2013编译Crypto++加密库”,里面有详细介绍。
在导入Crypto++的库文件到自己的工程项目的时候,要对自己的工程项目进行编译设置。主要一点就是:项目工程的属性中的“运行库”设置,要与编译Crypto++的库文件时设置的“运行库”选项要对应一致,否则程序会编译不过的。也就是要检查LIB库工程和本测试工程的:属性 —> C/C++ —> 代码生成 —> 运行库 是否统一。
如果编译出错,报告XX重复定义等错误,同样,要检查LIB库工程和本测试工程的:属性 —> C/C++ —> 代码生成 —> 运行库 是否统一。
程序设计原理
1. 产生公钥和私钥文件原理
RSA是非对称加密算法,所以它的加密和解密的密钥是不同的,它有自己的公钥和私钥。在正常的使用过程中,公钥一般是用来加密数据,私钥用来解密数据。反之,也可以。公钥可以公开,但是私钥不可以公开。
对于Crypto++库产生公私钥的原理如下:
-
首先用类RandomPool的方法Put()产生种子seed的byte型伪随机
-
RSAES_OAEP_SHA_Decryptor是一个解密的公钥密码系统在文件rsa.h 有如下定义:
-
typedef RSAES<OAEP<SHA>>::Decryptor RSAES_OAEP_SHA_Decryptor; 就是在这个类用前面产生的伪随机数和密钥长度keyLength生解密的密钥
-
接着,通过类FileSink打开文件szPrivateKeyFileName实行序列化操作,用HexEncoder把它转换为十六进制
-
最后,用DEREncode把上面处理好的密码对象写入文件
这样就得到私钥密码的密钥文件了。产生公钥文件的方法和产生私钥密码文件不同的地方就是使用了RSAES_OAEP_SHA_Encryptor是一个加密的公钥密码系统, 在文件rsa.h 有如下定义:
typedef RSAES<OAEP<SHA>>::Encryptor RSAES_OAEP_SHA_Encryptor; 是用上面产生的密钥密码系统priv来生成相应公钥。
2. 使用RSA公钥加密数据实现原理
RSA公钥加密可以分成以下两种形式:一种是公钥存储在文件中,加密时,从文件获取密钥;另一种是公钥存储在程序中,直接传递存储密钥的地址。虽然,这两种形式只是公钥传递的形式上的区别,RSA加密的原理还是一样的。但,从编程的角度,把这两个进行区分。
先介绍公钥存储在文件中的方式,那么RSA公钥加密的实现原理是:
-
首先用类RandomPool在种子seed下用方法Put()产生伪随机数,Seed可以任取
-
用类FileSource对公钥文件pubFilename进行一定的转换放入临时缓冲区,并把它从十六进制转换为byte型
-
然后用FileSource的对象pubFile 实例化公钥密码系统RSAES_OAEP_SHA_Encryptor生成对象pub
-
用类StringSink 把outstr添加到一个String对象,接着用HexEncoder把这个对象转换为十六进制
-
然后用伪随机数randPool、公钥密码系统pub和十六进制的String对象实例化一个公钥密码加密的过滤器,再用这个过滤器对字符串message进行加密把结果放到十六进制的字符串result里,这样就完成了对字符串的加密
那么,对于另一种公钥存储在程序中的方式,RSA公钥加密的原理和上面的区别,只是在第 2 步的区别,也就是用类StringSource对公钥文件pubFilename进行一定的转换放入临时缓冲区,并把它从十六进制转换为byte型。
3. 使用RSA私钥钥解密数据实现原理
同样,对应上述的公钥加密,私钥解密同样区分两种私钥获取的形式。
先介绍私钥存储在文件中的方式,那么RSA私钥解密的实现原理的基本流程跟加密的基本流程差不多,就使用了几个不同的类,但是这些类跟加密函数的对应类的功能是相对的,很容易理解。
-
用类FileSource对私钥文件privFilename进行一定的转换放入临时缓冲区,并把它从十六进制转换为byte型
-
然后用FileSource的对象privFile 实例化公钥密码系统RSAES_OAEP_SHA_Decryptor生成对象priv
-
用类StringSink 把outstr添加到一个String对象,接着用HexEncoder把这个对象转换为十六进制
-
然后用伪随机数randPool、私钥密码系统prov和十六进制的String对象实例化一个私钥密码解密的过滤器,再用这个过滤器对字符串message进行解密把结果放到十六进制的字符串result里,这样就完成了对字符串的解密
和加密一样,对于另一种私钥存储在程序中的方式,RSA私钥解密的原理和上面的区别,只是在第 1 步的区别,也就是用类StringSource对公钥文件provFilename进行一定的转换放入临时缓冲区,并把它从十六进制转换为byte型。
编程实现
1. 导入Crypt++库文件
//************************************************* // crypt++加密库的头文件和静态库 //************************************************* #include "crypt\\include\\rsa.h" #include "crypt\\include\\randpool.h" #include "crypt\\include\\hex.h" #include "crypt\\include\\files.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. RSA产生公私钥实现
BOOL GenerateRSAKey(DWORD dwRSAKeyLength, char *pszPrivateKeyFileName, char *pszPublicKeyFileName, BYTE *pSeed, DWORD dwSeedLength) { RandomPool randPool; randPool.Put(pSeed, dwSeedLength); // 生成RSA私钥 RSAES_OAEP_SHA_Decryptor priv(randPool, dwRSAKeyLength); HexEncoder privFile(new FileSink(pszPrivateKeyFileName)); // 打开文件实行序列化操作 priv.DEREncode(privFile); privFile.MessageEnd(); // 生成RSA公钥 RSAES_OAEP_SHA_Encryptor pub(priv); HexEncoder pubFile(new FileSink(pszPublicKeyFileName)); // 打开文件实行序列化操作 pub.DEREncode(pubFile); // 写密码对象pub到文件对象pubFile里 pubFile.MessageEnd(); return TRUE; }
3. RSA公钥加密实现
1> 公钥存储在文件实现
string RSA_Encrypt_ByFile(char *pszOriginaString, char *pszPublicKeyFileName, BYTE *pSeed, DWORD dwSeedLength) { RandomPool randPool; randPool.Put(pSeed, dwSeedLength); FileSource pubFile(pszPublicKeyFileName, TRUE, new HexDecoder); RSAES_OAEP_SHA_Encryptor pub(pubFile); // 加密 string strEncryptString; StringSource(pszOriginaString, TRUE, new PK_EncryptorFilter(randPool, pub, new HexEncoder(new StringSink(strEncryptString)))); return strEncryptString; }
2> 公钥存储在程序中
string RSA_Encrypt_ByMem(char *pszOriginaString, char *pszMemPublicKey, BYTE *pSeed, DWORD dwSeedLength) { RandomPool randPool; randPool.Put(pSeed, dwSeedLength); StringSource pubStr(pszMemPublicKey, TRUE, new HexDecoder); RSAES_OAEP_SHA_Encryptor pub(pubStr); // 加密 string strEncryptString; StringSource(pszOriginaString, TRUE, new PK_EncryptorFilter(randPool, pub, new HexEncoder(new StringSink(strEncryptString)))); return strEncryptString; }
4. RSA私钥解密实现
1> 公钥存储在文件实现
string RSA_Decrypt_ByFile(char *pszEncryptString, char *pszPrivateKeyFileName) { FileSource privFile(pszPrivateKeyFileName, TRUE, new HexDecoder); RSAES_OAEP_SHA_Decryptor priv(privFile); string strDecryptString; StringSource(pszEncryptString, TRUE, new HexDecoder(new PK_DecryptorFilter(GlobalRNG(), priv, new StringSink(strDecryptString)))); return strDecryptString; }
2> 公钥存储在程序中
string RSA_Decrypt_ByMem(char *pszEncryptString, char *pszMemPrivateKey) { StringSource privStr(pszMemPrivateKey, TRUE, new HexDecoder); RSAES_OAEP_SHA_Decryptor priv(privStr); string strDecryptString; StringSource(pszEncryptString, TRUE, new HexDecoder(new PK_DecryptorFilter(GlobalRNG(), priv, new StringSink(strDecryptString)))); return strDecryptString; }
程序测试
在main函数中调用上面封装好的函数进行测试,main函数为:
char g_szPubKey[] = "30819D300D06092A864886F70D010101050003818B0030818702818100F0CE882D7CCB990323A6DB1B775EBE8F2910BFE75B4B580EF8C5089BB25FEDEEABCE2BBD2AC64A138E47F96A6C39152FE98067C0B4F5DC28F8D9394325ADB12A90A9598FF7A2A7211DEF974FC8A005D0CBCDE059FB8F7F9D214C5BAC2532CEB8EC4041AEAB19E80B8C4020F4A50102F9E738647E2384EA2FCD30C3681559CF6F020111"; char g_szPrivKey[] = "30820275020100300D06092A864886F70D01010105000482025F3082025B02010002818100F0CE882D7CCB990323A6DB1B775EBE8F2910BFE75B4B580EF8C5089BB25FEDEEABCE2BBD2AC64A138E47F96A6C39152FE98067C0B4F5DC28F8D9394325ADB12A90A9598FF7A2A7211DEF974FC8A005D0CBCDE059FB8F7F9D214C5BAC2532CEB8EC4041AEAB19E80B8C4020F4A50102F9E738647E2384EA2FCD30C3681559CF6F020111028180210D49E8203005F15F3F0F03C5170B18AB4892CF70EC39434F52426FB91C39C162E0100AE7C0DCFDAA1DF50E9B67351AA7942251AA68051EB8BE7145739A599220030CF5E35ED4DEA41DD6E955722AE46153339FE7417BD00ADF53B368EAB6E71FAE0F7F394A34C91612B0F11AEC5525DB84DD982E6BF10CE74F177FA51ADC51024100F80296900AF134CCC5AC12C58D741C735F5EE9CBDFB8C1B1EB039BF078E37B09322074193B7B0AE5A60B544DDDB9159294E91744404A2C7CDF96287F5483D691024100F8908925066C3ED9AC8EAFE63A59D56FCBEC354A3DD513489DEDA70E42338CD2AEBDEEF685148123B31A55CA27B2A59CA53E2352DA284F30585A5D6B571245FF02410091E367A0066FC4B4B083565616F901AD4728C5C3384E900E4E021F7E653A849BFF5E6269320C24871661046A09F4670AEE2EC264620D8394BFC1BD781398D891024057BA8AC1C608162EB55F896050D46972C0717C38520EF7BF46CC5914175D7CFF107F4547F2BBF157E4DC1E47594E1C55677F57C2E395C19897A76C44009D09A5024100BBB92D3E8776B52FA20303E39FE8AE862637BB75880D82C6580C3217445C4A95BFB6E94120AD62AADC313418A350FF21B0ED861848626CC0F55936F750B44FC4"; int _tmain(int argc, _TCHAR* argv[]) { char szPrivateFile[] = "privatefile"; char szPublicFile[] = "publicfile"; char szSeed[] = "WriteBugWriteBug"; char szOriginalString[] = "WRITE-BUG技术共享平台 - 一个专注校园计算机技术交流共享的平台"; /* 密钥在文件方式 */ // 生成RSA公私密钥对 GenerateRSAKey(1024, szPrivateFile, szPublicFile, (BYTE *)szSeed, ::lstrlen(szSeed)); // RSA公钥加密字符串 string strEncryptString = RSA_Encrypt_ByFile(szOriginalString, szPublicFile, (BYTE *)szSeed, ::lstrlen(szSeed)); // RSA私钥解密字符串 string strDecryptString = RSA_Decrypt_ByFile((char *)strEncryptString.c_str(), szPrivateFile); // 显示 printf("原文字符串:\n[%d]%s\n", ::lstrlen(szOriginalString), szOriginalString); printf("密文字符串:\n[%d]%s\n", strEncryptString.length(), strEncryptString.c_str()); printf("解密明文字符串:\n[%d]%s\n", strDecryptString.length(), strDecryptString.c_str()); printf("\n\n"); /* 密钥在内存方式 */ // RSA公钥加密字符串 string strEncryptString_Mem = RSA_Encrypt_ByMem(szOriginalString, g_szPubKey, (BYTE *)szSeed, ::lstrlen(szSeed)); // RSA私钥解密字符串 string strDecryptString_Mem = RSA_Decrypt_ByMem((char *)strEncryptString_Mem.c_str(), g_szPrivKey); // 显示 printf("原文字符串:\n[%d]%s\n", ::lstrlen(szOriginalString), szOriginalString); printf("密文字符串:\n[%d]%s\n", strEncryptString_Mem.length(), strEncryptString_Mem.c_str()); printf("解密明文字符串:\n[%d]%s\n", strDecryptString_Mem.length(), strDecryptString_Mem.c_str()); system("pause"); return 0; }
根据图片显示,数据成功被加密和解密,两种公钥存储形式测试均成功。
总结
使用Crypto++库的好处之一,就是方便易用,即使你不了解具体的加密算法,但你只要知道要使用的加密算法这个名称,依然可以做出使用相应的加密算法进行数据加解密。
参考
参考自《Windows黑客编程技术详解》一书
文章评论