Delphi 向け E2EE エンドツーエンド暗号化

· コンポーネント

バージョン 2026.1.0 より E2EE(エンドツーエンド暗号化)がサポートされました(eSeGeCe All-Access サブスクライバー専用)。

エンドツーエンド暗号化(E2EE)は、通信するピアのみが交換されたメッセージの内容を読めることを保証します。メッセージをルーティングするサーバーでさえ復号できません。本記事では、公開鍵暗号を使用して2 つのピア間で安全にメッセージを交換する E2EE の仕組みを説明します。

E2EE の解説

E2EE の核となる原則

2 ピア E2EE システムでは:


鍵の概要

各ピア(例:アリスとボブ)は以下を持ちます:

公開鍵と秘密鍵は数学的に結びついていますが、公開鍵を知っても秘密鍵は分かりません。

ステップ 1:公開鍵の交換

暗号化通信を始める前に、アリスとボブはお互いの公開鍵を知る必要があります。

一般的なアプローチ:

公開鍵は秘密ではないため、この交換によってセキュリティが損なわれることはありません

ステップ 2:共有シークレットの確立(ECDH)

メッセージを効率よく暗号化するため、アリスとボブはまず楕円曲線ディフィー・ヘルマン(ECDH)を使用して共有シークレットを導出します。

ECDH の仕組み(概念)

楕円曲線の数学的特性により、どちらの側もそのシークレットを送信することなく、両方の計算が同じシークレット値を生成します。

共有シークレットがネットワーク上を送信されることは一切ありません。

ステップ 3:対称暗号化鍵の導出

生の ECDH 共有シークレットは暗号化に直接使用しません。代わりに、通常は SHA-256 などの暗号ハッシュである鍵導出関数(KDF)で処理されます。

鍵導出の目的:


結果はアリスとボブのみが知る対称暗号化鍵です。

ステップ 4:メッセージの暗号化

アリスがボブにメッセージを送信する場合:

  1. アリスはメッセージをバイト列に変換します。
  2. アリスは対称暗号(一般的に AES-GCM)と以下を使用してメッセージを暗号化します:
    • 導出した対称鍵
    • ランダムな初期化ベクター(IV)
  3. アリスは暗号化されたメッセージと IV をサーバー経由でボブに送信します。

AES-GCM は以下を提供するため一般的に使用されます:


ステップ 5:メッセージの復号

ボブが暗号化されたメッセージを受信した場合:

  1. ボブは ECDH と同じ KDF を使用して独立して同じ対称鍵を導出します。
  2. ボブは対称鍵と IV を使用してメッセージを復号します。
  3. 認証が成功した場合、ボブは元の平文を取得します。

メッセージが改ざんされていた場合や誤った鍵が使用された場合、復号は失敗します。

サーバーの役割

このアーキテクチャでは、サーバーは:


サーバーは以下のことができません


これがエンドツーエンド暗号化の定義的特性です。


まとめ

2 つのピア間のエンドツーエンド暗号化は以下を組み合わせることで機能します:

  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;