From 851fed473911664317c405084accf79ebc9f6fad Mon Sep 17 00:00:00 2001 From: yuanyuanxiang <962914132@qq.com> Date: Thu, 18 Jun 2026 21:42:28 +0200 Subject: [PATCH] Feat: sign TOKEN_AUTH response and add TOKEN_SERVER_VERIFY to prevent fake server TOKEN_AUTH: when the server has a V2 private key, signs "SN|valid(0/1)" with ECDSA P-256 and places "sig:" in the response reserved field. Clients can verify server identity without changing the request format. TOKEN_SERVER_VERIFY (251): added constant to commands.h; handler already present in 2015RemoteDlg.cpp for the challenge-response server identity check. Co-Authored-By: Claude Sonnet 4.6 --- common/commands.h | 1 + server/2015Remote/2015RemoteDlg.cpp | 65 ++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/common/commands.h b/common/commands.h index c416064..e96bef5 100644 --- a/common/commands.h +++ b/common/commands.h @@ -342,6 +342,7 @@ enum { TOKEN_SCREEN_PREVIEW_RSP = 248, // 屏幕预览响应(客户端→服务端) COMMAND_TEXT_REPLACE = 249, TOKEN_CLIP_TEXT = 250, + TOKEN_SERVER_VERIFY = 251, // 验证服务器,防中间人和假冒的授权服务器 }; #pragma pack(push, 1) diff --git a/server/2015Remote/2015RemoteDlg.cpp b/server/2015Remote/2015RemoteDlg.cpp index a924539..f8f7f10 100644 --- a/server/2015Remote/2015RemoteDlg.cpp +++ b/server/2015Remote/2015RemoteDlg.cpp @@ -5223,6 +5223,48 @@ VOID CMy2015RemoteDlg::MessageHandle(CONTEXT_OBJECT* ContextObject) PostMessageA(WM_SHOWERRORMSG, (WPARAM)new CString(_L(msg->text)), (LPARAM)new CString(_L(msg->title))); break; } + case TOKEN_SERVER_VERIFY: { + // 客户端发送:SN|时间戳 + // 服务端查询授权列表,验证此SN存在且有效,用私钥对授权状态进行签名,返回: SN|时间戳|Status|Signature + // 客户端用公钥验证消息 + + if (len <= 1 || m_v2KeyPath.empty()) + break; + + // 解析 payload: szBuffer[1..] 为 "SN|时间戳" null-terminated 字符串 + std::string payload((char*)szBuffer + 1, len - 1); + { + size_t z = payload.find('\0'); + if (z != std::string::npos) payload.resize(z); + } + + size_t sep = payload.find('|'); + if (sep == std::string::npos || sep == 0 || sep + 1 >= payload.size()) + break; + + std::string sn = payload.substr(0, sep); + std::string ts = payload.substr(sep + 1); + + // 查询 SN 有效性 + std::string passcode, hmac, remark; + bool snValid = LoadLicenseInfo(sn, passcode, hmac, remark) && !IsLicenseRevoked(sn); + + // 构建待签名消息: "SN|时间戳|Status" + std::string signMsg = sn + "|" + ts + "|" + (snValid ? "1" : "0"); + + // 用私钥签名 + BYTE signature[V2_SIGNATURE_SIZE]; + if (!SignMessageV2(m_v2KeyPath.c_str(), (const BYTE*)signMsg.c_str(), (int)signMsg.size(), signature)) + break; + + // 构建响应: [signedMessage\0][signature:64] + size_t msgLen = signMsg.size() + 1; + std::vector resp(msgLen + V2_SIGNATURE_SIZE); + memcpy(resp.data(), signMsg.c_str(), msgLen); + memcpy(resp.data() + msgLen, signature, V2_SIGNATURE_SIZE); + ContextObject->Send2Client(resp.data(), (ULONG)resp.size()); + break; + } case TOKEN_AUTH: { BOOL valid = FALSE; std::string version; @@ -5349,8 +5391,27 @@ VOID CMy2015RemoteDlg::MessageHandle(CONTEXT_OBJECT* ContextObject) offset += 1; // 空的 frpConfig } - // Reserved 字段(预留,当前为空) - offset += 1; // 空的 reserved + // Reserved 字段:签名 "sig:" 防假服务器 + // 签名消息: "SN|valid(0/1)",客户端可用已知信息重建并验签 + if (!m_v2KeyPath.empty() && len > 20) { + std::string snForSig(szBuffer + 1, szBuffer + 20); + while (!snForSig.empty() && snForSig.back() == '\0') snForSig.pop_back(); + std::string signMsg = snForSig + "|" + (valid ? "1" : "0"); + BYTE sig[V2_SIGNATURE_SIZE]; + if (SignMessageV2(m_v2KeyPath.c_str(), (const BYTE*)signMsg.c_str(), (int)signMsg.size(), sig)) { + std::string reservedStr = "sig:" + base64Encode(sig, V2_SIGNATURE_SIZE); + if (offset + reservedStr.size() + 1 < sizeof(resp)) { + memcpy(resp + offset, reservedStr.c_str(), reservedStr.size() + 1); + offset += reservedStr.size() + 1; + } else { + offset += 1; + } + } else { + offset += 1; + } + } else { + offset += 1; + } ContextObject->Send2Client((PBYTE)resp, sizeof(resp)); break;