본문으로 건너뛰기

안동민 개발노트

본문 시작

TLS 핸드셰이크

TLS 1.2와 1.3이 암호 조합·서버 신원·트래픽 키를 합의하는 흐름과 인증서 체인 검증을 추적합니다.

HTTPS 연결은 TCP 연결 위에서 바로 HTTP를 보내지 않습니다.

먼저 TLS 핸드셰이크로 “어떤 버전과 암호 조합을 쓸지”, “서버가 그 서비스 이름에 맞는 인증서를 제시했는지”, “이번 연결에만 쓸 트래픽 키를 어떻게 만들지”를 합의합니다.


TLS 핸드셰이크가 정하는 것

HTTP를 보내기 전에 버전, 신원, 트래픽 키를 합의한다

TLS 핸드셰이크는 사용할 암호 조합, 서버 인증서의 신뢰성, 이번 연결에만 쓸 트래픽 키를 같은 transcript 안에서 확정합니다.

  1. ordered steps
    핸드셰이크 진행

    ordered steps 1 ClientHello 지원 버전, cipher suite, SNI, ALPN, 난수와 key_share를 보낸다. 2 ServerHello + Certificate 선택한 규칙, 서버 인증서, 서버 key_share와 검증 메시지를 돌려준다. 3 Verify + Finished 이름, 체인, 서명, 기간을 확인하고 transcript 검증을 끝낸다. 4 Encrypted data HTTP 요청과 응답은 파생된 트래픽 키로 보호된 record에 실린다.

  2. ClientHello

    지원 버전, cipher suite, SNI, ALPN, 난수와 key_share를 보낸다.

  3. ServerHello + Certificate

    선택한 규칙, 서버 인증서, 서버 key_share와 검증 메시지를 돌려준다.

  4. Verify + Finished

    름, 체인, 서명, 기간을 확인하고 transcript 검증을 끝낸다.

  5. Encrypted data

    HTTP 요청과 응답은 파생된 트래픽 키로 보호된 record에 실린다.

  6. checkpoints
    핵심 감각

    checkpoints 순서 핸드셰이크가 끝나기 전에는 보호된 HTTP 데이터가 열리지 않는다. 검증 중간자가 메시지를 바꾸면 Finished transcript 검증에서 드러난다. 분리 공개키 기술은 인증과 키 합의에, 대칭키는 실제 데이터 보호에 쓰인다.

  7. 순서

    핸드셰이크가 끝나기 전에는 보호된 HTTP 데이터가 열리지 않는다.

  8. 검증

    중간자가 메시지를 바꾸면 Finished transcript 검증에서 드러난다.

  9. 분리

    공개키 기술은 인증과 키 합의에, 대칭키는 실제 데이터 보호에 쓰인다.

결과무엇을 정하나왜 필요한가
협상 결과TLS 버전, AEAD, 해시, ALPN양쪽이 같은 규칙으로 record를 읽기 위해
서버 신원SAN 이름, 체인, 서명, 유효기간중간자가 서버인 척하지 못하게 하기 위해
트래픽 키ECDHE 공유 비밀에서 파생한 송수신 키이번 연결 데이터만 보호하기 위해

핸드셰이크의 결과는 크게 세 가지입니다.

첫째, 클라이언트와 서버가 TLS 버전과 암호 조합을 고릅니다.

둘째, 인증서와 서명으로 서버 신원을 확인합니다.

셋째, ECDHE 같은 임시 키 교환으로 공유 비밀을 만들고, 거기서 이번 연결의 여러 트래픽 키를 파생합니다.


TLS 1.2 전체 핸드셰이크

TLS 1.2 full handshake는 여러 왕복으로 키와 인증을 만든다

ClientHello부터 Finished까지 메시지 flight가 쌓이면서 RTT 비용이 발생한다.

  1. ClientHello

    지원 목록 제시

  2. Server flight

    선택, 인증서, key exchange

  3. Client key

    pre-master 또는 ECDHE 기여

  4. ChangeCipherSpec

    암호화 전환

  5. Finished

    handshake 검증

일반적인 TLS 1.2 full handshake는 TLS만 보면 2 RTT가 필요합니다.

TCP 3-way handshake까지 합치면, HTTP 요청을 안전하게 보내기까지 보통 3 RTT가 걸립니다.

