E2EE: cifrado de extremo a extremo para Delphi

· Componentes

A partir de la versión 2026.1.0 se admite E2EE (End-To-End Encryption), sólo para suscriptores de eSeGeCe All-Access.

El cifrado de extremo a extremo (E2EE) garantiza que sólo los peers que se comunican pueden leer el contenido de los mensajes intercambiados. Ni siquiera el servidor que enruta los mensajes puede descifrarlos. Este artículo explica cómo funciona E2EE entre dos peers usando criptografía de clave pública para intercambiar mensajes de forma segura.

E2EE en detalle

Principios básicos de E2EE

En un sistema E2EE entre dos peers:


Visión general del material de claves

Cada peer (por ejemplo, Alice y Bob) tiene:

Las claves pública y privada están vinculadas matemáticamente, pero conocer la clave pública no revela la clave privada.


Paso 1: intercambio de claves públicas

Antes de que pueda producirse la comunicación cifrada, Alice y Bob deben conocer sus claves públicas mutuas.

Enfoques habituales:

Este intercambio no compromete la seguridad, porque las claves públicas no son secretas.


Paso 2: establecer un secreto compartido (ECDH)

Para cifrar los mensajes de forma eficiente, Alice y Bob primero derivan un secreto compartido usando Elliptic Curve Diffie–Hellman (ECDH).

Cómo funciona ECDH a nivel conceptual

Por las propiedades matemáticas de las curvas elípticas, ambos cálculos producen el mismo valor secreto, aunque ninguna de las partes transmite nunca ese secreto.

En ningún momento se envía el secreto compartido por la red.


Paso 3: derivar una clave simétrica de cifrado

El secreto compartido en bruto de ECDH no se usa directamente para cifrar. En su lugar, se procesa mediante una Key Derivation Function (KDF), típicamente un hash criptográfico como SHA-256.

Objetivo de la derivación de clave:


El resultado es una clave simétrica de cifrado conocida sólo por Alice y Bob.


Paso 4: cifrar el mensaje

Cuando Alice quiere enviar un mensaje a Bob:

  1. Alice convierte el mensaje a bytes.
  2. Alice cifra el mensaje usando un cifrado simétrico (habitualmente AES-GCM) con:
    • la clave simétrica derivada
    • un vector de inicialización (IV) aleatorio
  3. Alice envía el mensaje cifrado y el IV a Bob a través del servidor.

AES-GCM es habitual porque aporta:



Paso 5: descifrar el mensaje

Cuando Bob recibe el mensaje cifrado:

  1. Bob deriva de forma independiente la misma clave simétrica usando ECDH y la misma KDF.
  2. Bob descifra el mensaje usando la clave simétrica y el IV.
  3. Si la autenticación es correcta, Bob obtiene el texto plano original.

Si el mensaje ha sido alterado o se usa una clave incorrecta, el descifrado falla.


El papel del servidor

En esta arquitectura, el servidor:


El servidor no puede:


Ésta es la propiedad que define al cifrado de extremo a extremo.


Resumen

El cifrado de extremo a extremo entre dos peers funciona combinando:

  1. Criptografía de clave pública (para el acuerdo de claves)
  2. Criptografía simétrica (para cifrar mensajes de forma eficiente)
  3. Funciones de derivación de claves (para seguridad y corrección)

El resultado es un sistema en el que:


Este modelo es la columna criptográfica de los sistemas modernos de mensajería segura. 

Ejemplo de 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;