브라우저 주소창에 https://google.com을 입력하는 순간, 수많은 패킷이 인터넷을 가로질러 오간다. 그 경로 어딘가에서 누군가 패킷을 가로챈다면 어떻게 될까.
HTTPS는 이 문제를 해결하기 위해 존재한다. 그리고 HTTPS가 동작하려면 먼저 두 가지 암호화 방식을 이해해야 한다.
대칭키 암호화는 암호화와 복호화에 동일한 키 하나를 사용하는 방식이다. 자물쇠에 비유하면, 잠글 때도 열 때도 같은 열쇠를 쓰는 것과 같다.
[송신측] 평문 + 대칭키 → 암호문 전송
[수신측] 암호문 + 대칭키 → 평문 복원
같은 키로 암호화와 복호화를 처리하기 때문에 연산 구조가 단순하다. 그래서 속도가 빠르고 대용량 데이터 암호화에 적합하다. 단, 양쪽 모두 같은 키를 가지고 있어야 동작한다.
비대칭키 암호화는 공개키(Public Key) 와 비밀키(Private Key) 두 개의 키를 한 쌍으로 사용한다. 공개키로 암호화한 것은 비밀키로만 복호화할 수 있다.
[공개키] 누구에게나 공개해도 된다
[비밀키] 절대 외부로 노출하지 않는다
[송신측] 평문 + 수신측 공개키 → 암호문 전송
[수신측] 암호문 + 수신측 비밀키 → 평문 복원
공개키는 누가 가져가도 상관없다. 그 키로 암호화한 내용은 비밀키를 가진 사람만 풀 수 있기 때문이다. 비밀키는 서버 안에서만 존재하고 네트워크를 통해 절대 전송되지 않는다.
다만 대칭키 대비 연산 비용이 크다. 수백 배 느리기 때문에 대용량 데이터를 매번 비대칭키로 암호화하면 성능 문제가 생긴다.
브라우저와 서버 사이를 오가는 데이터를 암호화하려면 빠른 대칭키가 적합하다. 그런데 대칭키를 쓰려면 브라우저와 서버가 먼저 같은 키를 가지고 있어야 한다.
문제는 그 키를 어떻게 공유하느냐다. 인터넷을 통해 키를 보내면 중간에 탈취당할 수 있다.
graph LR
B[브라우저] -- "대칭키를 그냥 전송?" --> 공격자
공격자 -- "탈취 가능" --> S[서버]
여기서 비대칭키를 활용하면 이 문제를 해결할 수 있다. 대칭키 자체를 서버의 공개키로 암호화해서 전달하면, 서버의 비밀키를 가진 서버만 그 대칭키를 풀 수 있다.
sequenceDiagram
participant B as 브라우저
participant S as 서버
S->>B: 서버 공개키 전송 (탈취당해도 OK)
Note over B: 세션키(대칭키) 생성
B->>S: 세션키를 서버 공개키로 암호화해서 전송
Note over S: 서버 비밀키로 복호화 → 세션키 획득
Note over B,S: 이후 통신은 세션키(대칭키)로 암호화
비밀키는 서버 밖으로 나간 적이 없다. 네트워크 구간에서 탈취될 일이 없다.
결국 비대칭키는 대칭키를 안전하게 교환하는 데만 쓰고, 실제 데이터 통신은 빠른 대칭키로 처리한다. 두 방식의 장점만 취하는 방식이다.
비대칭키로 세션키 교환 문제는 해결됐다. 그런데 한 가지 문제가 남아있다.
서버가 공개키를 보내면서 "나 google.com이야"라고 주장한다고 해보자. 공격자도 똑같이 공개키를 만들어서 "나 google.com이야"라고 할 수 있다. 공개키 자체로는 신원을 증명하지 못한다.
여기서 CA(Certificate Authority, 인증 기관) 가 필요하다. DigiCert, Let's Encrypt 같은 공인된 제3자 기관이다.
서명(Signature)이라는 개념이 핵심이다. 암호화와 방향이 반대다.
암호화: 공개키로 잠그고 → 비밀키로 열어
서명: 비밀키로 서명하고 → 공개키로 검증
CA가 서버 인증서에 CA 비밀키로 서명을 남긴다. 그 서명은 CA 공개키로만 검증할 수 있다. 공격자가 가짜 인증서를 만들어도 CA 비밀키가 없으면 서명이 맞지 않아 검증에 실패한다.
sequenceDiagram
participant G as google.com 서버
participant CA as CA (DigiCert 등)
participant B as 브라우저
G->>CA: 내 공개키 인증해줘
Note over CA: google.com 신원 확인
CA->>G: CA 비밀키로 서명한 인증서 발급
B->>G: https 접속 요청
G->>B: 인증서 전송 (서버 공개키 + CA 서명 포함)
Note over B: 브라우저 내장 CA 공개키로 서명 검증
Note over B: 검증 성공 → 진짜 google.com
브라우저는 신뢰할 수 있는 CA 목록과 각 CA의 공개키를 설치 시점에 내장하고 있다. CA 서버에 실시간으로 접속할 필요 없이 로컬에서 검증이 완결된다. CA 공개키는 도메인 단위가 아니라 CA 기관 단위로 브라우저에 저장된다.
브라우저 내장 CA 목록
├── DigiCert 공개키
├── Let's Encrypt 공개키
├── GlobalSign 공개키
└── ...
지금까지 설명한 과정이 HTTPS 통신에서 실제로 어떻게 순서대로 동작하는지 보면 이렇다.
sequenceDiagram
participant B as 브라우저
participant S as google.com 서버
Note over B,S: 1단계 — TCP 3-way handshake
B->>S: SYN
S->>B: SYN-ACK
B->>S: ACK
Note over B,S: 2단계 — TLS 핸드셰이크
B->>S: Client Hello (지원하는 암호화 방식 목록)
S->>B: Server Hello + 인증서 전송 (서버 공개키 포함)
Note over B: CA 공개키로 인증서 서명 검증
Note over B: 세션키(대칭키) 생성
B->>S: 세션키를 서버 공개키로 암호화해서 전송
Note over S: 서버 비밀키로 복호화 → 세션키 획득
B->>S: Finished (세션키로 암호화)
S->>B: Finished (세션키로 암호화)
Note over B,S: 3단계 — 실제 데이터 통신
B->>S: HTTP 요청 (세션키로 암호화)
S->>B: HTTP 응답 (세션키로 암호화)
TCP 3-way handshake로 연결 가능 여부를 먼저 확인하고, 그 위에서 TLS 핸드셰이크가 진행된다. 인증서 검증과 세션키 교환이 끝나야 비로소 실제 데이터 통신이 시작된다. 우리가 매일 쓰는 HTTPS가 이 구조 위에서 동작한다.