단, 세션 재개, False Start, TCP Fast Open 같은 최적화가 있으면 실제 지연은 달라질 수 있습니다.

구성 요소TLS 1.2에서의 위치TLS 1.3에서의 변화
버전 협상ClientHello, ServerHelloClientHello의 supported_versions 확장으로 협상
키 교환RSA key transport, DHE, ECDHE 등이 가능일반 full handshake는 (EC)DHE key_share, 재개는 PSK 또는 PSK+(EC)DHE
인증인증서와 ServerKeyExchange 서명 등CertificateVerify로 핸드셰이크 transcript에 서명
데이터 보호CBC, GCM 등 여러 방식AEAD 기반 cipher suite만 사용

Cipher Suite 읽기

TLS 1.2 이름은 길고 TLS 1.3 이름은 역할이 줄었다

TLS 1.2 cipher suite는 키 교환, 인증, 데이터 보호, 해시를 한 줄에 담습니다. TLS 1.3은 AEAD와 해시 중심으로 짧아지고, 키 교환과 인증은 확장과 인증서 알고리즘에서 따로 봅니다.

  1. compare
    이름 해석

    compare TLS 1.2 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ECDHE는 임시 키 교환, RSA는 서버 인증 서명, AES_128_GCM은 record 보호, SHA256은 PRF와 Finished 검증을 뜻한다. TLS 1.3 TLS_AES_128_GCM_SHA256 AES_128_GCM과 SHA256만 이름에 남고 key_share와 Certificate 계열 메시지에서 나머지를 협상한다.

  2. TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256

    TLS 1.2 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ECDHE는 임시 키 교환, RSA는 서버 인증 서명, AES_128_GCM은 record 보호, SHA256은 PRF와 Finished 검증을 뜻한다.

  3. TLS_AES_128_GCM_SHA256

    TLS 1.3 TLS_AES_128_GCM_SHA256 AES_128_GCM과 SHA256만 이름에 남고 key_share와 Certificate 계열 메시지에서 나머지를 협상한다.

  4. checkpoints
    오해 방지

    checkpoints TLS 1.2 이름만 봐도 핸드셰이크 재료가 많이 보인다. TLS 1.3 cipher suite 이름만으로 인증 방식까지 알 수 없다. 실무 버전, 확장, 인증서 알고리즘을 같이 읽어야 정확하다.

  5. TLS 1.2

    름만 봐도 핸드셰이크 재료가 많이 보인다.

  6. TLS 1.3

    cipher suite 이름만으로 인증 방식까지 알 수 없다.

  7. 실무

    버전, 확장, 인증서 알고리즘을 같이 읽어야 정확하다.

항목TLS 1.2TLS 1.3
키 교환cipher suite 이름의 ECDHE/RSA 등ClientHello key_share 확장
인증이름의 RSA/ECDSA와 인증서Certificate와 CertificateVerify
데이터 보호AES_GCM, CBC 등 선택 가능AEAD cipher suite만 사용
해시PRF와 Finished 검증HKDF와 transcript hash

TLS 1.2의 cipher suite 이름은 키 교환, 인증, 대칭 암호, 해시가 한 줄에 들어 있습니다.

예를 들어 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256은 ECDHE로 공유 비밀을 만들고, RSA 인증서 키로 서버 서명을 검증하며, AES-128-GCM으로 레코드를 보호하고, SHA-256을 키 유도와 Finished 검증에 사용한다는 뜻입니다.

TLS 1.3의 cipher suite 이름은 더 짧습니다.

TLS_AES_128_GCM_SHA256처럼 데이터 보호 알고리즘과 해시만 담고, 키 교환과 인증 방식은 별도 확장과 인증서 알고리즘으로 협상합니다.


디피-헬만 키 교환

임시 비밀값은 로컬에 두고 공개값만 네트워크에 보낸다

