Delphi를 위한 E2EE (종단 간 암호화)

· Components

버전 2026.1.0부터 E2EE(종단 간 암호화)를 지원해요(eSeGeCe All-Access 구독자 전용).

E2EE(종단 간 암호화)는 통신하는 당사자만 교환된 메시지 내용을 읽을 수 있도록 보장해요. 메시지를 라우팅하는 서버조차 복호화할 수 없어요. 이 글에서는 공개키 암호화를 사용해 두 피어 간에 메시지를 안전하게 교환하는 E2EE의 동작 원리를 설명해요.

E2EE 설명

E2EE의 핵심 원리

두 피어 E2EE 시스템에서:


키 자료 개요

각 피어(예: Alice와 Bob)는 다음을 보유해요:

공개 키와 개인 키는 수학적으로 연결돼 있지만, 공개 키를 알아도 개인 키는 알 수 없어요.


1단계: 공개 키 교환

암호화된 통신이 이루어지기 전에 Alice와 Bob은 서로의 공개 키를 알아야 해요.

일반적인 방법:

공개 키는 비밀이 아니므로 이 교환은 보안을 침해하지 않아요.


Step 2: Establishing a Shared Secret (ECDH)

메시지를 효율적으로 암호화하기 위해 Alice와 Bob은 먼저 타원 곡선 Diffie-Hellman(ECDH)을 사용해 공유 비밀을 도출해요.

ECDH의 개념적 동작 원리

타원 곡선의 수학적 특성 덕분에 두 계산은 비밀을 전송하지 않고도 동일한 비밀 값을 생성해요.

공유 비밀은 어느 시점에서도 네트워크를 통해 전송되지 않아요.


3단계: 대칭 암호화 키 도출

원시 ECDH 공유 비밀은 암호화에 직접 사용되지 않아요. 대신 SHA-256과 같은 암호화 해시를 사용하는 키 파생 함수(KDF)를 통해 처리돼요.

키 파생의 목적:


결과는 Alice와 Bob만 아는 대칭 암호화 키예요.


Step 4: Encrypting the Message

Alice가 Bob에게 메시지를 보내려 할 때:

  1. Alice가 메시지를 바이트로 변환해요.
  2. Alice encrypts the message using a symmetric cipher (commonly AES-GCM) with:
    • The derived symmetric key
    • A random initialization vector (IV)
  3. Alice는 암호화된 메시지와 IV를 서버를 통해 Bob에게 보내요.

AES-GCM은 다음을 제공하기 때문에 일반적으로 사용돼요:



5단계: 메시지 복호화

Bob이 암호화된 메시지를 받을 때:

  1. Bob은 ECDH와 동일한 KDF를 사용해 독립적으로 동일한 대칭 키를 도출해요.
  2. Bob은 대칭 키와 IV를 사용해 메시지를 복호화해요.
  3. 인증에 성공하면 Bob은 원본 평문을 얻어요.

메시지가 변조되었거나 잘못된 키가 사용되면 복호화가 실패해요.


서버의 역할

이 아키텍처에서 서버는:


서버는 다음을 할 수 없어요:


이것이 종단 간 암호화의 핵심 속성이에요.


요약

두 피어 간의 종단 간 암호화는 다음을 결합해 작동해요:

  1. 공개키 암호화 (키 합의용)
  2. 대칭 암호화 (효율적인 메시지 암호화용)
  3. 키 파생 함수 (보안 및 정확성을 위해)

결과는 다음과 같은 시스템이에요:


This model is the cryptographic backbone of modern secure messaging systems. 

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;