HTTPS 연결이 느리거나 SSL certificate problem이 뜰 때, 무슨 일이 벌어지는지 모르면 어디를 고칠지도 모릅니다. TLS 핸드셰이크는 평문 통신을 암호화 채널로 바꾸는 협상 절차입니다. 단계를 알면 어느 단계에서 막혔는지 메시지로 읽어낼 수 있습니다.
핸드셰이크가 하는 일
TCP 연결(3-way handshake)이 끝난 뒤, 본격적인 데이터를 주고받기 전에 클라이언트와 서버는 세 가지를 합의합니다: 어떤 암호 방식을 쓸지, 서버가 진짜인지(인증서), 이 세션에서 쓸 대칭키. 이게 끝나야 비로소 암호화된 HTTP 요청이 흐릅니다.
단계별 흐름
- ClientHello — 클라이언트가 지원하는 TLS 버전·암호 스위트(cipher suite) 목록과 랜덤값을 보냅니다.
- ServerHello — 서버가 그중 하나를 고르고, 자신의 랜덤값을 보냅니다.
- Certificate — 서버가 인증서 체인을 보냅니다. 클라이언트는 신뢰할 수 있는 CA가 서명했는지, 만료되지 않았는지, 이름이 맞는지 검증합니다.
- 키 교환 — 양쪽 랜덤값과 (ECDHE 등) 키 교환으로 동일한 세션 키를 만들어냅니다. 이후 통신은 이 대칭키로 암호화됩니다.
- Finished — 양쪽이 협상이 변조되지 않았음을 확인하고 암호화 채널이 열립니다.
TLS 1.3에서는 왕복(RTT)이 줄어 이 과정이 한 번의 왕복으로 끝나, 1.2보다 연결이 빠릅니다.
openssl로 직접 보기
로컬 터미널
openssl s_client -connect example.com:443 -servername example.com
-servername은 SNI(같은 IP에 여러 도메인이 있을 때 어느 인증서를 줄지 알려주는 확장)를 지정합니다. 이걸 빼면 엉뚱한 기본 인증서가 와서 이름 불일치가 날 수 있습니다.
출력에서 핵심은 이 줄들입니다.
OUTPUT
Protocol : TLSv1.3 ← 협상된 버전
Cipher : TLS_AES_256_GCM_SHA384 ← 선택된 암호 스위트
Verify return code: 0 (ok) ← 인증서 검증 결과
Verify return code가 0이 아니면 인증서 문제입니다. 만료(certificate has expired), 체인 누락(unable to get local issuer certificate), 이름 불일치가 대표적입니다.
증상별 점검
| 증상 | 의미 | 점검 |
|---|---|---|
| 연결이 느림 | 핸드셰이크 왕복 과다 | TLS 1.3·세션 재사용 확인 |
| certificate has expired | 인증서 만료 | 갱신·자동 갱신(certbot) |
| unable to get local issuer | 중간 인증서 누락 | 서버에 풀체인 설치 |
| 이름 불일치 | SNI·인증서 도메인 불일치 | -servername·SAN 확인 |
점검 순서
로컬 터미널
openssl s_client -connect <host>:443 -servername <host>
# Protocol·Cipher로 협상 결과 확인
# Verify return code로 인증서 검증 확인
TLS 핸드셰이크와 인증서 검증을 직접 끊어보고 진단하는 실습은 네트워크 트랙에서 회원가입 없이 무료로 익힐 수 있습니다.