mirror of
https://github.com/TencentCloud/TIMSDK.git
synced 2026-10-18 13:42:19 +08:00
375 lines
9.2 KiB
C++
375 lines
9.2 KiB
C++
/*
|
|
* Module: GenerateTestUserSig
|
|
*
|
|
* Function: 用于获取 TIMLogin 接口所必须的 UserSig,腾讯云使用 UserSig 进行安全校验,保护您的 IM 功能不被盗用
|
|
*/
|
|
#include "GenerateTestUserSig.h"
|
|
#include "./zlib/zlib.h"
|
|
|
|
#include <time.h>
|
|
#include <stdio.h>
|
|
#include <memory>
|
|
#include <assert.h>
|
|
#include <Windows.h>
|
|
#include <bcrypt.h>
|
|
|
|
#pragma comment(lib, "bcrypt.lib")
|
|
|
|
class GenerateTestUserSigImpl
|
|
{
|
|
public:
|
|
GenerateTestUserSigImpl(uint32_t SDKAppID, const std::string& secretKey, uint32_t currTime, uint32_t expireTime);
|
|
~GenerateTestUserSigImpl();
|
|
|
|
std::string genTestUserSig(const std::string& userId);
|
|
private:
|
|
std::string genHMACSHA256(const std::string& userId);
|
|
void base64Enc(char* dest, const void* src, uint32_t length);
|
|
char bit6ToAscii(uint8_t a);
|
|
private:
|
|
const uint32_t m_SDKAppID;
|
|
const uint32_t m_currTime;
|
|
const uint32_t m_expireTime;
|
|
const std::string m_secretKey;
|
|
};
|
|
|
|
GenerateTestUserSigImpl::GenerateTestUserSigImpl(uint32_t SDKAppID, const std::string& secretKey, uint32_t currTime, uint32_t expireTime)
|
|
: m_SDKAppID(SDKAppID)
|
|
, m_currTime(currTime)
|
|
, m_expireTime(expireTime)
|
|
, m_secretKey(secretKey)
|
|
{
|
|
// 参数检测,请先给 GenerateTestUserSig 类的 _SDKAppID 、_EXPIRETIME 和 _SECRETKEY 成员变量赋值,避免触发 DEBUG 版本的断言框。
|
|
|
|
assert(m_SDKAppID > 0);
|
|
assert(m_currTime > 0);
|
|
assert(m_expireTime > 0);
|
|
assert(!m_secretKey.empty());
|
|
}
|
|
|
|
GenerateTestUserSigImpl::~GenerateTestUserSigImpl()
|
|
{
|
|
|
|
}
|
|
|
|
std::string GenerateTestUserSigImpl::genTestUserSig(const std::string& userId)
|
|
{
|
|
// 参数检测,请传入非空的 userId 参数。
|
|
assert(!userId.empty());
|
|
|
|
std::string sig = genHMACSHA256(userId);
|
|
|
|
size_t dataBufferLen = userId.size() + 256; // userId长度 + 其他字段的长度(不超过256)
|
|
std::unique_ptr<char[]> dataBuffer(new char[dataBufferLen]);
|
|
|
|
// 为避免引入 json 库,直接拼接 json 形式的字符串。
|
|
|
|
int count = ::sprintf_s(dataBuffer.get(), dataBufferLen,
|
|
"{"
|
|
"\"TLS.ver\":\"2.0\","
|
|
"\"TLS.identifier\":\"%s\","
|
|
"\"TLS.sdkappid\":%lu,"
|
|
"\"TLS.expire\":%lu,"
|
|
"\"TLS.time\":%lu,"
|
|
"\"TLS.sig\":\"%s\""
|
|
"}", userId.c_str(), m_SDKAppID, m_expireTime, m_currTime, sig.c_str());
|
|
if (count == -1)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
// json 数据压缩
|
|
|
|
uLong upperBound = compressBound(count);
|
|
std::unique_ptr<Bytef[]> zipDest(new Bytef[count]);
|
|
uLongf zipDestLen = count;
|
|
int ret = compress2(zipDest.get(), &zipDestLen, (const Bytef*)dataBuffer.get(), count, Z_BEST_SPEED);
|
|
if (ret != Z_OK)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
// base64 编码
|
|
|
|
size_t base64Len = (zipDestLen / 3 + 1) * 4 + 1;
|
|
std::unique_ptr<char[]> base64Buffer(new char[base64Len]);
|
|
base64Enc(base64Buffer.get(), zipDest.get(), zipDestLen);
|
|
|
|
std::string result(base64Buffer.get());
|
|
for (auto it = result.begin(); it != result.end(); ++it)
|
|
{
|
|
switch (*it)
|
|
{
|
|
case '+':
|
|
*it = '*';
|
|
break;
|
|
case '/':
|
|
*it = '-';
|
|
break;
|
|
case '=':
|
|
*it = '_';
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// 资源清理函数
|
|
|
|
static void closeAlgHandle(BCRYPT_ALG_HANDLE* p)
|
|
{
|
|
if (*p)
|
|
{
|
|
BCryptCloseAlgorithmProvider(*p, 0);
|
|
*p = NULL;
|
|
}
|
|
}
|
|
|
|
static void closeHashHandle(BCRYPT_HASH_HANDLE* p)
|
|
{
|
|
if (*p)
|
|
{
|
|
BCryptDestroyHash(*p);
|
|
*p = NULL;
|
|
}
|
|
}
|
|
|
|
std::string GenerateTestUserSigImpl::genHMACSHA256(const std::string& userId)
|
|
{
|
|
// 拼接用于生成 HMACSHA256 的字符串。
|
|
|
|
size_t dataBufferLen = userId.size() + 256;
|
|
std::unique_ptr<char[]> dataBuffer(new char[dataBufferLen]);
|
|
|
|
int count = ::sprintf_s(dataBuffer.get(), dataBufferLen,
|
|
"TLS.identifier:%s\n"
|
|
"TLS.sdkappid:%lu\n"
|
|
"TLS.time:%lu\n"
|
|
"TLS.expire:%lu\n", userId.c_str(), m_SDKAppID, m_currTime, m_expireTime);
|
|
if (count == -1)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
std::string data(dataBuffer.get(), count);
|
|
|
|
// 打开加密算法的句柄。
|
|
BCRYPT_ALG_HANDLE alg = NULL;
|
|
NTSTATUS status = BCryptOpenAlgorithmProvider(
|
|
&alg,
|
|
BCRYPT_SHA256_ALGORITHM,
|
|
NULL,
|
|
BCRYPT_ALG_HANDLE_HMAC_FLAG);
|
|
if (!BCRYPT_SUCCESS(status))
|
|
{
|
|
printf("[Error] BCryptOpenAlgorithmProvider failed: 0x%x\n", status);
|
|
return "";
|
|
}
|
|
|
|
// 资源托管,自动释放
|
|
std::unique_ptr<BCRYPT_ALG_HANDLE, decltype(closeAlgHandle)*> algAutoDel(&alg, closeAlgHandle);
|
|
|
|
// 计算存放 hashBuffer 对象的 dataBuffer 大小。
|
|
DWORD cbHashObject = 0;
|
|
DWORD cbData = 0;
|
|
status = BCryptGetProperty(
|
|
alg,
|
|
BCRYPT_OBJECT_LENGTH,
|
|
(PBYTE)&cbHashObject,
|
|
sizeof(DWORD),
|
|
&cbData,
|
|
0);
|
|
if (!BCRYPT_SUCCESS(status))
|
|
{
|
|
printf("[Error] BCryptGetProperty failed: 0x%x\n", status);
|
|
return "";
|
|
}
|
|
|
|
// 分配 hashBuffer 对象的内存
|
|
std::unique_ptr<BYTE[]> hashObject(new BYTE[cbHashObject]);
|
|
if (!hashObject.get())
|
|
{
|
|
printf("[Error] memory allocation failed\n");
|
|
return "";
|
|
}
|
|
|
|
// 计算 hashBuffer 的长度。
|
|
DWORD cbHash = 0;
|
|
status = BCryptGetProperty(
|
|
alg,
|
|
BCRYPT_HASH_LENGTH,
|
|
(PBYTE)&cbHash,
|
|
sizeof(DWORD),
|
|
&cbData,
|
|
0);
|
|
if (!BCRYPT_SUCCESS(status))
|
|
{
|
|
printf("[Error] BCryptGetProperty: 0x%x\n", status);
|
|
return "";
|
|
}
|
|
|
|
// 分配 hashBuffer dataBuffer 的内存
|
|
std::unique_ptr<BYTE[]> hashBuffer(new BYTE[cbHash]);
|
|
if (!hashBuffer.get())
|
|
{
|
|
printf("[Error] memory allocation failed\n");
|
|
return "";
|
|
}
|
|
|
|
// 创建 hashBuffer 对象。
|
|
BCRYPT_HASH_HANDLE hash = NULL;
|
|
status = BCryptCreateHash(
|
|
alg,
|
|
&hash,
|
|
hashObject.get(),
|
|
cbHashObject,
|
|
(PBYTE)m_secretKey.c_str(),
|
|
m_secretKey.size(),
|
|
0);
|
|
if (!BCRYPT_SUCCESS(status))
|
|
{
|
|
printf("[Error]BCryptCreateHash: 0x%x\n", status);
|
|
return "";
|
|
}
|
|
|
|
// 资源托管,自动释放
|
|
std::unique_ptr<BCRYPT_HASH_HANDLE, decltype(closeHashHandle)*> hashAutoDel(&hash, closeHashHandle);
|
|
|
|
// 生成 hashBuffer 值。
|
|
status = BCryptHashData(
|
|
hash,
|
|
(PBYTE)data.c_str(),
|
|
data.size(),
|
|
0);
|
|
if (!BCRYPT_SUCCESS(status))
|
|
{
|
|
printf("[Error]BCryptHashData: 0x%x\n", status);
|
|
return "";
|
|
}
|
|
|
|
// 关闭 hashBuffer 对象
|
|
status = BCryptFinishHash(
|
|
hash,
|
|
hashBuffer.get(),
|
|
cbHash,
|
|
0);
|
|
if (!BCRYPT_SUCCESS(status))
|
|
{
|
|
printf("[Error]BCryptFinishHash: 0x%x\n", status);
|
|
return "";
|
|
}
|
|
|
|
// base64 编码
|
|
size_t base64Len = (cbHash / 3 + 1) * 4 + 1;
|
|
std::unique_ptr<char[]> base64Buffer(new char[base64Len + 1]);
|
|
base64Enc(base64Buffer.get(), hashBuffer.get(), cbHash);
|
|
|
|
return std::string(base64Buffer.get());
|
|
}
|
|
|
|
void GenerateTestUserSigImpl::base64Enc(char *dest, const void *src, uint32_t length)
|
|
{
|
|
uint32_t i = 0;
|
|
uint32_t j = 0;
|
|
uint8_t a[4] = { 0 };
|
|
for (i = 0; i < length / 3; ++i)
|
|
{
|
|
a[0] = (((uint8_t*)src)[i * 3 + 0]) >> 2;
|
|
a[1] = (((((uint8_t*)src)[i * 3 + 0]) << 4) | ((((uint8_t*)src)[i * 3 + 1]) >> 4)) & 0x3F;
|
|
a[2] = (((((uint8_t*)src)[i * 3 + 1]) << 2) | ((((uint8_t*)src)[i * 3 + 2]) >> 6)) & 0x3F;
|
|
a[3] = (((uint8_t*)src)[i * 3 + 2]) & 0x3F;
|
|
for (j = 0; j < 4; ++j)
|
|
{
|
|
*dest++ = bit6ToAscii(a[j]);
|
|
}
|
|
}
|
|
|
|
switch (length % 3)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
a[0] = (((uint8_t*)src)[i * 3 + 0]) >> 2;
|
|
a[1] = ((((uint8_t*)src)[i * 3 + 0]) << 4) & 0x3F;
|
|
*dest++ = bit6ToAscii(a[0]);
|
|
*dest++ = bit6ToAscii(a[1]);
|
|
*dest++ = '=';
|
|
*dest++ = '=';
|
|
break;
|
|
case 2:
|
|
a[0] = (((uint8_t*)src)[i * 3 + 0]) >> 2;
|
|
a[1] = (((((uint8_t*)src)[i * 3 + 0]) << 4) | ((((uint8_t*)src)[i * 3 + 1]) >> 4)) & 0x3F;
|
|
a[2] = ((((uint8_t*)src)[i * 3 + 1]) << 2) & 0x3F;
|
|
*dest++ = bit6ToAscii(a[0]);
|
|
*dest++ = bit6ToAscii(a[1]);
|
|
*dest++ = bit6ToAscii(a[2]);
|
|
*dest++ = '=';
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
// 尾部添加结束符
|
|
*dest = '\0';
|
|
}
|
|
|
|
char GenerateTestUserSigImpl::bit6ToAscii(uint8_t a)
|
|
{
|
|
a &= (uint8_t)0x3F;
|
|
|
|
if (a <= 25)
|
|
{
|
|
return a + 'A';
|
|
}
|
|
|
|
if (a <= 51)
|
|
{
|
|
return a - 26 + 'a';
|
|
}
|
|
|
|
if (a <= 61)
|
|
{
|
|
return a - 52 + '0';
|
|
}
|
|
|
|
if (a == 62)
|
|
{
|
|
return '+';
|
|
}
|
|
|
|
return '/'; // a == 63
|
|
}
|
|
|
|
GenerateTestUserSig::GenerateTestUserSig()
|
|
{
|
|
|
|
}
|
|
|
|
GenerateTestUserSig::~GenerateTestUserSig()
|
|
{
|
|
|
|
}
|
|
|
|
GenerateTestUserSig& GenerateTestUserSig::instance()
|
|
{
|
|
static GenerateTestUserSig aInstance;
|
|
return aInstance;
|
|
}
|
|
|
|
uint32_t GenerateTestUserSig::getSDKAppID() const
|
|
{
|
|
return SDKAPPID;
|
|
}
|
|
|
|
std::string GenerateTestUserSig::genTestUserSig(const std::string& userId)
|
|
{
|
|
uint32_t currTime = (uint32_t)time(NULL);
|
|
GenerateTestUserSigImpl impl(SDKAPPID, SECRETKEY, currTime, EXPIRETIME);
|
|
|
|
return impl.genTestUserSig(userId);
|
|
}
|