This commit is contained in:
parent
0c7e06f3db
commit
edab6cfa30
@ -4,7 +4,7 @@
|
||||
#include <boost/beast/version.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/stacktrace.hpp>
|
||||
#include <boost/url/parse_path.hpp>
|
||||
#include <boost/url/parse.hpp>
|
||||
#include <boost/url/url_view.hpp>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
@ -75,7 +75,7 @@ void HttpSession::onRead(const boost::beast::error_code &error, std::size_t) {
|
||||
}
|
||||
|
||||
auto &request = m_parser->get();
|
||||
auto path = boost::urls::parse_path(request.target());
|
||||
auto path = boost::urls::parse_origin_form(request.target());
|
||||
if (!path) {
|
||||
LOG(error) << request.target() << " failed, error: " << path.error().message();
|
||||
errorReply(request, http::status::bad_request, "Illegal request-target");
|
||||
@ -83,7 +83,7 @@ void HttpSession::onRead(const boost::beast::error_code &error, std::size_t) {
|
||||
}
|
||||
auto router = m_router.lock();
|
||||
boost::urls::matches matches;
|
||||
auto handler = router->find(*path, matches);
|
||||
auto handler = router->find(path->encoded_segments(), matches);
|
||||
if (handler) {
|
||||
try {
|
||||
(*handler)(*this, request, matches);
|
||||
|
@ -51,6 +51,13 @@ Application::Application() : m_d{new ApplicationPrivate()} {
|
||||
m_database->open(m_settings->sqlitePath());
|
||||
m_sessionStore = Singleton<SessionStore>::construct(*m_ioContext->ioContext());
|
||||
|
||||
// clang-format off
|
||||
m_messageManager->subscribe<RegisterUrlHandler>([this](std::string_view url, RequestHandler handler) {
|
||||
LOG(info)<<"register url: "<<url;
|
||||
m_d->router->insert(url, handler);
|
||||
});
|
||||
// clang-format on
|
||||
|
||||
m_corporationContext = Singleton<WeChat::Corporation::Context>::construct(*m_ioContext->ioContext());
|
||||
m_corporationContext->start();
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
add_library(WeChat
|
||||
Corporation/Context.h Corporation/Context.cpp
|
||||
Corporation/WXBizMsgCrypt.h Corporation/WXBizMsgCrypt.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(WeChat
|
||||
PUBLIC Base
|
||||
PUBLIC Kylin::Core
|
||||
PUBLIC OpenSSL::Crypto
|
||||
)
|
@ -1,14 +1,18 @@
|
||||
#include "Context.h"
|
||||
#include "Base/HttpSession.h"
|
||||
#include "Base/Messages.h"
|
||||
#include "Core/Logger.h"
|
||||
#include "Core/MessageManager.h"
|
||||
#include "Http/Utility.h"
|
||||
#include "WXBizMsgCrypt.h"
|
||||
#include <boost/asio/defer.hpp>
|
||||
#include <boost/beast/core.hpp>
|
||||
#include <boost/beast/version.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/json/object.hpp>
|
||||
#include <boost/json/parse.hpp>
|
||||
#include <boost/json/serialize.hpp>
|
||||
#include <boost/url/parse.hpp>
|
||||
|
||||
namespace WeChat {
|
||||
namespace Corporation {
|
||||
@ -127,13 +131,43 @@ void Context::updateAccessToken() {
|
||||
|
||||
void Context::registerMoneyNote() {
|
||||
// 接口说明: https://developer.work.weixin.qq.com/document/10514
|
||||
// https://amass.fun/api/v1/wx/moneynote
|
||||
// /api/v1/wx/moneynote?msg_signature=1096371bf526609f8aaacc247b3bebf2fbe15969×tamp=1749021023&nonce=1748464240&echostr=1jccl4g1jk%2FMqIHQ445D9yOikKpcKkYOCMCLxqPSpKBtkMuKAuLGe8c6dbsTN5LZmRy0a45mxGvpqntvb3Vvxg%3D%3D
|
||||
using namespace Core;
|
||||
using namespace Older;
|
||||
using namespace boost::urls;
|
||||
using namespace boost::beast;
|
||||
auto manager = Singleton<MessageManager>::instance();
|
||||
manager->publish<RegisterUrlHandler>(
|
||||
"/api/v1/wx/moneynote",
|
||||
[this](HttpSession &session, const HttpRequest &request, const matches &matches) { LOG(info) << request.target(); });
|
||||
// clang-format off
|
||||
manager->publish<RegisterUrlHandler>("/api/v1/wx/moneynote", [this](HttpSession &session, const HttpRequest &request, const matches &matches) {
|
||||
auto path = boost::urls::parse_origin_form(request.target());
|
||||
auto params = path->params();
|
||||
auto msg_signature = params.find("msg_signature");
|
||||
auto timestamp = params.find("timestamp");
|
||||
auto nonce = params.find("nonce");
|
||||
|
||||
constexpr auto token = "BqnhzhsRqIJXO5k";
|
||||
constexpr auto encodingAESKey = "XMZ74tSRRe9MtEsEc9ihBTmvXxOisDrrFsukmcoPJR4";
|
||||
constexpr auto corpid = "ww1a786851749bdadc";
|
||||
std::string reply;
|
||||
Tencent::WXBizMsgCrypt wxcpt(token, encodingAESKey, corpid);
|
||||
if (request.method() == http::verb::get) {
|
||||
auto echostr = params.find("echostr");
|
||||
wxcpt.VerifyURL((*msg_signature).value, (*timestamp).value, (*nonce).value, (*echostr).value, reply);
|
||||
} else {
|
||||
LOG(info) << "received: " << request.body();
|
||||
}
|
||||
|
||||
LOG(info) << "reply: " << reply;
|
||||
http::response<boost::beast::http::string_body> s{boost::beast::http::status::ok, request.version()};
|
||||
s.set(http::field::server, BOOST_BEAST_VERSION_STRING);
|
||||
s.set(http::field::content_type, "application/json;charset=UTF-8");
|
||||
s.keep_alive(request.keep_alive());
|
||||
s.body() = reply;
|
||||
s.prepare_payload();
|
||||
session.reply(std::move(s));
|
||||
});
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
} // namespace Corporation
|
||||
|
404
WeChat/Corporation/WXBizMsgCrypt.cpp
Normal file
404
WeChat/Corporation/WXBizMsgCrypt.cpp
Normal file
@ -0,0 +1,404 @@
|
||||
|
||||
#include "WXBizMsgCrypt.h"
|
||||
#include <algorithm>
|
||||
#include <arpa/inet.h>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/xml_parser.hpp>
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "openssl/aes.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/sha.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 sReceiveId = sNoEncryptData.substr(kRandEncryptStrLen + kMsgLen + iMsgLen);
|
||||
if (sReceiveId != m_sReceiveId) {
|
||||
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
|
||||
boost::property_tree::ptree ptree;
|
||||
std::istringstream iss(sPostData);
|
||||
boost::property_tree::read_xml(iss, ptree);
|
||||
std::string sEncryptMsg = ptree.get<std::string>("Encrypt");
|
||||
|
||||
// 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 sReceiveId = sNoEncryptData.substr(kRandEncryptStrLen + kMsgLen + iMsgLen);
|
||||
if (sReceiveId != m_sReceiveId) {
|
||||
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;
|
||||
}
|
||||
|
||||
// 计算末尾=号个数
|
||||
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;
|
||||
}
|
||||
|
||||
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_sReceiveId;
|
||||
}
|
||||
|
||||
int WXBizMsgCrypt::GenReturnXml(const std::string &sEncryptMsg, const std::string &sSignature, const std::string &sTimeStamp,
|
||||
const std::string &sNonce, std::string &sResult) {
|
||||
boost::property_tree::ptree ptree;
|
||||
ptree.put("Encrypt", sEncryptMsg);
|
||||
ptree.put("MsgSignature", sSignature);
|
||||
ptree.put("TimeStamp", sTimeStamp);
|
||||
ptree.put("Nonce", sNonce);
|
||||
std::ostringstream oss;
|
||||
boost::property_tree::write_xml(oss, ptree);
|
||||
|
||||
sResult = oss.str();
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Tencent
|
107
WeChat/Corporation/WXBizMsgCrypt.h
Normal file
107
WeChat/Corporation/WXBizMsgCrypt.h
Normal file
@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
namespace Tencent {
|
||||
|
||||
static const unsigned int kAesKeySize = 32;
|
||||
static const unsigned int kAesIVSize = 16;
|
||||
static const unsigned int kEncodingKeySize = 43;
|
||||
static const unsigned int kRandEncryptStrLen = 16;
|
||||
static const unsigned int kMsgLen = 4;
|
||||
static const unsigned int kMaxBase64Size = 1000000000;
|
||||
enum WXBizMsgCryptErrorCode {
|
||||
WXBizMsgCrypt_OK = 0,
|
||||
WXBizMsgCrypt_ValidateSignature_Error = -40001,
|
||||
WXBizMsgCrypt_ParseXml_Error = -40002,
|
||||
WXBizMsgCrypt_ComputeSignature_Error = -40003,
|
||||
WXBizMsgCrypt_IllegalAesKey = -40004,
|
||||
WXBizMsgCrypt_ValidateCorpid_Error = -40005,
|
||||
WXBizMsgCrypt_EncryptAES_Error = -40006,
|
||||
WXBizMsgCrypt_DecryptAES_Error = -40007,
|
||||
WXBizMsgCrypt_IllegalBuffer = -40008,
|
||||
WXBizMsgCrypt_EncodeBase64_Error = -40009,
|
||||
WXBizMsgCrypt_DecodeBase64_Error = -40010,
|
||||
WXBizMsgCrypt_GenReturnXml_Error = -40011,
|
||||
};
|
||||
|
||||
class WXBizMsgCrypt {
|
||||
public:
|
||||
// 构造函数
|
||||
// @param sToken: 企业微信后台,开发者设置的Token
|
||||
// @param sEncodingAESKey: 企业微信后台,开发者设置的EncodingAESKey
|
||||
// @param sReceiveId: 不同场景含义不同,详见文档
|
||||
WXBizMsgCrypt(const std::string &sToken, const std::string &sEncodingAESKey, const std::string &sReceiveId)
|
||||
: m_sToken(sToken), m_sEncodingAESKey(sEncodingAESKey), m_sReceiveId(sReceiveId) {
|
||||
}
|
||||
// 验证URL
|
||||
// @param sMsgSignature: 签名串,对应URL参数的msg_signature
|
||||
// @param sTimeStamp: 时间戳,对应URL参数的timestamp
|
||||
// @param sNonce: 随机串,对应URL参数的nonce
|
||||
// @param sEchoStr: 随机串,对应URL参数的echostr
|
||||
// @param sReplyEchoStr: 解密之后的echostr,当return返回0时有效
|
||||
// @return:成功0,失败返回对应的错误码
|
||||
int VerifyURL(const std::string &sMsgSignature, const std::string &sTimeStamp, const std::string &sNonce,
|
||||
const std::string &sEchoStr, std::string &sReplyEchoStr);
|
||||
|
||||
// 检验消息的真实性,并且获取解密后的明文
|
||||
// @param sMsgSignature: 签名串,对应URL参数的msg_signature
|
||||
// @param sTimeStamp: 时间戳,对应URL参数的timestamp
|
||||
// @param sNonce: 随机串,对应URL参数的nonce
|
||||
// @param sPostData: 密文,对应POST请求的数据
|
||||
// @param sMsg: 解密后的原文,当return返回0时有效
|
||||
// @return: 成功0,失败返回对应的错误码
|
||||
int DecryptMsg(const std::string &sMsgSignature, const std::string &sTimeStamp, const std::string &sNonce,
|
||||
const std::string &sPostData, std::string &sMsg);
|
||||
|
||||
// 将企业微信回复用户的消息加密打包
|
||||
// @param sReplyMsg:企业微信待回复用户的消息,xml格式的字符串
|
||||
// @param sTimeStamp: 时间戳,可以自己生成,也可以用URL参数的timestamp
|
||||
// @param sNonce: 随机串,可以自己生成,也可以用URL参数的nonce
|
||||
// @param sEncryptMsg: 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串,
|
||||
// 当return返回0时有效
|
||||
// return:成功0,失败返回对应的错误码
|
||||
int EncryptMsg(const std::string &sReplyMsg, const std::string &sTimeStamp, const std::string &sNonce,
|
||||
std::string &sEncryptMsg);
|
||||
|
||||
private:
|
||||
std::string m_sToken;
|
||||
std::string m_sEncodingAESKey;
|
||||
std::string m_sReceiveId;
|
||||
|
||||
private:
|
||||
// AES CBC
|
||||
int AES_CBCEncrypt(const char *sSource, const uint32_t iSize, const char *sKey, unsigned int iKeySize, std::string *poResult);
|
||||
|
||||
int AES_CBCEncrypt(const std::string &objSource, const std::string &objKey, std::string *poResult);
|
||||
|
||||
int AES_CBCDecrypt(const char *sSource, const uint32_t iSize, const char *sKey, uint32_t iKeySize, std::string *poResult);
|
||||
|
||||
int AES_CBCDecrypt(const std::string &objSource, const std::string &objKey, std::string *poResult);
|
||||
|
||||
// base64
|
||||
int EncodeBase64(const std::string sSrc, std::string &sTarget);
|
||||
|
||||
int DecodeBase64(const std::string sSrc, std::string &sTarget);
|
||||
|
||||
// genkey
|
||||
int GenAesKeyFromEncodingKey(const std::string &sEncodingKey, std::string &sAesKey);
|
||||
|
||||
// signature
|
||||
int ComputeSignature(const std::string sToken, const std::string sTimeStamp, const std::string &sNonce,
|
||||
const std::string &sMessage, std::string &sSignature);
|
||||
|
||||
int ValidateSignature(const std::string &sMsgSignature, const std::string &sTimeStamp, const std::string &sNonce,
|
||||
const std::string &sEncryptMsg);
|
||||
|
||||
// get , set data
|
||||
void GenRandStr(std::string &sRandStr, uint32_t len);
|
||||
|
||||
void GenNeedEncryptData(const std::string &sReplyMsg, std::string &sNeedEncrypt);
|
||||
|
||||
int GenReturnXml(const std::string &sEncryptMsg, const std::string &sSignature, const std::string &sTimeStamp,
|
||||
const std::string &sNonce, std::string &sResult);
|
||||
};
|
||||
|
||||
} // namespace Tencent
|
@ -19,7 +19,7 @@ function cmake_scan() {
|
||||
${cmake_exe} -G Ninja -S ${base_path} -B ${build_path} \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DMbedTLS_DIR=${libraries_root}/mbedtls-3.6.3.1/lib/cmake/MbedTLS \
|
||||
-Dnng_DIR=${libraries_root}/nng-1.10.1/lib/cmake/nng \
|
||||
-Dnng_DIR=${libraries_root}/nng-1.11/lib/cmake/nng \
|
||||
-DBOOST_ROOT=${libraries_root}/boost_1_88_0
|
||||
}
|
||||
|
||||
@ -52,4 +52,4 @@ function main() {
|
||||
|
||||
main $@
|
||||
|
||||
# docker run -it -d --name ubuntu -v ~/Projects:/home/amass/Projects registry.cn-shenzhen.aliyuncs.com/amass_toolset/ubuntu_dev:24.04-qt6.9.0
|
||||
# docker run -it -d --name ubuntu --hostname Ubuntu -v ~/Projects:/home/amass/Projects registry.cn-shenzhen.aliyuncs.com/amass_toolset/ubuntu_dev:24.04-qt6.9.1
|
||||
|
Loading…
x
Reference in New Issue
Block a user