ECDHE는 클라이언트와 서버가 각자의 임시 비밀값을 숨긴 채 공개값을 교환하고, 같은 공유 비밀을 계산하는 키 교환 방식입니다.

  1. compare
    양쪽 계산

    compare Client secret a, public g^a 서버의 공개값 g^b와 자신의 비밀값 a로 g^(ab)를 계산한다. Server secret b, public g^b 클라이언트의 공개값 g^a와 자신의 비밀값 b로 같은 g^(ab)를 계산한다.

  2. secret a, public g^a

    Client secret a, public g^a 서버의 공개값 g^b와 자신의 비밀값 a로 g^(ab)를 계산한다.

  3. secret b, public g^b

    Server secret b, public g^b 클라이언트의 공개값 g^a와 자신의 비밀값 b로 같은 g^(ab)를 계산한다.

  4. checkpoints
    TLS가 추가로 하는 일

    checkpoints 인증 순수 DH만 쓰면 중간자가 공개값을 바꿀 수 있어 인증서 서명이 필요하다. 전방 비밀성 임시 비밀값을 폐기하면 장기 키 유출만으로 과거 세션을 풀기 어렵다. 키 파생 공유 비밀은 그대로 쓰지 않고 transcript와 난수를 섞어 여러 키로 파생한다.

  5. 인증

    순수 DH만 쓰면 중간자가 공개값을 바꿀 수 있어 인증서 서명이 필요하다.

  6. 전방 비밀성

    임시 비밀값을 폐기하면 장기 키 유출만으로 과거 세션을 풀기 어렵다.

  7. 키 파생

    공유 비밀은 그대로 쓰지 않고 transcript와 난수를 섞어 여러 키로 파생한다.

구분예시의미
공개 정보기준점, g^a, g^b도청자가 볼 수 있지만 이것만으로 공유 비밀을 계산하기 어렵다
비밀 정보a, b각자 로컬에만 두고 네트워크에 보내지 않는다
공유 비밀g^(ab)핸드셰이크 키와 트래픽 키 파생의 입력이 된다

ECDHE(Elliptic Curve Diffie-Hellman Ephemeral)는 현재 TLS에서 가장 중요한 키 교환 방식입니다.

공개 채널에는 기준점과 공개값만 오가고, 각자의 임시 비밀값은 네트워크에 나오지 않습니다.

양쪽은 상대의 공개값과 자신의 비밀값을 조합해 같은 공유 비밀에 도달합니다.

다만 순수한 디피-헬만만으로는 상대가 누구인지 인증하지 못하므로, TLS는 인증서와 서명으로 공개값이 진짜 서버 흐름에 속했는지 확인합니다.

여기서 E(Ephemeral)는 매 연결마다 새 임시 키 쌍을 만든다는 뜻입니다.

서버의 장기 개인키가 나중에 유출되어도, 과거 연결의 임시 비밀값이 안전하게 폐기되었고 세션 키 로그나 티켓 키 유출 같은 별도 사고가 없다면 녹화된 과거 트래픽을 다시 풀기 어렵습니다.

이 성질을 전방 비밀성(Forward Secrecy)이라고 합니다.


TLS 1.3의 개선점

TLS 1.3은 첫 왕복 안에 키 교환을 끝낸다

ClientHello 에 key_share 를 미리 넣어 서버의 첫 응답 뒤부터 핸드셰이크 대부분을 암호화한다.

  1. 1-RTT

    전체 핸드셰이크는 보통 한 번의 왕복으로 완료

  2. 단순한 cipher suite

    AEAD와 해시 중심, 키 교환·인증은 확장으로 협상

  3. 낡은 선택 제거

    정적 RSA/DH와 재협상을 제거해 위험한 분기 축소

TLS 1.3은 2018년에 표준화되었고, TLS 1.2보다 핸드셰이크를 짧고 단단하게 만들었습니다.

클라이언트가 ClientHello에 (EC)DHE key_share를 미리 싣기 때문에, 일반적인 full handshake에서는 서버 응답을 한 번 받은 뒤 바로 암호화 상태로 들어갈 수 있습니다.

변경 사항TLS 1.2TLS 1.3
Full handshake보통 2 RTT보통 1 RTT
0-RTT없음재개 연결에서 일부 early data 가능
정적 RSA/DH가능하지만 권장되지 않음제거
핸드셰이크 암호화많은 메시지가 평문ServerHello 이후 메시지 암호화
Cipher suite키 교환·인증까지 이름에 포함AEAD와 해시 중심
재협상존재제거

0-RTT는 “재접속을 더 빨리 시작할 수 있는 기능”이지, 기본으로 켜 둘 만능 최적화가 아닙니다.

