C/C++ SDK 完整示例

以下是完整的 C++ 验证客户端实现,包含所有必要的加密、签名和网络请求功能。

依赖说明

完整代码 (auth_client.h)

#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

使用示例 (main.cpp)

#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