#pragma once // VerifyV2.h - V2 授权验证(仅验证,不含签名功能) // 用于客户端离线验证 V2 签名 // // ============================================================================ // 授权校验逻辑说明 // ============================================================================ // // 授权分为 V1 和 V2 两种: // - V1: 需要在线连接授权服务器校验 // - V2: 支持离线校验(使用 ECDSA P-256 签名) // // V2 离线校验流程: // 1. 读取配置中的 SN (设备ID)、Password (授权码)、PwdHmac (签名) // 2. 验证签名:使用内嵌公钥验证 PwdHmac 是否为 "SN|Password" 的有效签名 // 3. 检查有效期:解析 Password 中的结束日期,判断是否过期 // // Password 格式: "YYYYMMDD-YYYYMMDD-NNNN-XXXXXXXX" // 开始日期 结束日期 数量 校验码 // // 授权行为: // +---------------------------+-------------------------------------------+ // | 情况 | 行为 | // +---------------------------+-------------------------------------------+ // | V2 + 签名有效 + 未过期 | SetAuthorized() → 离线OK,无需连接服务器 | // | V2 + 签名有效 + 已过期 | 需要连接服务器续期 | // | V2 + 签名无效 | 需要连接服务器 | // | V1 | 需要连接服务器 | // | 试用版 | 保持连接,WAN检测+端口限制 | // | 未授权/连不上服务器 | 循环弹窗警告 | // +---------------------------+-------------------------------------------+ // // 服务器授权成功后: // - 试用版: 保持连接,执行 WAN 检测和端口限制 // - 正式版: AuthKernelManager 退出 // // ============================================================================ #include #include #include #include #pragma comment(lib, "bcrypt.lib") // 包含公钥 #include "key.h" // V2 签名长度 (ECDSA P-256: 64 bytes) #define V2_SIGNATURE_SIZE 64 // V2 公钥长度 (ECDSA P-256: 64 bytes, X+Y coordinates) #define V2_PUBKEY_SIZE 64 namespace VerifyV2 { // Base64 解码 inline 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 == 0 || inLen % 4 != 0) return false; size_t outLen = inLen / 4 * 3; if (encoded[inLen - 1] == '=') outLen--; if (inLen >= 2 && 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 (j < outLen) dataOut[j++] = (b << 4) | (c >> 2); if (j < outLen) dataOut[j++] = (c << 6) | d; } *lenOut = outLen; return true; } // 计算 SHA256 哈希 (使用 BCrypt) inline 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; } // 使用公钥验证 ECDSA P-256 签名 inline bool VerifySignature(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 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; } // 验证口令签名 (V2) // deviceId: 设备ID (如 "XXXX-XXXX-XXXX-XXXX") // password: 口令字符串 (如 "20250301-20261231-0002-XXXXXXXX") // hmacField: HMAC 字段值 (应以 "v2:" 开头) // publicKey: 公钥 (64 bytes),默认使用内嵌公钥 // 返回: true 签名验证通过 inline bool verifyPasswordV2(const std::string& deviceId, const std::string& password, const std::string& hmacField, const BYTE* publicKey = g_LicensePublicKey) { // 检查 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 VerifySignature(publicKey, (const BYTE*)payload.c_str(), (int)payload.length(), signature); } // 获取设备ID (从配置读取或从硬件ID计算) // 供外部调用,简化验证流程 inline std::string getDeviceIdFromHardwareId(const std::string& hardwareId) { // 与 pwd_gen.cpp 中的 getDeviceID 逻辑一致 // SHA256(hardwareId) -> 取前16字节 -> 格式化为 XXXX-XXXX-XXXX-XXXX BYTE hash[32]; if (!computeSHA256((const BYTE*)hardwareId.c_str(), hardwareId.length(), hash)) return ""; char deviceId[20]; snprintf(deviceId, sizeof(deviceId), "%02X%02X-%02X%02X-%02X%02X-%02X%02X", hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7]); return deviceId; } // 检查 V2 密码是否过期 // password 格式: "20250301-20261231-0002-XXXXXXXX" (startDate-endDate-...) // 返回: true 如果未过期(在有效期内) inline bool isPasswordValid(const std::string& password) { // 解析结束日期 (位置 9-16, 格式 YYYYMMDD) if (password.length() < 17 || password[8] != '-') { return false; } std::string endDateStr = password.substr(9, 8); if (endDateStr.length() != 8) { return false; } // 解析 YYYYMMDD int endYear = 0, endMonth = 0, endDay = 0; if (sscanf(endDateStr.c_str(), "%4d%2d%2d", &endYear, &endMonth, &endDay) != 3) { return false; } // 获取当前日期 SYSTEMTIME st; GetLocalTime(&st); // 比较日期 (转换为 YYYYMMDD 整数比较) int endDate = endYear * 10000 + endMonth * 100 + endDay; int currentDate = st.wYear * 10000 + st.wMonth * 100 + st.wDay; return currentDate <= endDate; } } // 便捷宏:从配置验证 V2 授权并设置授权状态 // 需要包含 iniFile.h 和 LANChecker.h // 注意:只有签名有效且未过期才会跳过超时检测,过期则需要连接服务器续期 #define VERIFY_V2_AND_SET_AUTHORIZED() \ do { \ config* _v2cfg = IsDebug ? new config : new iniFile; \ std::string _pwdHmac = _v2cfg->GetStr("settings", "PwdHmac", ""); \ if (_pwdHmac.length() >= 3 && _pwdHmac.substr(0, 3) == "v2:") { \ std::string _deviceId = _v2cfg->GetStr("settings", "SN", ""); \ std::string _password = _v2cfg->GetStr("settings", "Password", ""); \ if (!_deviceId.empty() && !_password.empty() && \ VerifyV2::verifyPasswordV2(_deviceId, _password, _pwdHmac) && \ VerifyV2::isPasswordValid(_password)) { \ AuthTimeoutChecker::SetAuthorized(); \ } \ } \ delete _v2cfg; \ } while(0)