프롤로그 - "어떻게 신용카드 정보를 안전하게 보낼까?"
제가 온라인 쇼핑몰을 처음 만들었을 때의 일입니다. 결제 시스템을 연동하다가 문득 무서운 생각이 들었습니다.
"유저가 입력한 신용카드 번호가 인터넷을 통과해서 서버로 오는데, 중간에 해커가 가로채면 어떡하지?"
HTTP로 통신한다는 건 평문(Plain Text)으로 데이터가 날아간다는 뜻입니다. 네트워크를 지나가는 패킷을 누군가 캡처하면 그대로 읽힙니다.
POST /payment
카드번호: 4532-1234-5678-9010
와이파이가 뚫린 카페에서 쇼핑하면? 같은 네트워크에 있는 사람이 패킷을 긁어갈 수 있습니다. 이걸 막으려면 암호화가 필요했습니다.
그런데 처음 공부할 때 이해가 안 됐던 게: "암호화하려면 비밀번호(키)가 필요한데, 그 비밀번호는 어떻게 안전하게 전달하지?"
이게 바로 Key Distribution Problem(키 배송 문제)였고, 비대칭키 암호화가 이 문제를 해결한 혁명이었습니다.
1. 대칭키의 한계 - 키를 어떻게 전달하지?
먼저 전통적인 방식인 대칭키(Symmetric Key) 방식을 봅시다.
대칭키 방식
암호화할 때 쓴 키 = 복호화할 때 쓸 키 (동일)
평문: "안녕하세요"
키: "SECRET123"
암호화: "xJ2k9$mP@"
...
암호문: "xJ2k9$mP@"
키: "SECRET123" (동일한 키)
복호화: "안녕하세요"
빠르고 간단합니다. 하지만 치명적인 문제가 있습니다.
문제 - 키를 어떻게 전달하지?
제가 서버와 안전하게 통신하려면:
- 서버와 제가 같은 키(SECRET123)를 공유해야 합니다.
- 그 키를 인터넷으로 보내야 합니다.
- 그 키가 도중에 해커한테 들키면 끝장입니다.
비유하자면:
- 저: "이 편지는 비밀이니까 자물쇠로 잠갔어요."
- 서버: "좋아요. 열쇠를 보내주세요."
- 저: "열쇠를... 우편으로 보낼게요?"
- 우체부가 열쇠를 복사해갑니다. ❌
이게 바로 Key Distribution Problem입니다.
제가 처음 생각한 해결책 (실패)
"그럼 처음에 만날 때 키를 직접 손으로 전달하면 되잖아?"
하지만 제 서비스 사용자가 전 세계에 흩어져 있는데 어떻게 다 만나나요? 물리적으로 불가능했습니다.
2. 천재들의 발상 - "잠그는 키와 여는 키를 다르게 만들자"
1976년, 두 명의 천재(Whitfield Diffie, Martin Hellman)가 혁명적인 아이디어를 냈습니다.
"암호화하는 키와 복호화하는 키를 다르게 만들면 어떨까?"
이게 비대칭키(Asymmetric Key) 방식입니다.
공개키와 개인키
- 공개키(Public Key): "자물쇠"라고 생각하세요. 누구에게나 마음껏 뿌립니다.
- 개인키(Private Key): "열쇠"입니다. 나만 몰래 가지고 있습니다.
핵심:
- 공개키로 잠근 것은 개인키로만 열 수 있습니다.
- 개인키로 잠근 것은 공개키로만 열 수 있습니다.
3. 시나리오 1 - 비밀 편지 보내기 (기밀성)
제가 서버에게 비밀 메시지를 보낸다고 칩시다.
Step 1: 서버가 공개키를 공개
서버가 인터넷에 자기 공개키(자물쇠)를 올립니다. 아무나 다운로드할 수 있습니다.
서버 공개키: RSA-2048-PUBLIC-KEY-ABC123...
Step 2: 나는 서버의 공개키로 메시지를 암호화
const message = "신용카드: 1234-5678-9012";
const encrypted = encrypt(message, 서버공개키);
// 결과: "xK9jP2#mQ..."
이제 암호문 "xK9jP2#mQ..."를 서버에게 보냅니다.
Step 3: 해커가 중간에 가로챔
해커가 패킷을 캡처해서 암호문을 손에 넣었습니다.
암호문: "xK9jP2#mQ..."
하지만 열 수가 없습니다. 왜냐하면 이 암호문은 서버의 개인키로만 복호화할 수 있기 때문입니다. 서버의 개인키는 서버만 가지고 있습니다.
Step 4: 서버가 개인키로 복호화
const decrypted = decrypt("xK9jP2#mQ...", 서버개인키);
// 결과: "신용카드: 1234-5678-9012"
성공! 비밀이 지켜졌습니다.
핵심 정리
- 공개키는 누구나 알아도 됩니다 (자물쇠는 복사해도 상관없음).
- 개인키만 절대 유출되지 않으면 안전합니다.
4. 시나리오 2 - 전자 서명 (인증)
이번엔 반대로 씁니다.
문제 상황
제가 "이 문서는 제가 작성했습니다"라는 걸 증명하고 싶습니다. 인터넷에선 누구나 제 이름을 사칭할 수 있으니까요.
Step 1: 내 개인키로 문서를 서명
const document = "송금: 100만원 → 홍길동";
const signature = sign(document, 내개인키);
// 결과: "9xP2#kQ..."
이제 문서와 서명을 함께 보냅니다:
문서: "송금: 100만원 → 홍길동"
서명: "9xP2#kQ..."
Step 2: 받는 사람이 내 공개키로 검증
받는 사람(은행)은 제 공개키를 가져옵니다 (인터넷에 공개되어 있음).
const isValid = verify(document, signature, 내공개키);
// 결과: true (진짜 내가 서명함)
만약 문서가 조작됐거나, 다른 사람이 서명했다면 false가 나옵니다.
왜 안전한가?
- 내 개인키로만 만들 수 있는 서명입니다.
- 누군가 문서를 위조하려 해도, 내 개인키가 없으면 유효한 서명을 만들 수 없습니다.
실제 사례 - Git Commit 서명
제가 GitHub에 코드를 올릴 때 GPG 키로 커밋에 서명합니다.
git commit -S -m "Fix security bug"
GitHub는 제 공개키로 이 커밋이 진짜 제가 한 건지 검증합니다. 커밋 옆에 "Verified" 배지가 뜹니다.
5. 제가 직접 구현해본 경험 - JWT 토큰 서명
제 블로그 백엔드에서 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);
이제:
- 서버는 개인키로 토큰 발급.
- 클라이언트는 공개키로 검증 (공개키는 누구에게나 배포 가능).
- 다른 마이크로서비스도 공개키만 있으면 토큰 검증 가능.
6. HTTPS: 느린 비대칭키를 어떻게 실용화했나?
비대칭키는 치명적인 단점이 있습니다. 수학적으로 너무 복잡해서 느립니다. 대칭키보다 100배 이상 느립니다.
파일 하나를 RSA로 암호화/복호화하면 몇 초가 걸립니다. 실시간 웹 통신에는 쓸 수 없습니다.
해결책 - 하이브리드 (HTTPS/TLS)
인터넷(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. 빠른 대칭키 통신
단계별 설명
Step 1-2: 악수(Handshake)
처음 접속 시 비대칭키로 연결. 서버가 자기 공개키(SSL 인증서)를 보냅니다.
Step 3-5: 대칭키 공유
클라이언트가 랜덤으로 대칭키(Session Key) 생성. 이 대칭키를 서버 공개키로 암호화해서 전송. 서버는 개인키로 복호화.
이제 양쪽 모두 같은 대칭키를 가지고 있습니다. 해커는 이 대칭키를 모릅니다 (암호화돼서 전달됐으니까).
Step 6-7: 빠른 통신
이후 모든 데이터는 대칭키로 암호화. 빠릅니다.
결과
- 보안: 비대칭키 덕분에 대칭키를 안전하게 공유.
- 속도: 대칭키로 실제 데이터 전송.
7. 팁 - RSA 키 생성하기
제가 실제로 쓴 명령어입니다.
개인키 생성
openssl genrsa -out private.key 2048
공개키 추출
openssl rsa -in private.key -pubout -out public.key
파일 확인
private.key (비밀! 절대 공유 금지)
public.key (누구에게나 공개 가능)
8. 비대칭키의 단점과 대안
단점 1 - 느림
RSA 2048-bit 암호화는 대칭키(AES-256)보다 100배 느립니다.
해결책: 하이브리드 방식 (HTTPS처럼).
단점 2 - 키 관리
개인키를 잃어버리면? 모든 암호문이 영원히 잠깁니다. 개인키가 유출되면? 모든 보안이 무너집니다.
해결책:
- AWS KMS, HashiCorp Vault 같은 키 관리 서비스 사용.
- 정기적으로 키 교체 (Key Rotation).
9. 요약 - 언제 무엇을 쓸까?
| 목적 | 방식 | 예시 |
|---|---|---|
| 비밀 전달 | 공개키로 암호화 → 개인키로 복호화 | HTTPS, 메일 암호화 |
| 신원 증명 | 개인키로 서명 → 공개키로 검증 | Git Commit, JWT, SSL 인증서 |
| 빠른 암호화 | 대칭키 | AES (파일 암호화) |
| 실무 | 하이브리드 | HTTPS (비대칭+대칭) |
마치며 - "인터넷은 이 마법 덕분에 작동합니다"
처음 쇼핑몰을 만들 때 "어떻게 신용카드 정보를 안전하게 받지?"라는 고민에서 시작했습니다. 비대칭키 암호화를 공부하고 나서야 "아, 인터넷이 이렇게 작동하는구나"를 알았습니다.
- 은행 사이트(HTTPS)
- 로그인 토큰(JWT)
- Git 커밋 서명
- SSL 인증서
모두 공개키/개인키의 마법입니다.
이 글을 쓰고 있는 지금도 제 브라우저 주소창엔 자물쇠 아이콘이 떠 있습니다. 저와 서버가 안전하게 대화하고 있다는 증표입니다.
4.5. 전자서명 (Digital Signature) - 거꾸로 쓰기
공개키 암호화를 반대로 쓰면 서명이 됩니다.
- 암호화: B의 공개키로 잠금 -> B만 열 수 있음 (기밀성).
- 서명: A의 비밀키로 잠금 -> 누구나 A의 공개키로 열어볼 수 있음 (인증).
"누구나 열 수 있는데 무슨 의미가 있냐?"라고 할 수 있습니다. 하지만 "이게 A의 공개키로 열린다"는 사실 자체가 중요합니다. A의 비밀키는 A밖에 없으니까요. 즉, "이 문서는 A가 작성했음이 확실하다"는 것을 수학적으로 증명하는 겁니다. 이것이 비트코인 거래, 공인인증서, JWT 토큰 서명의 핵심 원리입니다.
10. 차세대 암호화 - 타원 곡선 암호(ECC)
RSA는 훌륭하지만, 컴퓨터가 발전하면서 키가 너무 커져야 한다는 단점이 있습니다. 안전하려면 최소 2048비트는 되어야 하는데, 이는 모바일 기기에서 부담스럽습니다.
그래서 등장한 것이 ECC (Elliptic Curve Cryptography)입니다. 소인수분해 대신 타원 곡선의 수학적 성질을 이용합니다.
- 장점: 256비트 ECC 키가 3072비트 RSA 키와 같은 보안 강도를 가집니다.
- 효과: 키가 작으니 계산이 빠르고 배터리를 덜 씁니다.
- 현황: 비트코인/이더리움(secp256k1)과 최신 HTTPS 통신(ECDSA)은 대부분 ECC를 씁니다. 만약 서버를 직접 세팅한다면 RSA 대신 ECDSA 인증서를 쓰는 것이 성능상 유리합니다.
11. 핵심 정리
Q1. HTTPS는 비대칭키 방식인가요, 대칭키 방식인가요?
- 답변: 둘 다 사용하는 하이브리드 방식입니다. 처음 연결(Handshake)할 때만 비대칭키를 사용하여 '대칭키'를 안전하게 교환하고, 이후 데이터를 주고받을 때는 속도가 빠른 '대칭키'를 사용하여 암호화합니다. 비대칭키의 '보안성'과 대칭키의 '속도'를 모두 취하기 위함입니다.
Q2. 공개키로 암호화하는 것과 개인키로 암호화하는 것의 차이는?
- 답변: 목적이 다릅니다.
- 공개키로 암호화: 오직 개인키 소유자만 열 수 있으므로 '기밀성(Confidentiality)'을 보장합니다. (편지 봉인)
- 개인키로 암호화: 누구나 공개키로 열어볼 수 있지만, 개인키 소유자가 작성했음을 보증하므로 '인증(Authentication)' 및 '무결성'을 보장합니다. 이를 전자서명이라고 합니다.
Q3. 양자 컴퓨터가 나오면 RSA는 뚫리나요?
- 답변: 네, 뚫립니다. 쇼어 알고리즘(Shor's Algorithm)을 사용하면 소인수분해를 순식간에 풀 수 있기 때문입니다. 그래서 현재 보안 업계는 양자 내성 암호(PQC)로의 전환을 서두르고 있습니다.