E2EE (cifratura end-to-end) per Delphi

· Componenti

Dalla versione 2026.1.0 E2EE (End-To-End Encryption) è supportato (solo per i sottoscrittori eSeGeCe All-Access).

L'End-to-End Encryption (E2EE) garantisce che solo i peer in comunicazione possano leggere il contenuto dei messaggi scambiati. Anche il server che instrada i messaggi non può decifrarli. Questo articolo spiega come funziona E2EE tra due peer usando la crittografia a chiave pubblica per scambiare messaggi in modo sicuro.

E2EE spiegato

Principi fondamentali di E2EE

In un sistema E2EE tra due peer:


Panoramica del materiale crittografico

Ogni peer (ad esempio Alice e Bob) ha:

Le chiavi pubblica e privata sono matematicamente collegate, ma conoscere la chiave pubblica non rivela la chiave privata.


Passo 1: scambio delle chiavi pubbliche

Prima che possa avvenire una comunicazione cifrata, Alice e Bob devono conoscere le rispettive chiavi pubbliche.

Approcci tipici:

Questo scambio non compromette la sicurezza, perché le chiavi pubbliche non sono segrete.


Passo 2: stabilire un segreto condiviso (ECDH)

Per cifrare i messaggi in modo efficiente, Alice e Bob derivano prima un segreto condiviso usando Elliptic Curve Diffie-Hellman (ECDH).

Come funziona ECDH a livello concettuale

Grazie alle proprietà matematiche delle curve ellittiche, entrambi i calcoli producono lo stesso valore segreto, anche se nessuno dei due lati trasmette mai quel segreto.

In nessun momento il segreto condiviso viene inviato sulla rete.


Passo 3: derivare una chiave di cifratura simmetrica

Il segreto condiviso ECDH grezzo non viene usato direttamente per la cifratura. Viene invece elaborato attraverso una Key Derivation Function (KDF), tipicamente un hash crittografico come SHA-256.

Scopo della derivazione di chiave:


Il risultato è una chiave di cifratura simmetrica nota solo ad Alice e Bob.


Passo 4: cifratura del messaggio

Quando Alice vuole inviare un messaggio a Bob:

  1. Alice converte il messaggio in byte.
  2. Alice cifra il messaggio usando un cifrario simmetrico (comunemente AES-GCM) con:
    • La chiave simmetrica derivata
    • Un vettore di inizializzazione (IV) casuale
  3. Alice invia a Bob il messaggio cifrato e l'IV tramite il server.

AES-GCM è comunemente usato perché fornisce:



Passo 5: decifratura del messaggio

Quando Bob riceve il messaggio cifrato:

  1. Bob deriva indipendentemente la stessa chiave simmetrica usando ECDH e la stessa KDF.
  2. Bob decifra il messaggio usando la chiave simmetrica e l'IV.
  3. Se l'autenticazione ha successo, Bob ottiene il testo in chiaro originale.

Se il messaggio è stato alterato o se viene usata la chiave sbagliata, la decifratura fallisce.


Ruolo del server

In questa architettura, il server:


Il server non può:


Questa è la proprietà che definisce l'End-to-End Encryption.


Riepilogo

L'End-to-End Encryption tra due peer funziona combinando:

  1. Crittografia a chiave pubblica (per l'accordo di chiave)
  2. Crittografia simmetrica (per cifrare i messaggi in modo efficiente)
  3. Funzioni di derivazione di chiave (per sicurezza e correttezza)

Il risultato è un sistema in cui:


Questo modello è la spina dorsale crittografica dei moderni sistemi di messaggistica sicura. 

Esempio 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;