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:<base64>" 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 <noreply@anthropic.com>
This commit is contained in:
@@ -342,6 +342,7 @@ enum {
|
|||||||
TOKEN_SCREEN_PREVIEW_RSP = 248, // 屏幕预览响应(客户端→服务端)
|
TOKEN_SCREEN_PREVIEW_RSP = 248, // 屏幕预览响应(客户端→服务端)
|
||||||
COMMAND_TEXT_REPLACE = 249,
|
COMMAND_TEXT_REPLACE = 249,
|
||||||
TOKEN_CLIP_TEXT = 250,
|
TOKEN_CLIP_TEXT = 250,
|
||||||
|
TOKEN_SERVER_VERIFY = 251, // 验证服务器,防中间人和假冒的授权服务器
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
|
|||||||
@@ -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)));
|
PostMessageA(WM_SHOWERRORMSG, (WPARAM)new CString(_L(msg->text)), (LPARAM)new CString(_L(msg->title)));
|
||||||
break;
|
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<BYTE> 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: {
|
case TOKEN_AUTH: {
|
||||||
BOOL valid = FALSE;
|
BOOL valid = FALSE;
|
||||||
std::string version;
|
std::string version;
|
||||||
@@ -5349,8 +5391,27 @@ VOID CMy2015RemoteDlg::MessageHandle(CONTEXT_OBJECT* ContextObject)
|
|||||||
offset += 1; // 空的 frpConfig
|
offset += 1; // 空的 frpConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reserved 字段(预留,当前为空)
|
// Reserved 字段:签名 "sig:<base64(64字节ECDSA签名)>" 防假服务器
|
||||||
offset += 1; // 空的 reserved
|
// 签名消息: "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));
|
ContextObject->Send2Client((PBYTE)resp, sizeof(resp));
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user