0-RTT early data는 replay 위험이 있고 1-RTT 데이터보다 보안 성질이 약하므로, 멱등 요청처럼 재전송되어도 안전한 작업에만 제한적으로 써야 합니다.

tls_check.sh
# SNI를 명시해 TLS 1.3 연결이 되는지 확인
openssl s_client -connect example.com:443 -servername example.com -tls1_3 </dev/null 2>/dev/null | grep "Protocol"

# 협상된 cipher suite 확인
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | grep "Cipher"

# leaf 인증서의 주체와 유효기간 확인
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

# 서버가 보내는 인증서 체인 확인
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null \
  | grep -E "s:|i:"

인증서와 서버 신원

TLS 핸드셰이크에서 서버가 보내는 X.509 인증서는 “이 공개키는 이 서비스 이름에 묶여 있고, 신뢰할 수 있는 CA가 그 사실을 서명했다”는 주장입니다.

서버 신뢰는 이름, 기간, 용도, 체인을 모두 통과해야 한다

인증서는 단독 파일이 아니라 루트 CA까지 이어지는 검증 경로와 현재 요청 이름의 일치가 함께 필요하다.

  1. Root CA
    신뢰 저장소

    Root CA 신뢰 저장소 브라우저/OS가 이미 신뢰

  2. Intermediate
    중간 CA

    Intermediate 중간 CA 서버 인증서를 서명

  3. Leaf
    서버 인증서

    Leaf 서버 인증서 도메인과 공개키 포함

  4. Request
    현재 hostname

    Request 현재 hostname SAN과 일치해야 함

브라우저는 단순히 “인증서가 있다”만 보지 않습니다.

접속한 DNS 이름이 인증서의 subjectAltName(dNSName)에 있는지, 유효기간 안인지, 용도와 확장이 맞는지, 그리고 인증서 체인이 신뢰 저장소의 trust anchor까지 이어지는지를 함께 확인합니다.

현대 검증에서는 도메인 이름을 Common Name(CN)에 기대기보다 SAN을 기준으로 봅니다.


인증서 체인 검증

서버 인증서는 leaf에서 root trust anchor까지 이어져야 한다

서버는 leaf와 intermediate를 보내고, 브라우저는 로컬 신뢰 저장소의 root까지 서명 사슬이 이어지는지 확인한다.

  1. leaf
    example.com 인증서

    leaf SAN에 접속 이름이 있고, 서버 공개키와 용도가 들어 있다.

  2. intermediate CA
    실제 발급을 담당

    intermediate CA 루트 대신 서버 인증서를 서명하며, 사고 시 교체 가능한 경계가 된다.

  3. root CA
    브라우저/OS의 trust anchor

    root CA 대개 서버가 보내지 않고 클라이언트 신뢰 저장소에 이미 들어 있다.

  4. root는 오프라인에 가깝게
    root

    오프라인에 가깝게 루트 개인키가 흔들리면 전체 신뢰 체계가 무너져서 직접 발급에 쓰지 않는다.

  5. intermediate는 교체 가능
    intermediate

    교체 가능 발급 업무를 맡기고 문제가 생기면 해당 중간 CA를 폐기하거나 교체한다.

  6. leaf만으로는 부족
    leaf만으로

    부족 서버 인증서 하나만 맞아 보여도 신뢰 사슬과 이름 검사가 함께 통과해야 한다.

서버는 보통 leaf 인증서와 intermediate CA 인증서를 보냅니다.

루트 CA 인증서는 대개 서버가 보내는 대상이 아니라, 운영체제나 브라우저의 신뢰 저장소에 들어 있는 trust anchor입니다.

루트 CA가 모든 서버 인증서를 직접 서명하지 않는 이유는 운영과 사고 격리 때문입니다.

루트 개인키는 오프라인에 가깝게 보호하고, 실제 발급 업무는 중간 CA가 맡습니다.

중간 CA에 문제가 생기면 해당 중간 CA를 폐기하거나 교체할 수 있지만, 루트 CA가 유출되면 그 루트에 기대는 신뢰 체계 전체가 흔들립니다.

다음 절에서는 실무에서 인증서를 발급하고 갱신하는 방법을 살펴보겠습니다.