FXSend/WxWork/WXBizMsgCrypt.cpp

580 lines
14 KiB
C++
Raw Normal View History

2025-02-28 17:05:50 +08:00
#include "WXBizMsgCrypt.h"
#include <string.h>
#include <stdlib.h>
//#include <arpa/inet.h>
#include <Winsock2.h>
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
#include "openssl/aes.h"
#include "openssl/sha.h"
#include "openssl/evp.h"
#define FREE_PTR(ptr) \
if (NULL != (ptr)) {\
free (ptr);\
(ptr) = NULL;\
}
#define DELETE_PTR(ptr) \
if (NULL != (ptr)) {\
delete (ptr);\
(ptr) = NULL;\
}
namespace Tencent{
int WXBizMsgCrypt::VerifyURL(const std::string &sMsgSignature,
const std::string &sTimeStamp,
const std::string &sNonce,
const std::string &sEchoStr,
std::string &sReplyEchoStr)
{
if(0 != ValidateSignature(sMsgSignature,sTimeStamp,sNonce,sEchoStr) )
{
return WXBizMsgCrypt_ValidateSignature_Error;
}
//1.decode base64
std::string sAesData;
if(0 != DecodeBase64(sEchoStr,sAesData))
{
return WXBizMsgCrypt_DecodeBase64_Error;
}
//2.decode aes
std::string sAesKey;
std::string sNoEncryptData;
if(0 != GenAesKeyFromEncodingKey(m_sEncodingAESKey,sAesKey))
{
return WXBizMsgCrypt_IllegalAesKey;
}
if(0 != AES_CBCDecrypt(sAesData, sAesKey, &sNoEncryptData))
{
return WXBizMsgCrypt_DecryptAES_Error;
}
// 3. remove kRandEncryptStrLen str
if(sNoEncryptData.size() <= (kRandEncryptStrLen + kMsgLen))
{
return WXBizMsgCrypt_IllegalBuffer;
}
uint32_t iNetLen = *((const uint32_t *)(sNoEncryptData.c_str() + kRandEncryptStrLen));
uint32_t iMsgLen = ntohl(iNetLen);
if(sNoEncryptData.size() <= (kRandEncryptStrLen + kMsgLen + iMsgLen))
{
return WXBizMsgCrypt_IllegalBuffer;
}
sReplyEchoStr = sNoEncryptData.substr(kRandEncryptStrLen+kMsgLen,iMsgLen );
//4. validate Corpid
std::string sCorpid = sNoEncryptData.substr(kRandEncryptStrLen+kMsgLen+iMsgLen);
if(sCorpid != m_sCorpid)
{
return WXBizMsgCrypt_ValidateCorpid_Error;
}
return WXBizMsgCrypt_OK;
}
int WXBizMsgCrypt::DecryptMsg(const std::string &sMsgSignature,
const std::string &sTimeStamp,
const std::string &sNonce,
const std::string &sPostData,
std::string &sMsg)
{
//1.validate xml format
std::string sEncryptMsg;
if(0 != GetXmlField(sPostData,"Encrypt",sEncryptMsg))
{
return WXBizMsgCrypt_ParseXml_Error;
}
//2.validate signature
if(0 != ValidateSignature(sMsgSignature,sTimeStamp,sNonce,sEncryptMsg) )
{
return WXBizMsgCrypt_ValidateSignature_Error;
}
//3.decode base64
std::string sAesData;
if(0 != DecodeBase64(sEncryptMsg,sAesData))
{
return WXBizMsgCrypt_DecodeBase64_Error;
}
//4.decode aes
std::string sAesKey;
std::string sNoEncryptData;
if(0 != GenAesKeyFromEncodingKey(m_sEncodingAESKey,sAesKey))
{
return WXBizMsgCrypt_IllegalAesKey;
}
if(0 != AES_CBCDecrypt(sAesData, sAesKey, &sNoEncryptData))
{
return WXBizMsgCrypt_DecryptAES_Error;
}
// 5. remove kRandEncryptStrLen str
if(sNoEncryptData.size() <= (kRandEncryptStrLen + kMsgLen))
{
return WXBizMsgCrypt_IllegalBuffer;
}
uint32_t iNetLen = *((const uint32_t *)(sNoEncryptData.c_str() + kRandEncryptStrLen));
uint32_t iMsgLen = ntohl(iNetLen);
if(sNoEncryptData.size() <= (kRandEncryptStrLen + kMsgLen + iMsgLen))
{
return WXBizMsgCrypt_IllegalBuffer;
}
sMsg = sNoEncryptData.substr(kRandEncryptStrLen+kMsgLen,iMsgLen );
//6. validate corpid
std::string sCorpid = sNoEncryptData.substr(kRandEncryptStrLen+kMsgLen+iMsgLen);
if(sCorpid != m_sCorpid)
{
return WXBizMsgCrypt_ValidateCorpid_Error;
}
return WXBizMsgCrypt_OK;
}
int WXBizMsgCrypt::EncryptMsg(const std::string &sReplyMsg,
const std::string &sTimeStamp,
const std::string &sNonce,
std::string &sEncryptMsg)
{
if(0 == sReplyMsg.size())
{
return WXBizMsgCrypt_ParseXml_Error;
}
//1.add rand str ,len, corpid
std::string sNeedEncrypt;
GenNeedEncryptData(sReplyMsg,sNeedEncrypt);
//2. AES Encrypt
std::string sAesData;
std::string sAesKey;
if(0 != GenAesKeyFromEncodingKey(m_sEncodingAESKey,sAesKey))
{
return WXBizMsgCrypt_IllegalAesKey;
}
if(0 != AES_CBCEncrypt(sNeedEncrypt, sAesKey, &sAesData))
{
return WXBizMsgCrypt_EncryptAES_Error;
}
//3. base64Encode
std::string sBase64Data;
if( 0!= EncodeBase64(sAesData,sBase64Data) )
{
return WXBizMsgCrypt_EncodeBase64_Error;
}
//4. compute signature
std::string sSignature;
if(0!=ComputeSignature(m_sToken, sTimeStamp, sNonce, sBase64Data, sSignature))
{
return WXBizMsgCrypt_ComputeSignature_Error;
}
//5. Gen xml
if(0 != GenReturnXml(sBase64Data, sSignature, sTimeStamp, sNonce, sEncryptMsg) )
{
return WXBizMsgCrypt_GenReturnXml_Error ;
}
return WXBizMsgCrypt_OK;
}
int WXBizMsgCrypt::AES_CBCEncrypt( const std::string & objSource,
const std::string & objKey, std::string * poResult )
{
return AES_CBCEncrypt( objSource.data(), objSource.size(),
objKey.data(), objKey.size(), poResult );
}
int WXBizMsgCrypt::AES_CBCEncrypt( const char * sSource, const uint32_t iSize,
const char * sKey, uint32_t iKeySize, std::string * poResult )
{
if ( !sSource || !sKey || !poResult || iSize <= 0)
{
return -1;
}
poResult->clear();
int padding = kAesKeySize - iSize % kAesKeySize;
char * tmp = (char*)malloc( iSize + padding );
if(NULL == tmp)
{
return -1;
}
memcpy( tmp, sSource, iSize );
memset( tmp + iSize, padding, padding );
unsigned char * out = (unsigned char*)malloc( iSize + padding );
if(NULL == out)
{
FREE_PTR(tmp);
return -1;
}
unsigned char key[ kAesKeySize ] = { 0 };
unsigned char iv[ kAesIVSize ] = { 0 };
memcpy( key, sKey, iKeySize > kAesKeySize ? kAesKeySize : iKeySize );
memcpy(iv, key, sizeof(iv) < sizeof(key) ? sizeof(iv) : sizeof(key));
AES_KEY aesKey;
AES_set_encrypt_key( key, 8 * kAesKeySize, &aesKey );
AES_cbc_encrypt((unsigned char *)tmp, out,iSize + padding, &aesKey, iv, AES_ENCRYPT);
poResult->append((char*)out, iSize + padding);
FREE_PTR(tmp);
FREE_PTR(out);
return 0;
}
int WXBizMsgCrypt::AES_CBCDecrypt( const std::string & objSource,
const std::string & objKey, std::string * poResult )
{
return AES_CBCDecrypt( objSource.data(), objSource.size(),
objKey.data(), objKey.size(), poResult );
}
int WXBizMsgCrypt::AES_CBCDecrypt( const char * sSource, const uint32_t iSize,
const char * sKey, uint32_t iKeySize, std::string * poResult )
{
if ( !sSource || !sKey || iSize < kAesKeySize || iSize % kAesKeySize != 0 || !poResult)
{
return -1;
}
poResult->clear();
unsigned char * out = (unsigned char*)malloc( iSize );
if(NULL == out)
{
return -1;
}
unsigned char key[ kAesKeySize ] = { 0 };
unsigned char iv[ kAesIVSize ] = {0} ;
memcpy( key, sKey, iKeySize > kAesKeySize ? kAesKeySize : iKeySize );
memcpy(iv, key, sizeof(iv) < sizeof(key) ? sizeof(iv) : sizeof(key));
int iReturnValue = 0;
AES_KEY aesKey;
AES_set_decrypt_key( key, 8 * kAesKeySize, &aesKey );
AES_cbc_encrypt( (unsigned char *)sSource, out, iSize, &aesKey, iv ,AES_DECRYPT);
if( out[iSize-1] > 0 && out[iSize-1] <= kAesKeySize && (iSize - out[iSize-1]) > 0 )
{
poResult->append( (char *)out , iSize - out[iSize-1] );
} else {
iReturnValue = -1;
}
FREE_PTR(out);
return iReturnValue;
}
int WXBizMsgCrypt::EncodeBase64(const std::string sSrc, std::string & sTarget)
{
if(0 == sSrc.size() || kMaxBase64Size < sSrc.size())
{
return -1;
}
uint32_t iBlockNum = sSrc.size() / 3;
if (iBlockNum * 3 != sSrc.size())
{
iBlockNum++;
}
uint32_t iOutBufSize = iBlockNum * 4 + 1;
char * pcOutBuf = (char*)malloc( iOutBufSize);
if(NULL == pcOutBuf)
{
return -1;
}
int iReturn = 0;
int ret = EVP_EncodeBlock((unsigned char*)pcOutBuf, (const unsigned char*)sSrc.c_str(), sSrc.size());
if (ret > 0 && ret < (int)iOutBufSize)
{
sTarget.assign(pcOutBuf,ret);
}
else
{
iReturn = -1;
}
FREE_PTR(pcOutBuf);
return iReturn;
}
int WXBizMsgCrypt::DecodeBase64(const std::string sSrc, std::string & sTarget)
{
if(0 == sSrc.size() || kMaxBase64Size < sSrc.size())
{
return -1;
}
//<2F><><EFBFBD><EFBFBD>ĩβ=<3D>Ÿ<EFBFBD><C5B8><EFBFBD>
int iEqualNum = 0;
for(int n= sSrc.size() - 1; n>=0; --n)
{
if(sSrc.c_str()[n] == '=')
{
iEqualNum++;
}
else
{
break;
}
}
int iOutBufSize = sSrc.size();
char * pcOutBuf = (char*)malloc( iOutBufSize);
if(NULL == pcOutBuf)
{
return -1;
}
int iRet = 0;
int iTargetSize = 0;
iTargetSize = EVP_DecodeBlock((unsigned char*)pcOutBuf, (const unsigned char*)sSrc.c_str(), sSrc.size());
if(iTargetSize > iEqualNum && iTargetSize < iOutBufSize)
{
sTarget.assign(pcOutBuf, iTargetSize - iEqualNum);
}
else
{
iRet = -1;
}
FREE_PTR(pcOutBuf);
return iRet;
}
int WXBizMsgCrypt::ComputeSignature(const std::string sToken, const std::string sTimeStamp, const std::string & sNonce,
const std::string & sMessage, std::string & sSignature)
{
if( 0 == sToken.size() || 0 == sNonce.size() || 0 == sMessage.size() || 0 == sTimeStamp.size())
{
return -1;
}
//sort
std::vector< std::string > vecStr;
vecStr.push_back( sToken );
vecStr.push_back( sTimeStamp );
vecStr.push_back( sNonce );
vecStr.push_back( sMessage );
std::sort( vecStr.begin(), vecStr.end() );
std::string sStr = vecStr[0] + vecStr[1] + vecStr[2] + vecStr[3] ;
//compute
unsigned char output[SHA_DIGEST_LENGTH] = { 0 };
if( NULL == SHA1( (const unsigned char *)sStr.c_str(), sStr.size(), output ) )
{
return -1;
}
// to hex
sSignature.clear();
char tmpChar[ 8 ] = { 0 };
for( int i = 0; i < SHA_DIGEST_LENGTH; i++ )
{
snprintf( tmpChar, sizeof( tmpChar ), "%02x", 0xff & output[i] );
sSignature.append( tmpChar );
}
return 0;
}
int WXBizMsgCrypt::ValidateSignature(const std::string &sMsgSignature, const std::string &sTimeStamp,
const std::string &sNonce, const std::string & sEncryptMsg)
{
std::string sSignature;
if(0 != ComputeSignature(m_sToken, sTimeStamp, sNonce, sEncryptMsg, sSignature))
{
return -1;
}
if( sMsgSignature != sSignature)
{
return -1;
}
return 0;
}
int WXBizMsgCrypt::GenAesKeyFromEncodingKey( const std::string & sEncodingKey, std::string & sAesKey)
{
if(kEncodingKeySize != sEncodingKey.size())
{
return -1;
}
std::string sBase64 = sEncodingKey + "=";
int ret = DecodeBase64(sBase64, sAesKey);
if(0 != ret || kAesKeySize != sAesKey.size())
{
return -1;
}
return 0;
}
int WXBizMsgCrypt::GetXmlField(const std::string & sPostData, const std::string & sField, std::string &sEncryptMsg)
{
tinyxml2::XMLDocument xmlDoc;
if(tinyxml2::XML_SUCCESS != xmlDoc.Parse(sPostData.c_str(), sPostData.size()))
{
return -1;
}
tinyxml2::XMLElement * xmlElement = xmlDoc.FirstChildElement("xml");
if(NULL == xmlElement)
{
return -1;
}
tinyxml2::XMLElement * msgElement = xmlElement->FirstChildElement(sField.c_str());
if(NULL == msgElement)
{
return -1;
}
const char* pMsgBuf = msgElement->GetText();
if(NULL == pMsgBuf)
{
return -1;
}
sEncryptMsg = pMsgBuf;
return 0;
}
void WXBizMsgCrypt::GenRandStr(std::string & sRandStr, uint32_t len)
{
uint32_t idx = 0;
srand((unsigned)time(NULL));
char tempChar = 0;
sRandStr.clear();
while(idx < len)
{
tempChar = rand()%128;
if(isprint(tempChar))
{
sRandStr.append(1, tempChar);
++idx;
}
}
}
void WXBizMsgCrypt::GenNeedEncryptData(const std::string &sReplyMsg,std::string & sNeedEncrypt )
{
//random(16B)+ msg_len(4B) + msg + $corpid
std::string sRandStr;
GenRandStr(sRandStr,kRandEncryptStrLen);
uint32_t iXmlSize = sReplyMsg.size();
uint32_t iNSize = htonl(iXmlSize);
std::string sSize ;
sSize.assign((const char *)&iNSize,sizeof(iNSize));
sNeedEncrypt.erase();
sNeedEncrypt = sRandStr;
sNeedEncrypt += sSize;
sNeedEncrypt += sReplyMsg;
sNeedEncrypt += m_sCorpid;
}
int WXBizMsgCrypt::SetOneFieldToXml(tinyxml2::XMLDocument * pDoc, tinyxml2::XMLNode* pXmlNode, const char * pcFieldName,
const std::string & value, bool bIsCdata)
{
if(!pDoc || !pXmlNode || !pcFieldName)
{
return -1;
}
tinyxml2::XMLElement * pFiledElement = pDoc->NewElement(pcFieldName);
if(NULL == pFiledElement)
{
return -1;
}
tinyxml2::XMLText * pText = pDoc->NewText(value.c_str());
if(NULL == pText)
{
return -1;
}
pText->SetCData(bIsCdata);
pFiledElement->LinkEndChild(pText);
pXmlNode->LinkEndChild(pFiledElement);
return 0;
}
int WXBizMsgCrypt::GenReturnXml(const std::string & sEncryptMsg, const std::string & sSignature, const std::string & sTimeStamp,
const std::string & sNonce, std::string & sResult)
{
tinyxml2::XMLPrinter oPrinter;
tinyxml2::XMLNode* pXmlNode = NULL;
tinyxml2::XMLDocument * pDoc = new tinyxml2::XMLDocument();
if(NULL == pDoc)
{
return -1;
}
pXmlNode = pDoc->InsertEndChild( pDoc->NewElement( "xml" ) );
if(NULL == pXmlNode)
{
DELETE_PTR(pDoc);
return -1;
}
if(0 != SetOneFieldToXml(pDoc,pXmlNode,"Encrypt",sEncryptMsg,true))
{
DELETE_PTR(pDoc);
return -1;
}
if(0 != SetOneFieldToXml(pDoc,pXmlNode,"MsgSignature",sSignature,true))
{
DELETE_PTR(pDoc);
return -1;
}
if(0 != SetOneFieldToXml(pDoc,pXmlNode,"TimeStamp",sTimeStamp,true))
{
DELETE_PTR(pDoc);
return -1;
}
if(0 != SetOneFieldToXml(pDoc,pXmlNode,"Nonce",sNonce,true))
{
DELETE_PTR(pDoc);
return -1;
}
//ת<><D7AA>string
pDoc->Accept(&oPrinter);
sResult = oPrinter.CStr();
DELETE_PTR(pDoc);
return 0;
}
}