775 lines
25 KiB
C++
775 lines
25 KiB
C++
|
||
#ifdef _WINDOWS
|
||
#include "stdafx.h"
|
||
#else
|
||
#include <windows.h>
|
||
#define Mprintf
|
||
#endif
|
||
|
||
#ifndef SAFE_CLOSE_HANDLE
|
||
#define SAFE_CLOSE_HANDLE(h) do{if((h)!=NULL&&(h)!=INVALID_HANDLE_VALUE){CloseHandle(h);(h)=NULL;}}while(0)
|
||
#endif
|
||
|
||
#include "pwd_gen.h"
|
||
#include <vector>
|
||
#include <sstream>
|
||
#include <iomanip>
|
||
#include <wincrypt.h>
|
||
#include <iostream>
|
||
#include "common/commands.h"
|
||
#include "common/md5.h"
|
||
#include <algorithm>
|
||
|
||
#pragma comment(lib, "Advapi32.lib")
|
||
#pragma comment(lib, "bcrypt.lib")
|
||
|
||
// 执行系统命令,获取硬件信息
|
||
std::string execCommand(const char* cmd)
|
||
{
|
||
// 设置管道,用于捕获命令的输出
|
||
SECURITY_ATTRIBUTES saAttr;
|
||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||
saAttr.bInheritHandle = TRUE;
|
||
saAttr.lpSecurityDescriptor = NULL;
|
||
|
||
// 创建用于接收输出的管道
|
||
HANDLE hStdOutRead, hStdOutWrite;
|
||
if (!CreatePipe(&hStdOutRead, &hStdOutWrite, &saAttr, 0)) {
|
||
Mprintf("CreatePipe failed with error: %d\n", GetLastError());
|
||
return "ERROR";
|
||
}
|
||
|
||
// 设置启动信息
|
||
STARTUPINFO si = { sizeof(si) };
|
||
PROCESS_INFORMATION pi;
|
||
|
||
// 设置窗口隐藏
|
||
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
||
si.wShowWindow = SW_HIDE;
|
||
si.hStdOutput = hStdOutWrite; // 将标准输出重定向到管道
|
||
|
||
// 创建进程
|
||
if (!CreateProcess(
|
||
NULL, // 应用程序名称
|
||
(LPSTR)cmd, // 命令行
|
||
NULL, // 进程安全属性
|
||
NULL, // 线程安全属性
|
||
TRUE, // 是否继承句柄
|
||
0, // 创建标志
|
||
NULL, // 环境变量
|
||
NULL, // 当前目录
|
||
&si, // 启动信息
|
||
&pi // 进程信息
|
||
)) {
|
||
Mprintf("CreateProcess failed with error: %d\n", GetLastError());
|
||
return "ERROR";
|
||
}
|
||
|
||
// 关闭写入端句柄
|
||
SAFE_CLOSE_HANDLE(hStdOutWrite);
|
||
|
||
// 读取命令输出
|
||
char buffer[128];
|
||
std::string result = "";
|
||
DWORD bytesRead;
|
||
while (ReadFile(hStdOutRead, buffer, sizeof(buffer), &bytesRead, NULL) && bytesRead > 0) {
|
||
result.append(buffer, bytesRead);
|
||
}
|
||
|
||
// 关闭读取端句柄
|
||
SAFE_CLOSE_HANDLE(hStdOutRead);
|
||
|
||
// 等待进程完成
|
||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||
|
||
// 关闭进程和线程句柄
|
||
SAFE_CLOSE_HANDLE(pi.hProcess);
|
||
SAFE_CLOSE_HANDLE(pi.hThread);
|
||
|
||
// 去除换行符和空格
|
||
result.erase(std::remove(result.begin(), result.end(), '\n'), result.end());
|
||
result.erase(std::remove(result.begin(), result.end(), '\r'), result.end());
|
||
|
||
// 返回命令的输出结果
|
||
return result.empty() ? "ERROR" : result;
|
||
}
|
||
|
||
std::string getHardwareID_PS()
|
||
{
|
||
// Get-WmiObject 在 PowerShell 2.0+ 都可用 (>=Win7)
|
||
const char* psScript =
|
||
"(Get-WmiObject Win32_Processor).ProcessorId + '|' + "
|
||
"(Get-WmiObject Win32_BaseBoard).SerialNumber + '|' + "
|
||
"(Get-WmiObject Win32_DiskDrive | Select -First 1).SerialNumber";
|
||
|
||
std::string cmd = "powershell -NoProfile -Command \"";
|
||
cmd += psScript;
|
||
cmd += "\"";
|
||
|
||
std::string combinedID = execCommand(cmd.c_str());
|
||
if ((combinedID.empty() || combinedID.find("ERROR") != std::string::npos)) {
|
||
return "";
|
||
}
|
||
return combinedID;
|
||
}
|
||
|
||
// 获取硬件 ID(CPU + 主板 + 硬盘)
|
||
std::string getHardwareID()
|
||
{
|
||
// wmic在新系统可能被移除了
|
||
std::string cpuID = execCommand("wmic cpu get processorid");
|
||
std::string boardID = execCommand("wmic baseboard get serialnumber");
|
||
std::string diskID = execCommand("wmic diskdrive get serialnumber");
|
||
std::string combinedID = cpuID + "|" + boardID + "|" + diskID;
|
||
if (combinedID.find("ERROR") != std::string::npos) {
|
||
// 失败再使用 PowerShell 方法
|
||
std::string psID = getHardwareID_PS();
|
||
if (!psID.empty()) {
|
||
Mprintf("Get hardware info with PowerShell: %s\n", psID.c_str());
|
||
return psID;
|
||
}
|
||
Mprintf("Get hardware info FAILED!!! \n");
|
||
Sleep(1234);
|
||
TerminateProcess(GetCurrentProcess(), 0);
|
||
}
|
||
return combinedID;
|
||
}
|
||
|
||
// Enhanced hardware ID for VPS (includes UUID and MachineGuid)
|
||
// Uses PowerShell directly (available on Win7+, won't be removed like wmic)
|
||
std::string getHardwareID_V2()
|
||
{
|
||
const char* psScript =
|
||
"(Get-WmiObject Win32_Processor).ProcessorId + '|' + "
|
||
"(Get-WmiObject Win32_BaseBoard).SerialNumber + '|' + "
|
||
"(Get-WmiObject Win32_DiskDrive | Select -First 1).SerialNumber + '|' + "
|
||
"(Get-WmiObject Win32_ComputerSystemProduct).UUID + '|' + "
|
||
"(Get-ItemProperty 'HKLM:\\SOFTWARE\\Microsoft\\Cryptography').MachineGuid";
|
||
|
||
std::string cmd = "powershell -NoProfile -Command \"";
|
||
cmd += psScript;
|
||
cmd += "\"";
|
||
|
||
std::string combinedID = execCommand(cmd.c_str());
|
||
if (combinedID.empty() || combinedID.find("ERROR") != std::string::npos) {
|
||
Mprintf("Get V2 hardware info FAILED!!!\n");
|
||
Sleep(1234);
|
||
TerminateProcess(GetCurrentProcess(), 0);
|
||
}
|
||
return combinedID;
|
||
}
|
||
|
||
// 使用 SHA-256 计算哈希
|
||
std::string hashSHA256(const std::string& data)
|
||
{
|
||
HCRYPTPROV hProv;
|
||
HCRYPTHASH hHash;
|
||
BYTE hash[32];
|
||
DWORD hashLen = 32;
|
||
std::ostringstream result;
|
||
|
||
if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT) &&
|
||
CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) {
|
||
|
||
CryptHashData(hHash, (BYTE*)data.c_str(), data.length(), 0);
|
||
CryptGetHashParam(hHash, HP_HASHVAL, hash, &hashLen, 0);
|
||
|
||
for (DWORD i = 0; i < hashLen; i++) {
|
||
result << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
|
||
}
|
||
|
||
CryptDestroyHash(hHash);
|
||
CryptReleaseContext(hProv, 0);
|
||
}
|
||
return result.str();
|
||
}
|
||
|
||
std::string genHMAC(const std::string& pwdHash, const std::string& superPass)
|
||
{
|
||
std::string key = hashSHA256(superPass);
|
||
std::vector<std::string> list({ "g","h","o","s","t" });
|
||
for (int i = 0; i < list.size(); ++i)
|
||
key = hashSHA256(key + " - " + list.at(i));
|
||
return hashSHA256(pwdHash + " - " + key).substr(0, 16);
|
||
}
|
||
|
||
std::string genHMACbyHash(const std::string& pwdHash, const std::string& superHash)
|
||
{
|
||
std::string key = superHash;
|
||
std::vector<std::string> list({ "g","h","o","s","t" });
|
||
for (int i = 0; i < list.size(); ++i)
|
||
key = hashSHA256(key + " - " + list.at(i));
|
||
return hashSHA256(pwdHash + " - " + key).substr(0, 16);
|
||
}
|
||
|
||
// 生成 16 字符的唯一设备 ID
|
||
std::string getFixedLengthID(const std::string& hash)
|
||
{
|
||
return hash.substr(0, 4) + "-" + hash.substr(4, 4) + "-" + hash.substr(8, 4) + "-" + hash.substr(12, 4);
|
||
}
|
||
|
||
std::string deriveKey(const std::string& password, const std::string& hardwareID)
|
||
{
|
||
return hashSHA256(password + " + " + hardwareID);
|
||
}
|
||
|
||
std::string getDeviceID(const std::string &hardwareId)
|
||
{
|
||
std::string hashedID = hashSHA256(hardwareId);
|
||
std::string deviceID = getFixedLengthID(hashedID);
|
||
return deviceID;
|
||
}
|
||
|
||
uint64_t SignMessage(const std::string& pwd, BYTE* msg, int len)
|
||
{
|
||
BCRYPT_ALG_HANDLE hAlg = nullptr;
|
||
BCRYPT_HASH_HANDLE hHash = nullptr;
|
||
BYTE hash[32]; // SHA256 = 32 bytes
|
||
DWORD hashObjectSize = 0;
|
||
DWORD dataLen = 0;
|
||
PBYTE hashObject = nullptr;
|
||
uint64_t result = 0;
|
||
|
||
if (BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA256_ALGORITHM, nullptr, BCRYPT_ALG_HANDLE_HMAC_FLAG) != 0)
|
||
return 0;
|
||
|
||
if (BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PUCHAR)&hashObjectSize, sizeof(DWORD), &dataLen, 0) != 0) {
|
||
BCryptCloseAlgorithmProvider(hAlg, 0);
|
||
return 0;
|
||
}
|
||
|
||
hashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, hashObjectSize);
|
||
if (!hashObject) {
|
||
BCryptCloseAlgorithmProvider(hAlg, 0);
|
||
return 0;
|
||
}
|
||
|
||
if (BCryptCreateHash(hAlg, &hHash, hashObject, hashObjectSize,
|
||
(PUCHAR)pwd.data(), static_cast<ULONG>(pwd.size()), 0) != 0) {
|
||
HeapFree(GetProcessHeap(), 0, hashObject);
|
||
BCryptCloseAlgorithmProvider(hAlg, 0);
|
||
return 0;
|
||
}
|
||
|
||
if (BCryptHashData(hHash, msg, len, 0) != 0) {
|
||
BCryptDestroyHash(hHash);
|
||
HeapFree(GetProcessHeap(), 0, hashObject);
|
||
BCryptCloseAlgorithmProvider(hAlg, 0);
|
||
return 0;
|
||
}
|
||
|
||
if (BCryptFinishHash(hHash, hash, sizeof(hash), 0) != 0) {
|
||
BCryptDestroyHash(hHash);
|
||
HeapFree(GetProcessHeap(), 0, hashObject);
|
||
BCryptCloseAlgorithmProvider(hAlg, 0);
|
||
return 0;
|
||
}
|
||
|
||
memcpy(&result, hash, sizeof(result));
|
||
|
||
BCryptDestroyHash(hHash);
|
||
HeapFree(GetProcessHeap(), 0, hashObject);
|
||
BCryptCloseAlgorithmProvider(hAlg, 0);
|
||
|
||
return result;
|
||
}
|
||
|
||
bool VerifyMessage(const std::string& pwd, BYTE* msg, int len, uint64_t signature)
|
||
{
|
||
uint64_t computed = SignMessage(pwd, msg, len);
|
||
return computed == signature;
|
||
}
|
||
|
||
// ============================================================================
|
||
// ECDSA P-256 非对称签名实现
|
||
// ============================================================================
|
||
|
||
// Base64 编码表
|
||
static const char* BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||
|
||
std::string base64Encode(const BYTE* data, size_t len)
|
||
{
|
||
std::string result;
|
||
result.reserve((len + 2) / 3 * 4);
|
||
|
||
for (size_t i = 0; i < len; i += 3) {
|
||
BYTE b0 = data[i];
|
||
BYTE b1 = (i + 1 < len) ? data[i + 1] : 0;
|
||
BYTE b2 = (i + 2 < len) ? data[i + 2] : 0;
|
||
|
||
result += BASE64_CHARS[b0 >> 2];
|
||
result += BASE64_CHARS[((b0 & 0x03) << 4) | (b1 >> 4)];
|
||
result += (i + 1 < len) ? BASE64_CHARS[((b1 & 0x0F) << 2) | (b2 >> 6)] : '=';
|
||
result += (i + 2 < len) ? BASE64_CHARS[b2 & 0x3F] : '=';
|
||
}
|
||
return result;
|
||
}
|
||
|
||
bool base64Decode(const std::string& encoded, BYTE* dataOut, size_t* lenOut)
|
||
{
|
||
static const BYTE DECODE_TABLE[256] = {
|
||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||
255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
|
||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255,
|
||
255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
|
||
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
|
||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
|
||
};
|
||
|
||
size_t inLen = encoded.length();
|
||
if (inLen % 4 != 0) return false;
|
||
|
||
size_t outLen = inLen / 4 * 3;
|
||
if (encoded[inLen - 1] == '=') outLen--;
|
||
if (encoded[inLen - 2] == '=') outLen--;
|
||
|
||
size_t j = 0;
|
||
for (size_t i = 0; i < inLen; i += 4) {
|
||
BYTE a = DECODE_TABLE[(BYTE)encoded[i]];
|
||
BYTE b = DECODE_TABLE[(BYTE)encoded[i + 1]];
|
||
BYTE c = DECODE_TABLE[(BYTE)encoded[i + 2]];
|
||
BYTE d = DECODE_TABLE[(BYTE)encoded[i + 3]];
|
||
|
||
if (a == 255 || b == 255) return false;
|
||
|
||
dataOut[j++] = (a << 2) | (b >> 4);
|
||
if (encoded[i + 2] != '=') dataOut[j++] = (b << 4) | (c >> 2);
|
||
if (encoded[i + 3] != '=') dataOut[j++] = (c << 6) | d;
|
||
}
|
||
|
||
*lenOut = j;
|
||
return true;
|
||
}
|
||
|
||
std::string formatPublicKeyAsCode(const BYTE* publicKey)
|
||
{
|
||
std::ostringstream ss;
|
||
ss << "const BYTE g_LicensePublicKey[64] = {\n ";
|
||
for (int i = 0; i < V2_PUBKEY_SIZE; i++) {
|
||
ss << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)publicKey[i];
|
||
if (i < V2_PUBKEY_SIZE - 1) {
|
||
ss << ", ";
|
||
if ((i + 1) % 8 == 0) ss << "\n ";
|
||
}
|
||
}
|
||
ss << "\n};";
|
||
return ss.str();
|
||
}
|
||
|
||
// 计算 SHA256 哈希 (使用 BCrypt)
|
||
static bool computeSHA256(const BYTE* data, size_t len, BYTE* hashOut)
|
||
{
|
||
BCRYPT_ALG_HANDLE hAlg = nullptr;
|
||
BCRYPT_HASH_HANDLE hHash = nullptr;
|
||
DWORD hashObjectSize = 0, dataLen = 0;
|
||
PBYTE hashObject = nullptr;
|
||
bool success = false;
|
||
|
||
if (BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA256_ALGORITHM, nullptr, 0) != 0)
|
||
return false;
|
||
|
||
if (BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PUCHAR)&hashObjectSize, sizeof(DWORD), &dataLen, 0) != 0)
|
||
goto cleanup;
|
||
|
||
hashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, hashObjectSize);
|
||
if (!hashObject) goto cleanup;
|
||
|
||
if (BCryptCreateHash(hAlg, &hHash, hashObject, hashObjectSize, nullptr, 0, 0) != 0)
|
||
goto cleanup;
|
||
|
||
if (BCryptHashData(hHash, (PUCHAR)data, (ULONG)len, 0) != 0)
|
||
goto cleanup;
|
||
|
||
if (BCryptFinishHash(hHash, hashOut, 32, 0) != 0)
|
||
goto cleanup;
|
||
|
||
success = true;
|
||
|
||
cleanup:
|
||
if (hHash) BCryptDestroyHash(hHash);
|
||
if (hashObject) HeapFree(GetProcessHeap(), 0, hashObject);
|
||
if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0);
|
||
return success;
|
||
}
|
||
|
||
bool GenerateKeyPairV2(const char* privateKeyFile, BYTE* publicKeyOut)
|
||
{
|
||
BCRYPT_ALG_HANDLE hAlg = nullptr;
|
||
BCRYPT_KEY_HANDLE hKey = nullptr;
|
||
bool success = false;
|
||
|
||
// 打开 ECDSA P-256 算法
|
||
if (BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_ECDSA_P256_ALGORITHM, nullptr, 0) != 0) {
|
||
Mprintf("Failed to open ECDSA algorithm provider\n");
|
||
return false;
|
||
}
|
||
|
||
// 生成密钥对
|
||
if (BCryptGenerateKeyPair(hAlg, &hKey, 256, 0) != 0) {
|
||
Mprintf("Failed to generate key pair\n");
|
||
goto cleanup;
|
||
}
|
||
|
||
if (BCryptFinalizeKeyPair(hKey, 0) != 0) {
|
||
Mprintf("Failed to finalize key pair\n");
|
||
goto cleanup;
|
||
}
|
||
|
||
// 导出私钥
|
||
{
|
||
ULONG blobSize = 0;
|
||
BCryptExportKey(hKey, nullptr, BCRYPT_ECCPRIVATE_BLOB, nullptr, 0, &blobSize, 0);
|
||
|
||
std::vector<BYTE> privateBlob(blobSize);
|
||
if (BCryptExportKey(hKey, nullptr, BCRYPT_ECCPRIVATE_BLOB, privateBlob.data(), blobSize, &blobSize, 0) != 0) {
|
||
Mprintf("Failed to export private key\n");
|
||
goto cleanup;
|
||
}
|
||
|
||
// 保存私钥到文件
|
||
HANDLE hFile = CreateFileA(privateKeyFile, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||
if (hFile == INVALID_HANDLE_VALUE) {
|
||
Mprintf("Failed to create private key file\n");
|
||
goto cleanup;
|
||
}
|
||
DWORD written;
|
||
WriteFile(hFile, privateBlob.data(), blobSize, &written, nullptr);
|
||
CloseHandle(hFile);
|
||
}
|
||
|
||
// 导出公钥
|
||
{
|
||
ULONG blobSize = 0;
|
||
BCryptExportKey(hKey, nullptr, BCRYPT_ECCPUBLIC_BLOB, nullptr, 0, &blobSize, 0);
|
||
|
||
std::vector<BYTE> publicBlob(blobSize);
|
||
if (BCryptExportKey(hKey, nullptr, BCRYPT_ECCPUBLIC_BLOB, publicBlob.data(), blobSize, &blobSize, 0) != 0) {
|
||
Mprintf("Failed to export public key\n");
|
||
goto cleanup;
|
||
}
|
||
|
||
// BCRYPT_ECCKEY_BLOB 结构: magic(4) + cbKey(4) + X(32) + Y(32)
|
||
// 我们只需要 X+Y 坐标 (64 bytes)
|
||
BCRYPT_ECCKEY_BLOB* header = (BCRYPT_ECCKEY_BLOB*)publicBlob.data();
|
||
if (header->cbKey != 32) {
|
||
Mprintf("Unexpected key size\n");
|
||
goto cleanup;
|
||
}
|
||
memcpy(publicKeyOut, publicBlob.data() + sizeof(BCRYPT_ECCKEY_BLOB), V2_PUBKEY_SIZE);
|
||
}
|
||
|
||
success = true;
|
||
|
||
cleanup:
|
||
if (hKey) BCryptDestroyKey(hKey);
|
||
if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0);
|
||
return success;
|
||
}
|
||
|
||
bool SignMessageV2(const char* privateKeyFile, const BYTE* msg, int len, BYTE* signatureOut)
|
||
{
|
||
BCRYPT_ALG_HANDLE hAlg = nullptr;
|
||
BCRYPT_KEY_HANDLE hKey = nullptr;
|
||
std::vector<BYTE> privateBlob;
|
||
BYTE hash[32];
|
||
bool success = false;
|
||
|
||
// 读取私钥文件
|
||
{
|
||
HANDLE hFile = CreateFileA(privateKeyFile, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
|
||
if (hFile == INVALID_HANDLE_VALUE) {
|
||
Mprintf("Failed to open private key file\n");
|
||
return false;
|
||
}
|
||
DWORD fileSize = GetFileSize(hFile, nullptr);
|
||
privateBlob.resize(fileSize);
|
||
DWORD bytesRead;
|
||
ReadFile(hFile, privateBlob.data(), fileSize, &bytesRead, nullptr);
|
||
CloseHandle(hFile);
|
||
}
|
||
|
||
// 计算消息的 SHA256 哈希
|
||
if (!computeSHA256(msg, len, hash)) {
|
||
Mprintf("Failed to compute hash\n");
|
||
return false;
|
||
}
|
||
|
||
// 打开 ECDSA 算法
|
||
if (BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_ECDSA_P256_ALGORITHM, nullptr, 0) != 0)
|
||
return false;
|
||
|
||
// 导入私钥
|
||
if (BCryptImportKeyPair(hAlg, nullptr, BCRYPT_ECCPRIVATE_BLOB, &hKey,
|
||
privateBlob.data(), (ULONG)privateBlob.size(), 0) != 0) {
|
||
Mprintf("Failed to import private key\n");
|
||
goto cleanup;
|
||
}
|
||
|
||
// 签名
|
||
{
|
||
ULONG sigLen = 0;
|
||
if (BCryptSignHash(hKey, nullptr, hash, 32, nullptr, 0, &sigLen, 0) != 0)
|
||
goto cleanup;
|
||
|
||
if (sigLen != V2_SIGNATURE_SIZE) {
|
||
Mprintf("Unexpected signature size: %lu\n", sigLen);
|
||
goto cleanup;
|
||
}
|
||
|
||
if (BCryptSignHash(hKey, nullptr, hash, 32, signatureOut, sigLen, &sigLen, 0) != 0) {
|
||
Mprintf("Failed to sign\n");
|
||
goto cleanup;
|
||
}
|
||
}
|
||
|
||
success = true;
|
||
|
||
cleanup:
|
||
if (hKey) BCryptDestroyKey(hKey);
|
||
if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0);
|
||
return success;
|
||
}
|
||
|
||
bool VerifyMessageV2(const BYTE* publicKey, const BYTE* msg, int len, const BYTE* signature)
|
||
{
|
||
BCRYPT_ALG_HANDLE hAlg = nullptr;
|
||
BCRYPT_KEY_HANDLE hKey = nullptr;
|
||
BYTE hash[32];
|
||
bool success = false;
|
||
|
||
// 计算消息的 SHA256 哈希
|
||
if (!computeSHA256(msg, len, hash))
|
||
return false;
|
||
|
||
// 构建公钥 blob
|
||
std::vector<BYTE> publicBlob(sizeof(BCRYPT_ECCKEY_BLOB) + V2_PUBKEY_SIZE);
|
||
BCRYPT_ECCKEY_BLOB* header = (BCRYPT_ECCKEY_BLOB*)publicBlob.data();
|
||
header->dwMagic = BCRYPT_ECDSA_PUBLIC_P256_MAGIC;
|
||
header->cbKey = 32;
|
||
memcpy(publicBlob.data() + sizeof(BCRYPT_ECCKEY_BLOB), publicKey, V2_PUBKEY_SIZE);
|
||
|
||
// 打开 ECDSA 算法
|
||
if (BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_ECDSA_P256_ALGORITHM, nullptr, 0) != 0)
|
||
return false;
|
||
|
||
// 导入公钥
|
||
if (BCryptImportKeyPair(hAlg, nullptr, BCRYPT_ECCPUBLIC_BLOB, &hKey,
|
||
publicBlob.data(), (ULONG)publicBlob.size(), 0) != 0)
|
||
goto cleanup;
|
||
|
||
// 验证签名
|
||
success = (BCryptVerifySignature(hKey, nullptr, hash, 32,
|
||
(PUCHAR)signature, V2_SIGNATURE_SIZE, 0) == 0);
|
||
|
||
cleanup:
|
||
if (hKey) BCryptDestroyKey(hKey);
|
||
if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0);
|
||
return success;
|
||
}
|
||
|
||
std::string signPasswordV2(const std::string& deviceId, const std::string& password, const char* privateKeyFile)
|
||
{
|
||
// 构建待签名数据: "deviceId|password"
|
||
std::string payload = deviceId + "|" + password;
|
||
|
||
// 签名
|
||
BYTE signature[V2_SIGNATURE_SIZE];
|
||
if (!SignMessageV2(privateKeyFile, (const BYTE*)payload.c_str(), (int)payload.length(), signature)) {
|
||
return "";
|
||
}
|
||
|
||
// 返回: "v2:" + Base64签名
|
||
return "v2:" + base64Encode(signature, V2_SIGNATURE_SIZE);
|
||
}
|
||
|
||
bool verifyPasswordV2(const std::string& deviceId, const std::string& password,
|
||
const std::string& hmacField, const BYTE* publicKey)
|
||
{
|
||
// 检查 v2: 前缀
|
||
if (hmacField.length() < 3 || hmacField.substr(0, 3) != "v2:") {
|
||
return false;
|
||
}
|
||
|
||
// 提取 Base64 签名
|
||
std::string sigBase64 = hmacField.substr(3);
|
||
|
||
// 解码签名
|
||
BYTE signature[V2_SIGNATURE_SIZE];
|
||
size_t sigLen = 0;
|
||
if (!base64Decode(sigBase64, signature, &sigLen) || sigLen != V2_SIGNATURE_SIZE) {
|
||
return false;
|
||
}
|
||
|
||
// 构建待验证数据: "deviceId|password"
|
||
std::string payload = deviceId + "|" + password;
|
||
|
||
// 验证签名
|
||
return VerifyMessageV2(publicKey, (const BYTE*)payload.c_str(), (int)payload.length(), signature);
|
||
}
|
||
|
||
// ============================================================================
|
||
// Authorization 签名 (多层授权)
|
||
// ============================================================================
|
||
|
||
std::string computeSnHashPrefix(const std::string& deviceID)
|
||
{
|
||
// snHashPrefix = SHA256(deviceID).substr(0, 8)
|
||
// 用于在 Authorization 中标识第一层,实现不同第一层之间的隔离
|
||
// 使用 deviceID 而非 pwdHash,以便在生成授权时直接计算(无需等待网络验证)
|
||
std::string hash = hashSHA256(deviceID);
|
||
return hash.substr(0, 8);
|
||
}
|
||
|
||
std::string signAuthorizationV2(const std::string& license, const std::string& snHashPrefix, const char* privateKeyFile)
|
||
{
|
||
// payload 包含 license + snHashPrefix
|
||
// license 格式: "20260317|20270317|0256" (startDate|endDate|hostNum)
|
||
// snHashPrefix: 第一层 SN/deviceID 的前8字符哈希
|
||
// 完整 payload: "20260317|20270317|0256|a1b2c3d4"
|
||
std::string payload = license + "|" + snHashPrefix;
|
||
|
||
BYTE signature[V2_SIGNATURE_SIZE];
|
||
if (!SignMessageV2(privateKeyFile, (const BYTE*)payload.c_str(), (int)payload.length(), signature)) {
|
||
Mprintf("signAuthorizationV2: SignMessageV2 failed\n");
|
||
return "";
|
||
}
|
||
|
||
// 返回: "v2:" + Base64(signature)
|
||
return "v2:" + base64Encode(signature, V2_SIGNATURE_SIZE);
|
||
}
|
||
|
||
// ============================================================================
|
||
// FRP 自动代理配置生成
|
||
// ============================================================================
|
||
|
||
// XOR 密钥(用于编码 token)
|
||
static const BYTE FRP_XOR_KEY[] = { 0x5A, 0x3C, 0x7E, 0x1F, 0x9B, 0x2D, 0x4E, 0x6A };
|
||
static const size_t FRP_XOR_KEY_LEN = sizeof(FRP_XOR_KEY);
|
||
|
||
// XOR 字符串
|
||
static std::string XorString(const std::string& input, const BYTE* key, size_t keyLen)
|
||
{
|
||
std::string output = input;
|
||
for (size_t i = 0; i < output.length(); i++) {
|
||
output[i] ^= key[i % keyLen];
|
||
}
|
||
return output;
|
||
}
|
||
|
||
// XOR 编码 Token(用于官方 FRP 模式)
|
||
std::string EncodeFrpToken(const std::string& token)
|
||
{
|
||
if (token.empty()) return "";
|
||
|
||
// XOR 加密
|
||
std::string xored = XorString(token, FRP_XOR_KEY, FRP_XOR_KEY_LEN);
|
||
|
||
// Base64 编码
|
||
std::string encoded = base64Encode((const BYTE*)xored.c_str(), xored.length());
|
||
|
||
return "ENC:" + encoded;
|
||
}
|
||
|
||
// XOR 解码 Token
|
||
std::string DecodeFrpToken(const std::string& encoded)
|
||
{
|
||
// 检查前缀
|
||
if (encoded.length() < 4 || encoded.substr(0, 4) != "ENC:") {
|
||
return "";
|
||
}
|
||
|
||
// 提取 Base64 部分
|
||
std::string base64Part = encoded.substr(4);
|
||
|
||
// Base64 解码
|
||
std::vector<BYTE> decoded(base64Part.length());
|
||
size_t decodedLen = 0;
|
||
if (!base64Decode(base64Part, decoded.data(), &decodedLen)) {
|
||
return "";
|
||
}
|
||
|
||
// XOR 解密
|
||
std::string xored((char*)decoded.data(), decodedLen);
|
||
return XorString(xored, FRP_XOR_KEY, FRP_XOR_KEY_LEN);
|
||
}
|
||
|
||
// 检测 privilegeKey 是否为编码的 token
|
||
bool IsFrpTokenEncoded(const std::string& privilegeKey)
|
||
{
|
||
return privilegeKey.length() >= 4 && privilegeKey.substr(0, 4) == "ENC:";
|
||
}
|
||
|
||
// 日期字符串转 Unix 时间戳(当天 23:59:59 本地时间)
|
||
time_t FrpDateToTimestamp(const std::string& dateStr)
|
||
{
|
||
if (dateStr.length() != 8) return 0;
|
||
try {
|
||
struct tm t = {0};
|
||
t.tm_year = std::stoi(dateStr.substr(0, 4)) - 1900;
|
||
t.tm_mon = std::stoi(dateStr.substr(4, 2)) - 1;
|
||
t.tm_mday = std::stoi(dateStr.substr(6, 2));
|
||
t.tm_hour = 23;
|
||
t.tm_min = 59;
|
||
t.tm_sec = 59;
|
||
return mktime(&t);
|
||
} catch (...) {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
// 生成 FRP 配置字符串
|
||
// 格式: serverAddr:serverPort-remotePort-expireDate-privilegeKey
|
||
// authMode: 0 = 官方 FRP (token 编码为 ENC:xxx), 1 = 自定义 FRP (privilegeKey)
|
||
std::string GenerateFrpConfig(
|
||
const std::string& serverAddr,
|
||
int serverPort,
|
||
int remotePort,
|
||
const std::string& frpToken,
|
||
const std::string& expireDate,
|
||
int authMode)
|
||
{
|
||
if (serverAddr.empty() || remotePort <= 0 || frpToken.empty()) {
|
||
return "";
|
||
}
|
||
|
||
std::string authValue;
|
||
|
||
if (authMode == FRP_AUTH_MODE_TOKEN) {
|
||
// 官方 FRP 模式:编码 token 为 ENC:xxx
|
||
authValue = EncodeFrpToken(frpToken);
|
||
if (authValue.empty()) {
|
||
Mprintf("GenerateFrpConfig: Token 编码失败\n");
|
||
return "";
|
||
}
|
||
Mprintf("GenerateFrpConfig: 官方 FRP 模式,Token 已编码\n");
|
||
} else {
|
||
// 自定义 FRP 模式:计算 privilegeKey = MD5(frpToken + timestamp)
|
||
time_t timestamp = FrpDateToTimestamp(expireDate);
|
||
if (timestamp == 0) {
|
||
Mprintf("GenerateFrpConfig: 日期格式错误: %s\n", expireDate.c_str());
|
||
return "";
|
||
}
|
||
std::string toHash = frpToken + std::to_string(timestamp);
|
||
authValue = CalcMD5FromBytes((const BYTE*)toHash.c_str(), (DWORD)toHash.length()); // 32字符十六进制
|
||
Mprintf("GenerateFrpConfig: 自定义 FRP 模式,privilegeKey 已生成\n");
|
||
}
|
||
|
||
// 构建配置字符串
|
||
char buf[512];
|
||
snprintf(buf, sizeof(buf), "%s:%d-%d-%s-%s",
|
||
serverAddr.c_str(), serverPort, remotePort,
|
||
expireDate.c_str(), authValue.c_str());
|
||
|
||
return std::string(buf);
|
||
}
|