Delphi E2EE(端对端加密)

· 组件

2026.1.0 版本起,支持 E2EE(端对端加密),仅面向 eSeGeCe All-Access 订阅用户。

端对端加密(E2EE)确保只有通信双方才能读取所交换消息的内容。即使负责路由消息的服务器也无法解密。本文介绍 E2EE 如何在两个对等节点之间使用公钥密码学安全地交换消息。

E2EE 原理解析

E2EE 的核心原则

在双节点 E2EE 系统中:


密钥材料概览

每个节点(例如 Alice 和 Bob)各自拥有:

公钥和私钥在数学上相互关联,但知道公钥并不会暴露私钥。


第一步:交换公钥

在开始加密通信之前,Alice 和 Bob 必须知道彼此的公钥。

常见方式:

此交换不会危及安全性,因为公钥并非秘密。


第二步:建立共享密钥(ECDH)

为了高效加密消息,Alice 和 Bob 首先使用椭圆曲线 Diffie-Hellman(ECDH)算法派生出一个共享密钥

ECDH 工作原理概要

由于椭圆曲线的数学特性,两者的计算结果相同,即获得相同的密钥值,而双方都无需在网络上传输该密钥。

共享密钥在任何时候都不会通过网络传输。


第三步:派生对称加密密钥

原始 ECDH 共享密钥不会直接用于加密,而是通过密钥派生函数(KDF)(通常为 SHA-256 等加密哈希)进行处理。

密钥派生的目的:


最终结果是一个仅 Alice 和 Bob 知道的对称加密密钥


第四步:加密消息

当 Alice 要向 Bob 发送消息时:

  1. Alice 将消息转换为字节。
  2. Alice 使用对称加密算法(通常为 AES-GCM)和以下内容加密消息:
    • 派生的对称密钥
    • 随机初始化向量(IV)
  3. Alice 通过服务器将加密消息和 IV 发送给 Bob。

AES-GCM 常被选用,因为它提供:



第五步:解密消息

当 Bob 收到加密消息时:

  1. Bob 使用 ECDH 和相同的 KDF 独立派生出相同的对称密钥。
  2. Bob 使用对称密钥和 IV 解密消息。
  3. 若认证成功,Bob 获得原始明文。

若消息被篡改或使用了错误的密钥,解密将失败。


服务器的角色

在此架构中,服务器:


服务器无法


这是端对端加密的核心特性。


总结

两个节点之间的端对端加密通过以下三者的结合实现:

  1. 公钥密码学(用于密钥协商)
  2. 对称密码学(用于高效消息加密)
  3. 密钥派生函数(用于安全性和正确性)

最终构成一个:


的系统。这一模型是现代安全消息系统的密码学基础。

E2EE 示例

// ... Create the Server
WSServer := TsgcWebSocketHTTPServer.Create(nil);
WSServer.Port := 80;
WSPE2EE := TsgcWSPServer_E2EE.Create(nil);
WSPE2EE.Server := WSServer;
WSServer.Active := True;
// ... Create 2 clients
WSClient1 := TsgcWebSocketClient.Create(nil);
WSClient1.Host := '127.0.0.1';
WSClient1.Port := 80;
E2EE1 := TsgcWSPClient_E2EE.Create(nil);
E2EE1.Client := WSClient1;
E2EE1.E2EE_Otpions.UserId := 'client-1';
WSClient1.Active := True;
WSClient2 := TsgcWebSocketClient.Create(nil);
WSClient2.Host := '127.0.0.1';
WSClient2.Port := 80;
E2EE2 := TsgcWSPClient_E2EE.Create(nil);
E2EE2.OnE2EEMessageText := OnE2EEMessageTextEvent;
E2EE2.E2EE_Otpions.UserId := 'client-2';
E2EE2.Client := WSClient2;
WSClient2.Active := True;
// ... client-1 send a message to client-2
E2EE1.SendDirectMessage('client-2', 'Hello Client-2');
// ... read the message in the OnE2EEMessageText event
procedure OnE2EEMessageText(Sender: TObject; const aFrom, aText: string);
begin
  DoLog('#direct_message: ' + aText);
end;