以下是完整的 C++ 验证客户端实现,包含所有必要的加密、签名和网络请求功能。
#ifndef AUTH_CLIENT_H
#define AUTH_CLIENT_H
#include <string>
#include <map>
#include <vector>
#include <ctime>
#include <cstdlib>
#include <sstream>
#include <iomanip>
#include <algorithm>
#ifdef _WIN32
#include <windows.h>
#include <winhttp.h>
#pragma comment(lib, "winhttp.lib")
#else
#include <curl/curl.h>
#endif
#include <openssl/hmac.h>
#include <openssl/evp.h>
class AuthClient {
private:
std::string m_appKey;
std::string m_appSecret;
std::string m_transferKey;
std::string m_apiUrl;
std::string m_token;
std::string m_execToken;
std::string m_username;
std::string m_expireTime;
int m_points = 0;
int m_heartbeatInterval = 30;
// RC4 加密/解密
static void rc4_crypt(unsigned char* data, size_t len, const unsigned char* key, size_t keylen) {
unsigned char S[256];
for (int i = 0; i < 256; i++) S[i] = (unsigned char)i;
int j = 0;
for (int i = 0; i < 256; i++) {
j = (j + S[i] + key[i % keylen]) % 256;
std::swap(S[i], S[j]);
}
int x = 0, y = 0;
for (size_t k = 0; k < len; k++) {
x = (x + 1) % 256;
y = (y + S[x]) % 256;
std::swap(S[x], S[y]);
data[k] ^= S[(S[x] + S[y]) % 256];
}
}
// 字节转HEX
static std::string bytesToHex(const std::vector<unsigned char>& bytes) {
std::ostringstream oss;
for (unsigned char b : bytes) {
oss << std::hex << std::setw(2) << std::setfill('0') << (int)b;
}
return oss.str();
}
// HEX转字节
static std::vector<unsigned char> hexToBytes(const std::string& hex) {
std::vector<unsigned char> bytes;
for (size_t i = 0; i < hex.length(); i += 2) {
unsigned char byte = (unsigned char)strtol(hex.substr(i, 2).c_str(), nullptr, 16);
bytes.push_back(byte);
}
return bytes;
}
// HMAC-SHA256 签名
static std::string hmacSha256(const std::string& data, const std::string& key) {
unsigned char result[EVP_MAX_MD_SIZE];
unsigned int len = 0;
HMAC(EVP_sha256(), key.c_str(), (int)key.length(),
(unsigned char*)data.c_str(), data.length(), result, &len);
std::ostringstream oss;
for (unsigned int i = 0; i < len; i++) {
oss << std::hex << std::setw(2) << std::setfill('0') << (int)result[i];
}
return oss.str();
}
// 生成随机字符串
static std::string generateNonce(int length = 16) {
static const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
std::string result;
result.reserve(length);
for (int i = 0; i < length; i++) {
result += charset[rand() % (sizeof(charset) - 1)];
}
return result;
}
// 构建加密请求数据
std::string buildRequest(const std::map<std::string, std::string>& params) {
// 按key排序拼接参数
std::string data;
for (const auto& p : params) {
if (!data.empty()) data += "&";
data += p.first + "=" + p.second;
}
// 计算签名
std::string sign = hmacSha256(data, m_appSecret);
data += "&sign=" + sign;
// RC4加密
std::vector<unsigned char> encrypted(data.begin(), data.end());
rc4_crypt(encrypted.data(), encrypted.size(),
(unsigned char*)m_transferKey.c_str(), m_transferKey.length());
return bytesToHex(encrypted);
}
// 解密响应
std::string decryptResponse(const std::string& response) {
// 判断是否为加密响应
if (response.empty() || response[0] == '{') {
return response; // 明文响应
}
// HEX解码并RC4解密
std::vector<unsigned char> bytes = hexToBytes(response);
rc4_crypt(bytes.data(), bytes.size(),
(unsigned char*)m_transferKey.c_str(), m_transferKey.length());
return std::string(bytes.begin(), bytes.end());
}
#ifdef _WIN32
// Windows HTTP 请求
std::string httpGet(const std::string& url) {
std::string result;
// 解析URL
URL_COMPONENTSW urlComp = {0};
urlComp.dwStructSize = sizeof(urlComp);
wchar_t hostName[256] = {0};
wchar_t urlPath[2048] = {0};
urlComp.lpszHostName = hostName;
urlComp.dwHostNameLength = 256;
urlComp.lpszUrlPath = urlPath;
urlComp.dwUrlPathLength = 2048;
std::wstring wurl(url.begin(), url.end());
WinHttpCrackUrl(wurl.c_str(), 0, 0, &urlComp);
HINTERNET hSession = WinHttpOpen(L"AuthClient/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL, 0);
if (!hSession) return "";
HINTERNET hConnect = WinHttpConnect(hSession, hostName, urlComp.nPort, 0);
if (!hConnect) { WinHttpCloseHandle(hSession); return ""; }
DWORD flags = (urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? WINHTTP_FLAG_SECURE : 0;
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", urlPath,
NULL, NULL, NULL, flags);
if (!hRequest) {
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return "";
}
if (WinHttpSendRequest(hRequest, NULL, 0, NULL, 0, 0, 0) &&
WinHttpReceiveResponse(hRequest, NULL)) {
DWORD size = 0;
do {
WinHttpQueryDataAvailable(hRequest, &size);
if (size > 0) {
std::vector<char> buffer(size + 1, 0);
DWORD downloaded = 0;
WinHttpReadData(hRequest, buffer.data(), size, &downloaded);
result.append(buffer.data(), downloaded);
}
} while (size > 0);
}
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return result;
}
#else
// libcurl 回调
static size_t writeCallback(void* contents, size_t size, size_t nmemb, std::string* s) {
s->append((char*)contents, size * nmemb);
return size * nmemb;
}
std::string httpGet(const std::string& url) {
std::string result;
CURL* curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
return result;
}
#endif
// 发送API请求
std::string sendRequest(const std::map<std::string, std::string>& params) {
std::string encData = buildRequest(params);
std::string url = m_apiUrl + "?app_key=" + m_appKey + "&data=" + encData;
std::string response = httpGet(url);
return decryptResponse(response);
}
// 简单JSON解析(获取字符串值)
static std::string jsonGetString(const std::string& json, const std::string& key) {
std::string search = "\"" + key + "\":";
size_t pos = json.find(search);
if (pos == std::string::npos) return "";
pos += search.length();
while (pos < json.length() && (json[pos] == ' ' || json[pos] == '\t')) pos++;
if (json[pos] == '"') {
pos++;
size_t end = json.find('"', pos);
if (end != std::string::npos) {
return json.substr(pos, end - pos);
}
}
return "";
}
// 简单JSON解析(获取整数值)
static int jsonGetInt(const std::string& json, const std::string& key) {
std::string search = "\"" + key + "\":";
size_t pos = json.find(search);
if (pos == std::string::npos) return 0;
pos += search.length();
while (pos < json.length() && (json[pos] == ' ' || json[pos] == '\t')) pos++;
std::string numStr;
while (pos < json.length() && (isdigit(json[pos]) || json[pos] == '-')) {
numStr += json[pos++];
}
return numStr.empty() ? 0 : atoi(numStr.c_str());
}
public:
AuthClient(const std::string& appKey, const std::string& appSecret,
const std::string& transferKey, const std::string& apiUrl)
: m_appKey(appKey), m_appSecret(appSecret),
m_transferKey(transferKey), m_apiUrl(apiUrl) {
srand((unsigned int)time(nullptr));
}
// 初始化
bool init(const std::string& machineCode, const std::string& version = "") {
std::map<std::string, std::string> params;
params["action"] = "init";
params["machine_code"] = machineCode;
params["timestamp"] = std::to_string(time(nullptr));
params["nonce"] = generateNonce();
if (!version.empty()) params["version"] = version;
std::string resp = sendRequest(params);
int code = jsonGetInt(resp, "code");
if (code == 1) {
m_heartbeatInterval = jsonGetInt(resp, "heartbeat_interval");
if (m_heartbeatInterval <= 0) m_heartbeatInterval = 30;
return true;
}
return false;
}
// 登录(统一接口:账号登录或卡密登录)
bool login(const std::string& machineCode, const std::string& username,
const std::string& password = "0") {
std::map<std::string, std::string> params;
params["action"] = "login";
params["machine_code"] = machineCode;
params["password"] = password;
params["timestamp"] = std::to_string(time(nullptr));
params["username"] = username;
params["nonce"] = generateNonce();
std::string resp = sendRequest(params);
int code = jsonGetInt(resp, "code");
if (code == 2) {
m_token = jsonGetString(resp, "token");
m_execToken = jsonGetString(resp, "exec_token");
m_username = jsonGetString(resp, "username");
m_expireTime = jsonGetString(resp, "expire_time");
m_points = jsonGetInt(resp, "points");
return true;
}
return false;
}
// 心跳
bool heartbeat(const std::string& machineCode = "") {
std::map<std::string, std::string> params;
params["action"] = "heartbeat";
params["timestamp"] = std::to_string(time(nullptr));
params["token"] = m_token;
if (!machineCode.empty()) params["machine_code"] = machineCode;
std::string resp = sendRequest(params);
int code = jsonGetInt(resp, "code");
if (code == 3) {
m_expireTime = jsonGetString(resp, "expire_time");
m_points = jsonGetInt(resp, "points");
m_execToken = jsonGetString(resp, "exec_token");
return true;
}
return false;
}
// 登出
bool logout(const std::string& machineCode = "") {
std::map<std::string, std::string> params;
params["action"] = "logout";
params["timestamp"] = std::to_string(time(nullptr));
params["token"] = m_token;
if (!machineCode.empty()) params["machine_code"] = machineCode;
std::string resp = sendRequest(params);
return jsonGetInt(resp, "code") == 4;
}
// 扣点
bool deduct(int points, const std::string& requestId = "", const std::string& machineCode = "") {
std::map<std::string, std::string> params;
params["action"] = "deduct";
params["points"] = std::to_string(points);
params["timestamp"] = std::to_string(time(nullptr));
params["token"] = m_token;
if (!requestId.empty()) params["request_id"] = requestId;
if (!machineCode.empty()) params["machine_code"] = machineCode;
std::string resp = sendRequest(params);
int code = jsonGetInt(resp, "code");
if (code == 8) {
m_points = jsonGetInt(resp, "points");
return true;
}
return false;
}
// Getters
const std::string& getToken() const { return m_token; }
const std::string& getUsername() const { return m_username; }
const std::string& getExpireTime() const { return m_expireTime; }
int getPoints() const { return m_points; }
int getHeartbeatInterval() const { return m_heartbeatInterval; }
};
#endif // AUTH_CLIENT_H
#include <iostream>
#include <thread>
#include <chrono>
#include "auth_client.h"
// 获取机器码(示例:使用CPU ID + 硬盘序列号等)
std::string getMachineCode() {
// 实际项目中应该获取真实的硬件信息
return "MACHINE_CODE_EXAMPLE_12345";
}
int main() {
// 配置
const std::string APP_KEY = "your_app_key";
const std::string APP_SECRET = "your_app_secret";
const std::string TRANSFER_KEY = "your_transfer_key";
const std::string API_URL = "https://your-server.com/api/v1/client/enc";
AuthClient client(APP_KEY, APP_SECRET, TRANSFER_KEY, API_URL);
std::string machineCode = getMachineCode();
// 1. 初始化
std::cout << "正在初始化..." << std::endl;
if (!client.init(machineCode, "1.0.0")) {
std::cerr << "初始化失败!" << std::endl;
return 1;
}
std::cout << "初始化成功,心跳间隔: " << client.getHeartbeatInterval() << "秒" << std::endl;
// 2. 登录(卡密登录示例)
std::string cardNo;
std::cout << "请输入卡密: ";
std::cin >> cardNo;
std::cout << "正在登录..." << std::endl;
if (!client.login(machineCode, cardNo, "0")) {
std::cerr << "登录失败!" << std::endl;
return 1;
}
std::cout << "登录成功!" << std::endl;
std::cout << "用户名: " << client.getUsername() << std::endl;
std::cout << "到期时间: " << client.getExpireTime() << std::endl;
std::cout << "剩余点数: " << client.getPoints() << std::endl;
// 3. 心跳循环
bool running = true;
std::thread heartbeatThread([&]() {
while (running) {
std::this_thread::sleep_for(
std::chrono::seconds(client.getHeartbeatInterval()));
if (running && !client.heartbeat(machineCode)) {
std::cerr << "心跳失败,可能已掉线!" << std::endl;
running = false;
}
}
});
// 4. 主循环(模拟业务逻辑)
std::cout << "\n按 q 退出,按 d 扣点..." << std::endl;
char cmd;
while (running && std::cin >> cmd) {
if (cmd == 'q' || cmd == 'Q') {
break;
} else if (cmd == 'd' || cmd == 'D') {
if (client.deduct(1)) {
std::cout << "扣点成功,剩余: " << client.getPoints() << std::endl;
} else {
std::cout << "扣点失败!" << std::endl;
}
}
}
// 5. 退出
running = false;
if (heartbeatThread.joinable()) {
heartbeatThread.join();
}
client.logout(machineCode);
std::cout << "已退出登录" << std::endl;
return 0;
}
# Windows (MSVC)
cl /EHsc /I"path/to/openssl/include" main.cpp /link /LIBPATH:"path/to/openssl/lib" libssl.lib libcrypto.lib
# Linux
g++ -std=c++11 -o auth_client main.cpp -lssl -lcrypto -lcurl -pthread
# macOS
clang++ -std=c++11 -o auth_client main.cpp -lssl -lcrypto -lcurl -pthread