头文件清单
#pragma once
/*
本模块功能:
AES加解密,SHA256生成内容文摘,RSA非对称加解密。
測試環境:
[1]VS2008 SP1
[2]WinXP SP3
[3]cryptopp561
測試時間:
[1]2012-7 by kagula
更新记录:
[1]2012-10 修正AES加解密时key长度没有对齐的问题
备注:
[1]cryptopp三要素:XXXSource指的是源,Filter指的是过滤器,XXXSink是容器。
[2]AES加密产生的密文以零结尾? 密文跟明文的长度一样?
[3]要二进制编码的话参考testAES函数。
参考资料
[1]Sink的概念
http://www.cryptopp.com/wiki/Sink
[2]块为单位编解码
http://topic.csdn.net/u/20070512/19/297431db-3b82-480d-96a7-9101d4abf13c.html
[3]GCM模式
http://www.cryptopp.com/wiki/GCM
[4]Crypto++库在VS 2005中的使用——RSA加解密
http://www.cnblogs.com/cxun/archive/2008/07/30/743541.html
[5]CryptoPP 5.6.1 SHA256 results incorrect with 32 bit MSVC 2005 release build
http://sourceforge.net/apps/trac/cryptopp/ticket/7
[6]使用cryptopp实现密钥协商
http://bbs.gameres.com/thread_118476.html
[7]AES对称加密算法原理
http://www.2cto.com/Article/201112/113465.html
相关:
高级加密标准(Advanced Encryption Standard,AES),在密码学中又称Rijndael加密法,
是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,传递非机密文件。
*/
#include <string>
#include <config.h> //byte
//
/*
功能
二进制数据块,转,16进制字符串
入口
s 二进制数据块
s_len 二进制数据块 长度
出口
16进制ASCII码字符串
*/
std::string Bytes2Hex(byte * s,unsigned int s_len);
/*
功能
16进制字符串,转,二进制数据块
入口
encoded 16进制ASCII码字符串
出口
d 二进制数据块
d_len 二进制数据块的长度
*/
bool Hex2Bytes(std::string encoded,byte *d,unsigned int &d_len);
//
/*
功能
CFB模式AES算法字符串加密
入口
sKey 钥匙
plainText 明文
出口
密文
*/
std::string CFB_AESEncryptStr(std::string sKey,const char *plainText);
std::string CFB_AESEncryptStr(const char *plainText);
/*
入口
sKey 钥匙
cipherText 密文
出口
明文
*/
std::string CFB_AESDecryptStr(std::string sKey,const char *cipherText);
std::string CFB_AESDecryptStr(std::string cipherText);
/*
功能
CFB模式AES算法字符串加密,返回BASE64编码字符串,明文自动16字节对齐,使用0x30填充。
入口
key 钥匙,长度必须为 "16/24/32" 三者之一
plainText 明文
出口
密文
*/
std::string CFB_AESEncryptStr_BASE64(const char *key,const char *plainText);
/*
功能
CFB模式AES算法字符串解密,对BASE64编码字符串进行解码
入口
key 钥匙,长度必须为 "16/24/32" 三者之一
cipherText 密文
出口
明文
*/
std::string CFB_AESDecryptStr_BASE64(const char *key,const char *cipherText);
/*
功能
GCM模式AES算法字符串加密
入口:
sKey 钥匙。可以为空字符串。
sAuth Auth(解码时必须), 一般Auth存放明文的签名。可以为空字符串。
sPlain 明文
出口:
iv 增量(解码时必须) ,用于防止同样的钥匙和明文出现同样的密文。
原则上同样的IV,不能在同key上重复出现。
sCipher 密文
*/
bool GCM_AESEncryptStr(const std::string &sKey,const std::string &sAuth,const std::string &sPlain,
byte iv[16],std::string &sCipher);
/*
入口:
sKey 钥匙
sAuth Auth(解码时必须), 一般Auth存放明文的签名
sCipher 密文
iv 增量(解码时必须)
出口:
sPlain 明文
*/
bool GCM_AESDecryptStr(const std::string &sKey,const std::string &sAuth,const std::string &sCipher,const byte iv[16],
std::string &sPlain);
//
/*
入口:
msg 要产生文摘的内容
出口:
digest 文摘
*/
void SHA256_Cal(const std::string &msg,std::string &digest);
/*
入口:
msg 内容
digest 文摘
出口:
校验成功返回 True
*/
bool SHA256_Verify(const std::string &msg,const std::string &digest);
//
/*
入口
strSeed 种子,可以取值"seed"。
出口
strPri 私钥 解密用
strPub 公钥 加密用
*/
void GenerateRSAKey(const std::string &strSeed,std::string &strPri, std::string &strPub);
/*
入口
strSeed 调用GenerateRSAKey时用的种子。
strPub 公钥
plainText 明文
出口
返回密文
*/
std::string RSAEncryptStr(const std::string &strPub,const std::string &strSeed, const char *plainText);
/*
入口
strSeed 调用GenerateRSAKey时用的种子。
strPri 私钥
cipherText 密文
出口
返回明文
*/
std::string RSADecryptStr(const std::string &strPri, const char *cipherText);
//
源文件清单
#include "stdafx.h"
//
#include <aes.h>
#include <files.h> // FileSource, FileSink
#include <osrng.h> // AutoSeededRandomPool
#include <modes.h> // CFB_Mode
#include <Hex.h> // HexEncoder
#include <Base64.h> // Base64Encoder
#include <gcm.h> // GCM模式支持
#include <sha.h>
#include <rsa.h> // RSAES_OAEP_SHA_Decryptor
/*
[S1]安装并编译cryptopp561到"E:\SDK\cryptopp561"
[S2]
"E:\SDK\cryptopp561\Win32\Output\Debug\cryptlib.lib"文件复制并重命名到
"E:\SDK\cryptopp561\Win32\Output\cryptlib_D.lib"。
[S3]
"E:\SDK\cryptopp561\Win32\Output\Release\cryptlib.lib"文件复制到
"E:\SDK\cryptopp561\Win32\Output\"目录下。
*/
#ifdef _DEBUG
#pragma comment(lib,"cryptlib_D")
#else
#pragma comment(lib,"cryptlib")
#endif
using namespace CryptoPP;
//
#include <iostream> //std:cerr
#include <sstream> //std::stringstream
#include <string>
using namespace std;
//
//引用自http://www.cnblogs.com/jclugia/archive/2011/11/29/2267692.html
std::string Bytes2Hex(byte * s,unsigned int s_len)
{
std::string encoded;
StringSource ss(s, s_len, true,
new HexEncoder(new StringSink(encoded))
);
//std::cout << encoded << std::endl;
return encoded;
}
//string encoded = "FFEEDDCCBBAA99887766554433221100";
bool Hex2Bytes(std::string encoded,byte *d,unsigned int &d_len)
{
HexDecoder decoder;
decoder.Put( (byte*)encoded.data(), encoded.size() );
decoder.MessageEnd();
size_t size = (size_t) decoder.MaxRetrievable();
if(size && size<=d_len)
{
decoder.Get(d, size);
d_len = size;
return true;
}
return false;
}
/*
std::string Bytes2Base64(byte * s,unsigned int s_len)
{
std::string encoded;
StringSource ss(s, s_len, true,
new Base64Encoder(new StringSink(encoded))
);
return encoded;
}
*/
//
/*
这个例子好像可以稍微改一下,實現二進制數據塊的加解密,懒得测试了。
*/
/*
void testAES()
{
AutoSeededRandomPool rnd;
// Generate a random key
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
rnd.GenerateBlock( key, key.size() );
//iv 是一个增量值,可以随便取一个字符串
//加密使用的iv要和解密的一样
byte iv[AES::BLOCKSIZE];
rnd.GenerateBlock(iv, AES::BLOCKSIZE);
char plainText[] = "Hello! How are you.";
int messageLen = (int)strlen(plainText) + 1;
//
// Encrypt
CFB_Mode<AES>::Encryption cfbEncryption(key, key.size(), iv);
cfbEncryption.ProcessData((byte*)plainText, (byte*)plainText, messageLen);
//
// Decrypt
CFB_Mode<AES>::Decryption cfbDecryption(key, key.size(), iv);
cfbDecryption.ProcessData((byte*)plainText, (byte*)plainText, messageLen);
}
*/
std::string CFB_AESEncryptStr(std::string sKey,const char *plainText)
{
std::string outstr;
//填key
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
memset(key,0x30,key.size() );
sKey.size()<=AES::DEFAULT_KEYLENGTH?memcpy(key,sKey.c_str(),sKey.size()):memcpy(key,sKey.c_str(),AES::DEFAULT_KEYLENGTH);
//填iv
byte iv[AES::BLOCKSIZE];
memset(iv,0x30,AES::BLOCKSIZE);
AES::Encryption aesEncryption((byte *)key, AES::DEFAULT_KEYLENGTH);
CFB_Mode_ExternalCipher::Encryption cfbEncryption(aesEncryption, iv);
StreamTransformationFilter cfbEncryptor(cfbEncryption, new HexEncoder(new StringSink(outstr)));
cfbEncryptor.Put((byte *)plainText, strlen(plainText));
cfbEncryptor.MessageEnd();
return outstr;
}
std::string CFB_AESEncryptStr(const char *plainText)
{
std::string outstr;
//填key
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
memset(key,0x30,key.size() );
//填iv
byte iv[AES::BLOCKSIZE];
memset(iv,0x30,AES::BLOCKSIZE);
//加密
CFB_Mode<AES>::Encryption cfbEncryption(key, key.size(), iv);
cfbEncryption.ProcessData((byte*)plainText, (byte*)plainText, strlen(plainText)+1);
int nL = strlen(plainText);
outstr = Bytes2Hex((byte *)plainText, nL);
return outstr;
}
std::string CFB_AESDecryptStr(std::string sKey,const char *cipherText)
{
std::string outstr;
//填key
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
memset(key,0x30,key.size() );
sKey.size()<=AES::DEFAULT_KEYLENGTH?memcpy(key,sKey.c_str(),sKey.size()):memcpy(key,sKey.c_str(),AES::DEFAULT_KEYLENGTH);
//填iv
byte iv[AES::BLOCKSIZE];
memset(iv,0x30,AES::BLOCKSIZE);
CFB_Mode<AES >::Decryption cfbDecryption((byte *)key, AES::DEFAULT_KEYLENGTH, iv);
HexDecoder decryptor(new StreamTransformationFilter(cfbDecryption, new StringSink(outstr)));
decryptor.Put((byte *)cipherText, strlen(cipherText));
decryptor.MessageEnd();
return outstr;
}
std::string CFB_AESDecryptStr(std::string cipherText)
{
#define OUT_BUF_SIZE 256
unsigned int nSize = OUT_BUF_SIZE;
char plainText[OUT_BUF_SIZE];
Hex2Bytes(cipherText,(byte *)plainText,nSize);
//填key
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
memset(key,0x30,key.size() );
//填iv
byte iv[AES::BLOCKSIZE];
memset(iv,0x30,AES::BLOCKSIZE);
//解密
CFB_Mode<AES>::Decryption cfbDecryption(key, key.size(), iv);
cfbDecryption.ProcessData((byte*)plainText, (byte*)plainText, OUT_BUF_SIZE);
//返回
std::string r = plainText;
return r;
}
std::string CFB_AESEncryptStr_BASE64(const char *key,const char *plainText)
{
std::string outstr;
//填iv.begin
byte iv[AES::BLOCKSIZE];
memset(iv,0x30,AES::BLOCKSIZE);
//填iv.end
AES::Encryption aesEncryption((byte *)key, AES::DEFAULT_KEYLENGTH);
CFB_Mode_ExternalCipher::Encryption cfbEncryption(aesEncryption, iv);
StreamTransformationFilter cfbEncryptor(cfbEncryption, new Base64Encoder(new StringSink(outstr)));
cfbEncryptor.Put((byte *)plainText, strlen(plainText));
//对明文进行16字节对齐.begin
const int nPadding = strlen(plainText)%16;
byte *padding;
if(nPadding!=0)
{
padding = new byte[16-nPadding];
memset(padding,0,16-nPadding);
cfbEncryptor.Put(padding, 16-nPadding);
}
//对明文进行16字节对齐.end
cfbEncryptor.MessageEnd();
delete padding;
return outstr;
}
std::string CFB_AESDecryptStr_BASE64(const char *key,const char *cipherText)
{
std::string outstr;
//填iv.begin
byte iv[AES::BLOCKSIZE];
memset(iv,0x30,AES::BLOCKSIZE);
//填iv.end
CFB_Mode<AES >::Decryption cfbDecryption((byte *)key, AES::DEFAULT_KEYLENGTH, iv);
Base64Decoder decryptor(new StreamTransformationFilter(cfbDecryption, new StringSink(outstr)));
decryptor.Put((byte *)cipherText, strlen(cipherText));
decryptor.MessageEnd();
return outstr;
}
const int TAG_SIZE = 16;
bool GCM_AESEncryptStr(const std::string &sKey,const std::string &sAuth,const std::string &sPlain,
byte iv[16],std::string &sCipher)
{
#ifdef _DEBUG
std::cout << "Algorithm name=" << AES::StaticAlgorithmName() << std::endl;
std::cout << "AES::DEFAULT_KEYLENGTH=" <<AES::DEFAULT_KEYLENGTH<<std::endl;
std::cout << "AES::MIN_KEYLENGTH=" <<AES::MIN_KEYLENGTH<<std::endl;
std::cout << "AES::MAX_KEYLENGTH=" <<AES::MAX_KEYLENGTH<<std::endl;
#endif
/*
if(sKey.length()!=AES::DEFAULT_KEYLENGTH)
sKey.resize(AES::DEFAULT_KEYLENGTH,' ');
*/
AutoSeededRandomPool rnd;
//iv 是一个增量值,可以随便取一个字符串
//加密使用的iv要和解密的一样
std::cout<<"AES::BLOCKSIZE="<<AES::BLOCKSIZE<<std::endl;
assert(AES::BLOCKSIZE==16);
rnd.GenerateBlock(iv, AES::BLOCKSIZE);
// Encrypted, with Tag
try
{
GCM< AES >::Encryption e;
e.SetKeyWithIV( (byte*)sKey.c_str(), AES::DEFAULT_KEYLENGTH, iv, sizeof(iv) );
AuthenticatedEncryptionFilter ef( e,
new StringSink( sCipher ), false,
TAG_SIZE /* MAC_AT_END */
); // AuthenticatedEncryptionFilter
// AuthenticatedEncryptionFilter::ChannelPut
// defines two channels: DEFAULT_CHANNEL and AAD_CHANNEL
// DEFAULT_CHANNEL is encrypted and authenticated
// AAD_CHANNEL is authenticated
ef.ChannelPut( AAD_CHANNEL, (byte *)sAuth.c_str(), sAuth.length() );
ef.ChannelMessageEnd(AAD_CHANNEL);
// Authenticated data *must* be pushed before
// Confidential/Authenticated data. Otherwise
// we must catch the BadState exception
ef.ChannelPut( DEFAULT_CHANNEL, (byte *)sPlain.c_str(), sPlain.length() );
ef.ChannelMessageEnd(DEFAULT_CHANNEL);
}
catch( CryptoPP::Exception& e )
{
cerr << "Caught Exception..." << endl;
cerr << e.what() << endl;
cerr << endl;
return false;
}
return true;
}
bool GCM_AESDecryptStr(const std::string &sKey,const std::string &sAuth,const std::string &sCipher,const byte iv[16],
std::string &sPlain)
{
try
{
GCM< AES >::Decryption d;
d.SetKeyWithIV( (byte*)sKey.c_str(), AES::DEFAULT_KEYLENGTH, iv, sizeof(iv) );
// Break the cipher text out into it's
// components: Encrypted and MAC
string enc = sCipher.substr( 0, sCipher.length()-TAG_SIZE );
string mac = sCipher.substr( sCipher.length()-TAG_SIZE );//最后TAG_SIZE个字节是MAC码
// Sanity checks
if( sCipher.size() != enc.size() + mac.size() )
return false;
if( TAG_SIZE != mac.size() )
return false;
// Object *will* throw an exception
// during decryption\verification _if_
// verification fails.
AuthenticatedDecryptionFilter df( d, NULL,
AuthenticatedDecryptionFilter::Flags::MAC_AT_BEGIN | AuthenticatedDecryptionFilter::Flags::THROW_EXCEPTION, TAG_SIZE );
// The order of the following calls are important
df.ChannelPut( DEFAULT_CHANNEL, (byte *)mac.data(), mac.size() );
df.ChannelPut( AAD_CHANNEL, (byte *)sAuth.data(), sAuth.size() );
df.ChannelPut( DEFAULT_CHANNEL, (byte *)enc.data(), enc.size() );
// If the object throws, it will most likely occur
// during ChannelMessageEnd()
df.ChannelMessageEnd( AAD_CHANNEL );
df.ChannelMessageEnd( DEFAULT_CHANNEL );
// If the object does not throw, here's the only
// opportunity to check the data's integrity
if(! df.GetLastResult() )
return false;
// Remove data from channel
string retrieved;
size_t n = (size_t)-1;
// Plain text recovered from enc.data()
df.SetRetrievalChannel( DEFAULT_CHANNEL );
n = (size_t)df.MaxRetrievable();
retrieved.resize( n );
if( n > 0 ) { df.Get( (byte*)retrieved.data(), n ); }
sPlain = retrieved;
}
catch( CryptoPP::Exception& e )
{
cerr << "Caught Exception..." << endl;
cerr << e.what() << endl;
cerr << endl;
return false;
}
return true;
}
void SHA256_Cal(const std::string &msg,std::string &digest)
{
SHA256 sha256;
char byDigest[ 32 + 1 ];
memset(byDigest,0,sizeof(byDigest));
sha256.CalculateDigest((byte*)byDigest, (const byte *)msg.c_str(), msg.size());
digest = byDigest;
}
bool SHA256_Verify(const std::string &msg,const std::string &digest)
{
SHA256 sha256;
return sha256.VerifyDigest( (byte*)digest.c_str(), (const byte *)msg.c_str(), msg.size() );
}
void GenerateRSAKey(const std::string &strSeed,std::string &strPri, std::string &strPub)
{
RandomPool randPool;
randPool.Put((byte *)strSeed.c_str(), strSeed.length());
RSAES_OAEP_SHA_Decryptor priv(randPool, 1024);//keylength设为1024
HexEncoder privString(new StringSink(strPri));
priv.DEREncode(privString);
privString.MessageEnd();
RSAES_OAEP_SHA_Encryptor pub(priv);
HexEncoder pubString(new StringSink(strPub));
pub.DEREncode(pubString);
pubString.MessageEnd();
}
std::string RSAEncryptStr(const std::string &strPub,const std::string &strSeed, const char *plainText)
{
StringSource pubString(strPub, true, new HexDecoder);
RSAES_OAEP_SHA_Encryptor pub(pubString);
RandomPool randPool;
randPool.Put((byte *)strSeed.c_str(), strSeed.length());
string result;
StringSource(plainText, true, new PK_EncryptorFilter(randPool, pub, new HexEncoder(new StringSink(result))));
return result;
}
RandomPool & GlobalRNG()
{
static RandomPool randomPool;
return randomPool;
}
std::string RSADecryptStr(const std::string &strPri, const char *cipherText)
{
StringSource privString(strPri, true, new HexDecoder);
RSAES_OAEP_SHA_Decryptor priv(privString);
string result;
StringSource(cipherText, true, new HexDecoder(new PK_DecryptorFilter(GlobalRNG(), priv, new StringSink(result))));
return result;
}