
비대칭키 암호화: 자물쇠와 열쇠의 혁명
어떻게 지구 반대편에 있는 서버와 안전하게 비밀을 주고받을까? HTTPS의 기반이 되는 공개키/개인키의 마법.

어떻게 지구 반대편에 있는 서버와 안전하게 비밀을 주고받을까? HTTPS의 기반이 되는 공개키/개인키의 마법.
내 서버는 왜 걸핏하면 뻗을까? OS가 한정된 메모리를 쪼개 쓰는 처절한 사투. 단편화(Fragmentation)와의 전쟁.

미로를 탈출하는 두 가지 방법. 넓게 퍼져나갈 것인가(BFS), 한 우물만 팔 것인가(DFS). 최단 경로는 누가 찾을까?

프론트엔드 개발자가 알아야 할 4가지 저장소의 차이점과 보안 이슈(XSS, CSRF), 그리고 언제 무엇을 써야 하는지에 대한 명확한 기준.

이름부터 빠릅니다. 피벗(Pivot)을 기준으로 나누고 또 나누는 분할 정복 알고리즘. 왜 최악엔 느린데도 가장 많이 쓰일까요?

제가 온라인 쇼핑몰을 처음 만들었을 때의 일입니다. 결제 시스템을 연동하다가 문득 무서운 생각이 들었습니다.
"유저가 입력한 신용카드 번호가 인터넷을 통과해서 서버로 오는데, 중간에 해커가 가로채면 어떡하지?"
HTTP로 통신한다는 건 평문(Plain Text)으로 데이터가 날아간다는 뜻입니다. 네트워크를 지나가는 패킷을 누군가 캡처하면 그대로 읽힙니다.
POST /payment
카드번호: 4532-1234-5678-9010
와이파이가 뚫린 카페에서 쇼핑하면? 같은 네트워크에 있는 사람이 패킷을 긁어갈 수 있습니다. 이걸 막으려면 암호화가 필요했습니다.
그런데 처음 공부할 때 이해가 안 됐던 게: "암호화하려면 비밀번호(키)가 필요한데, 그 비밀번호는 어떻게 안전하게 전달하지?"
이게 바로 Key Distribution Problem(키 배송 문제)였고, 비대칭키 암호화가 이 문제를 해결한 혁명이었습니다.
먼저 전통적인 방식인 대칭키(Symmetric Key) 방식을 봅시다.
암호화할 때 쓴 키 = 복호화할 때 쓸 키 (동일)
평문: "안녕하세요"
키: "SECRET123"
암호화: "xJ2k9$mP@"
...
암호문: "xJ2k9$mP@"
키: "SECRET123" (동일한 키)
복호화: "안녕하세요"
빠르고 간단합니다. 하지만 치명적인 문제가 있습니다.
제가 서버와 안전하게 통신하려면:
비유하자면:
이게 바로 Key Distribution Problem입니다.
"그럼 처음에 만날 때 키를 직접 손으로 전달하면 되잖아?"
하지만 제 서비스 사용자가 전 세계에 흩어져 있는데 어떻게 다 만나나요? 물리적으로 불가능했습니다.
1976년, 두 명의 천재(Whitfield Diffie, Martin Hellman)가 혁명적인 아이디어를 냈습니다.
"암호화하는 키와 복호화하는 키를 다르게 만들면 어떨까?"
이게 비대칭키(Asymmetric Key) 방식입니다.
핵심:
제가 서버에게 비밀 메시지를 보낸다고 칩시다.
서버가 인터넷에 자기 공개키(자물쇠)를 올립니다. 아무나 다운로드할 수 있습니다.
서버 공개키: RSA-2048-PUBLIC-KEY-ABC123...
const message = "신용카드: 1234-5678-9012";
const encrypted = encrypt(message, 서버공개키);
// 결과: "xK9jP2#mQ..."
이제 암호문 "xK9jP2#mQ..."를 서버에게 보냅니다.
해커가 패킷을 캡처해서 암호문을 손에 넣었습니다.
암호문: "xK9jP2#mQ..."
하지만 열 수가 없습니다. 왜냐하면 이 암호문은 서버의 개인키로만 복호화할 수 있기 때문입니다. 서버의 개인키는 서버만 가지고 있습니다.
const decrypted = decrypt("xK9jP2#mQ...", 서버개인키);
// 결과: "신용카드: 1234-5678-9012"
성공! 비밀이 지켜졌습니다.
이번엔 반대로 씁니다.
제가 "이 문서는 제가 작성했습니다"라는 걸 증명하고 싶습니다. 인터넷에선 누구나 제 이름을 사칭할 수 있으니까요.
const document = "송금: 100만원 → 홍길동";
const signature = sign(document, 내개인키);
// 결과: "9xP2#kQ..."
이제 문서와 서명을 함께 보냅니다:
문서: "송금: 100만원 → 홍길동"
서명: "9xP2#kQ..."
받는 사람(은행)은 제 공개키를 가져옵니다 (인터넷에 공개되어 있음).
const isValid = verify(document, signature, 내공개키);
// 결과: true (진짜 내가 서명함)
만약 문서가 조작됐거나, 다른 사람이 서명했다면 false가 나옵니다.
제가 GitHub에 코드를 올릴 때 GPG 키로 커밋에 서명합니다.
git commit -S -m "Fix security bug"
GitHub는 제 공개키로 이 커밋이 진짜 제가 한 건지 검증합니다. 커밋 옆에 "Verified" 배지가 뜹니다.
제 블로그 백엔드에서 JWT(JSON Web Token)를 쓸 때 비대칭키 방식을 적용했습니다.
const jwt = require('jsonwebtoken');
const SECRET = 'mySecretPassword'; // 대칭키
// 토큰 생성 (서버)
const token = jwt.sign({ userId: 123 }, SECRET);
// 토큰 검증 (서버)
const payload = jwt.verify(token, SECRET);
문제: 서버만 토큰을 검증할 수 있습니다.
프론트엔드나 다른 서비스가 토큰을 검증하려면 SECRET를 공유해야 하는데, 보안상 위험합니다.
const fs = require('fs');
// 서버: 개인키로 서명
const privateKey = fs.readFileSync('private.key');
const token = jwt.sign({ userId: 123 }, privateKey, { algorithm: 'RS256' });
// 프론트엔드: 공개키로 검증
const publicKey = fs.readFileSync('public.key');
const payload = jwt.verify(token, publicKey);
이제:
비대칭키는 치명적인 단점이 있습니다. 수학적으로 너무 복잡해서 느립니다. 대칭키보다 100배 이상 느립니다.
파일 하나를 RSA로 암호화/복호화하면 몇 초가 걸립니다. 실시간 웹 통신에는 쓸 수 없습니다.
인터넷(HTTPS)은 꼼수를 씁니다.
sequenceDiagram
participant Client
participant Server
Client->>Server: 1. 서버 공개키 요청
Server-->>Client: 2. 서버 공개키 전송
Client->>Client: 3. 대칭키(Session Key) 생성
Client->>Server: 4. 대칭키를 서버 공개키로 암호화해서 전달
Server->>Server: 5. 서버 개인키로 대칭키 복호화
Note over Client,Server: 이제 양쪽 모두 대칭키 보유
Client->>Server: 6. 이후 모든 통신은 대칭키로 암호화
Server-->>Client: 7. 빠른 대칭키 통신
처음 접속 시 비대칭키로 연결. 서버가 자기 공개키(SSL 인증서)를 보냅니다.
클라이언트가 랜덤으로 대칭키(Session Key) 생성. 이 대칭키를 서버 공개키로 암호화해서 전송. 서버는 개인키로 복호화.
이제 양쪽 모두 같은 대칭키를 가지고 있습니다. 해커는 이 대칭키를 모릅니다 (암호화돼서 전달됐으니까).
이후 모든 데이터는 대칭키로 암호화. 빠릅니다.
제가 실제로 쓴 명령어입니다.
openssl genrsa -out private.key 2048
openssl rsa -in private.key -pubout -out public.key
private.key (비밀! 절대 공유 금지)
public.key (누구에게나 공개 가능)
RSA 2048-bit 암호화는 대칭키(AES-256)보다 100배 느립니다.
해결책: 하이브리드 방식 (HTTPS처럼).
개인키를 잃어버리면? 모든 암호문이 영원히 잠깁니다. 개인키가 유출되면? 모든 보안이 무너집니다.
해결책:
| 목적 | 방식 | 예시 |
|---|---|---|
| 비밀 전달 | 공개키로 암호화 → 개인키로 복호화 | HTTPS, 메일 암호화 |
| 신원 증명 | 개인키로 서명 → 공개키로 검증 | Git Commit, JWT, SSL 인증서 |
| 빠른 암호화 | 대칭키 | AES (파일 암호화) |
| 실무 | 하이브리드 | HTTPS (비대칭+대칭) |
처음 쇼핑몰을 만들 때 "어떻게 신용카드 정보를 안전하게 받지?"라는 고민에서 시작했습니다. 비대칭키 암호화를 공부하고 나서야 "아, 인터넷이 이렇게 작동하는구나"를 알았습니다.
모두 공개키/개인키의 마법입니다.
이 글을 쓰고 있는 지금도 제 브라우저 주소창엔 자물쇠 아이콘이 떠 있습니다. 저와 서버가 안전하게 대화하고 있다는 증표입니다.
공개키 암호화를 반대로 쓰면 서명이 됩니다.
"누구나 열 수 있는데 무슨 의미가 있냐?"라고 할 수 있습니다. 하지만 "이게 A의 공개키로 열린다"는 사실 자체가 중요합니다. A의 비밀키는 A밖에 없으니까요. 즉, "이 문서는 A가 작성했음이 확실하다"는 것을 수학적으로 증명하는 겁니다. 이것이 비트코인 거래, 공인인증서, JWT 토큰 서명의 핵심 원리입니다.
RSA는 훌륭하지만, 컴퓨터가 발전하면서 키가 너무 커져야 한다는 단점이 있습니다. 안전하려면 최소 2048비트는 되어야 하는데, 이는 모바일 기기에서 부담스럽습니다.
그래서 등장한 것이 ECC (Elliptic Curve Cryptography)입니다. 소인수분해 대신 타원 곡선의 수학적 성질을 이용합니다.