// ServerTlsSession.h — MFC 服务端单向 TLS(Schannel 入站)会话封装 // // 与客户端 TlsChannel 对称:客户端走 InitializeSecurityContext(出站),本类走 // AcceptSecurityContext(入站)。单向 TLS:不请求/校验客户端证书,客户端身份仍由 // 隧道内登录(V1 PwdHmac / V2 ECDSA)承担。 // // 分层: // ServerTlsIdentity —— 进程级「服务端身份」(证书 + 私钥 → 入站凭据)。可重入加载 // (Load 禁止用 call_once,供未来 UI/指令热切换),本轮启动时加载一次。 // ServerTlsSession —— 每条连接一个,持有握手/加解密所需的 Schannel 上下文与缓冲, // 随 CONTEXT_OBJECT 池化复用而重建。 // // 线程模型:Handshake 在专用握手线程上阻塞执行(方案 A);握手完成后,Decrypt 由 // IOCP 工作线程调用(每连接同一时刻只有一个未决 WSARecv,天然串行)、Encrypt 在 // SendLock 内调用。Schannel 上下文非线程安全,故 Encrypt/Decrypt 内部以 m_mtx 互斥。 // // 选型:Schannel(Windows 原生 SSPI),secur32.lib / crypt32.lib / ncrypt.lib 系统库。 // 证书/私钥为 PEM(与 Go 服务端 YAMA_TLS_CERT/KEY 同构,可共用同一对密钥);私钥 // 须为 PKCS#8(-----BEGIN PRIVATE KEY-----),见 ServerTlsSession.cpp 的加载实现。 #pragma once #include #ifndef SECURITY_WIN32 #define SECURITY_WIN32 1 #endif #include #include #include #include #include class config; // common/iniFile.h(仅需引用,避免把 ini 头拖进 Server.h 的 include 图) // ---- 进程级服务端身份(证书 + 私钥),可重入加载(禁止 call_once,供未来热切换)---- class ServerTlsIdentity { public: // 从 cfg(THIS_CFG)读 [tls] 节:Enabled / CertPath / KeyPath,并加载身份。 // 返回 true 表示「配置已生效」(开关关、或开关开且身份加载成功);开关开但加载 // 失败时返回 false 且 IsLoadError()==true,服务端须对 TLS ClientHello fail-closed。 static bool Load(config& cfg); static bool IsEnabled(); // [tls] Enabled != 0 static bool IsReady(); // 开关开 && 身份加载成功(有可用入站凭据) static bool IsLoadError(); // 开关开 && 身份加载失败(fail-closed) // 入站凭据(AcquireCredentialsHandle 产出)。返回拷贝;句柄值本身可安全拷贝。 static CredHandle Cred(); private: static void FreeIdentity(); static bool LoadIdentity(const std::string& certPath, const std::string& keyPath); }; // ---- 单连接服务端 TLS 会话(握手 + 加解密)---- class ServerTlsSession { public: ServerTlsSession() {} ~ServerTlsSession() { CloseHandles(); } ServerTlsSession(const ServerTlsSession&) = delete; ServerTlsSession& operator=(const ServerTlsSession&) = delete; // 阻塞式服务端握手(方案 A:专用握手线程上调用)。initial/initialLen 是嗅探阶段已 // 收到的 ClientHello 字节(可能只是分片;内部按 SEC_E_INCOMPLETE_MESSAGE 续收补齐)。 // 成功返回 true,失败 false(调用方关闭连接,fail-closed)。 bool Handshake(SOCKET sock, const BYTE* initial, int initialLen); // 解密:把本次 WSARecv 收到的密文 cipher[0..len] 喂入,把能解出的明文追加到 plain。 // 返回 0 = 成功(可能无明文);-1 = 错误 / 对端 close_notify(调用方关闭连接)。 int Decrypt(const BYTE* cipher, int len, std::vector& plain); // 加密:把明文 plain[0..len] 加密成完整 TLS 记录,追加到 wire。 // 返回加密的明文字节数(== len);-1 = 错误。 int Encrypt(const BYTE* plain, int len, std::vector& wire); bool Ready() const { return m_ready; } private: void CloseHandles(); bool SendAll(SOCKET sock, const void* data, int len); std::mutex m_mtx; // 序列化对 m_ctx 的 Encrypt/Decrypt(Schannel 非线程安全) CtxtHandle m_ctx = {}; bool m_ctxValid = false; bool m_ready = false; SecPkgContext_StreamSizes m_sizes = {}; std::vector m_raw; // 尚未解密 / 未消费完的 TLS 记录原始字节 };