이해하기 쉬운 TLS
초기 웹 통신에서는 제3자가 통신 내용을 엿볼 수 있는 보안 문제가 존재했습니다. 이를 해결하기 위해 1990년대 중반, 넷스케이프(Netscape)는 SSL(Secure Sockets Layer) 프로토콜을 개발했습니다. SSL은 클라이언트와 서버 간의 데이터를 암호화하여 외부의 엿보기를 방지하며, 데이터 암호화, 무결성 검증, 양측의 신원 인증을 통해 보안을 강화했습니다. 이로 인해 인터넷 뱅킹과 전자상거래에서 안전한 데이터 전송이 가능해졌습니다.
하지만 시간이 지남에 따라 SSL의 보안 취약점이 드러났습니다. 이를 해결하기 위해 1999년에 SSL 3.0의 후속으로 TLS(Transport Layer Security) 1.0이 개발되었습니다. TLS는 SSL의 후계자로서 더 강력한 보안 기능을 제공하며, SSL의 취약점을 보완하도록 설계되었습니다. 오늘날 TLS는 SSL과 호환되며 대부분의 현대 시스템에서 널리 사용되고 있습니다.
TLS는 두 가지 주요 단계로 구성됩니다. 첫 번째는 핸드셰이크 (Handshake) 단계로, 클라이언트와 서버가 프로토콜 버전과 암호화 키를 교환하고 서로의 신원을 확인합니다. 두 번째는 암호화된 데이터 전송 (Encrypted Data Transmission) 단계로, 애플리케이션 데이터가 암호화되어 안전하게 통신이 이루어집니다.
핸드셰이크 (Handshake)
TLS 핸드셰이크는 클라이언트와 서버 간의 안전한 통신을 설정하기 위한 과정으로, 세션의 암호화 매개변수를 설정하고 TLS 레코드 계층을 통해 안전한 연결을 수립하는 역할을 합니다.
Client Server
ClientHello -------->
ServerHello
Certificate*
ServerKeyExchange*
CertificateRequest*
<-------- ServerHelloDone
Certificate*
ClientKeyExchange
CertificateVerify*
[ChangeCipherSpec]
Finished -------->
[ChangeCipherSpec]
<-------- Finished
Application Data <-------> Application Data
ClientHello
TLS 핸드셰이크는 클라이언트가 ClientHello
메시지를 서버에 전송하면서 시작됩니다:
- TLS 프로토콜 버전: 클라이언트가 지원하는 TLS 버전, 예를 들어 TLS 1.2 또는 TLS 1.3입니다.
- 암호화 스위트: 클라이언트가 지원하는 암호화 알고리즘의 목록, 예를 들어
ECDHE-RSA-AES256-GCM-SHA384
입니다. - 압축 방법: 데이터 압축 방식을 명시합니다. TLS 1.3에서는 압축이 비활성화됩니다.
- 랜덤 값: 클라이언트가 생성한 임의 데이터로, 핸드셰이크의 보안성을 높이는 데 사용됩니다.
암호화 스위트의 조합은 세션의 보안성을 결정하는 중요한 요소입니다:
- ECDHE (Elliptic Curve Diffie-Hellman Ephemeral): 대칭 키를 안전하게 교환하는 알고리즘으로, 각 세션에서 새로운 키를 생성해 보안을 강화합니다.
- RSA: 인증서 서명과 키 교환에 사용되는 비대칭 암호화 알고리즘입니다.
- AES_256_GCM: 256비트 키를 사용하는 대칭 암호화 알고리즘으로, GCM 모드를 통해 데이터의 기밀성을 보장합니다.
- SHA384: 메시지 무결성을 확인하기 위한 해시 함수입니다.
특히 ECDHE 알고리즘은 Diffie-Hellman 알고리즘을 기반으로 타원 곡선 암호를 적용하여 매 세션마다 새로운 키를 생성하고 보안을 강화합니다. 이 방식은 비밀 정보를 직접 공유하지 않고도 동일한 공유 비밀 키를 생성할 수 있도록 합니다.
- 공개 파라미터 설정: 클라이언트와 서버는 공개적으로 사용할 타원 곡선을 선택합니다. 이 곡선의 방정식은 공개되지만, 곡선 자체는 안전하게 선택되어야 합니다.
- 비밀 키 생성: 클라이언트와 서버는 각각 개인 비밀 키를 생성합니다. 이 비밀 키는 각자만 알고 있는 값입니다.
- 공개 키 생성 및 전송: 클라이언트와 서버는 각자의 개인 비밀 키를 기반으로 공개 키를 생성하고, 이를 상대방에게 전송합니다. 공개 키는 다른 사람과 공유될 수 있지만, 비밀 키는 절대 공유되지 않습니다.
- 공유 비밀 키 생성: 클라이언트는 서버의 공개 키와 자신의 개인 비밀 키를 사용하여 공유 비밀 키를 계산합니다. 서버는 클라이언트의 공개 키와 자신의 개인 비밀 키를 사용하여 동일한 공유 비밀 키를 계산합니다. 이렇게 두 당사자는 서로 다른 방식으로 동일한 공유 비밀 키를 생성할 수 있습니다.
- 암호화 및 통신: 생성된 공유 비밀 키를 사용하여 암호화된 통신을 진행합니다. 이 키는 암호화와 복호화 과정에서 사용되며, 두 당사자만 알고 있는 비밀 값이 됩니다.
또한 ClientHello
메시지에는 몇 가지 확장 기능이 포함될 수 있습니다:
- SNI (Server Name Indication): 클라이언트가 요청하는 서버의 도메인 이름을 포함하여 서버가 적절한 인증서를 선택할 수 있게 합니다. 이는 여러 도메인을 하나의 IP 주소에서 호스팅할 때 유용합니다.
- ALPN (Application Layer Protocol Negotiation): TLS 핸드셰이크 중 클라이언트와 서버가 사용하려는 애플리케이션 계층 프로토콜을 협상할 수 있게 해줍니다. 클라이언트가 지원하는 프로토콜 목록을 제공하면, 서버는 적합한 프로토콜을 선택하여 응답합니다.
ServerHello
서버는 클라이언트의 ClientHello
메시지에 대한 응답으로 ServerHello
메시지를 전송합니다. 이 메시지에는 다음 정보가 포함됩니다:
- 협상된 TLS 프로토콜 버전: 서버가 지원하는 가장 높은 버전의 TLS입니다.
- 선택된 암호화 스위트: 클라이언트가 제안한 목록 중 서버가 선택한 암호화 알고리즘 조합입니다.
- 압축 방법: 서버가 사용할 데이터 압축 방식입니다.
- 서버 랜덤 값: 서버가 생성한 랜덤 데이터로, 핸드셰이크 과정에서 보안성을 높이는 데 사용됩니다.
이 단계에서 서버는 선택된 암호화 매개변수를 설정하고, 이후 클라이언트와의 안전한 데이터 전송을 준비합니다.
Certificate
서버는 Certificate
메시지를 통해 클라이언트에게 자신을 증명하는 인증서를 전송합니다. 이 인증서는 X.509을 따르며, 서버의 공개 키와 신원 정보를 포함합니다. 이를 통해 클라이언트는 서버가 신뢰할 수 있는지 검증할 수 있습니다. 인증서는 신뢰할 수 있는 인증 기관(CA)에 의해 서명되어 그 진위성과 무결성을 보장합니다.
Certificate
- Version: 인증서의 버전
- Serial Number: CA가 할당한 고유 번호
- Algorithm Identifier: 서명에 사용된 알고리즘 식별자
- Signature: 인증서의 디지털 서명
- Issuer: 인증서 발급자(CA)
- Validity: 인증서의 유효 기간
- Not Before: 유효 기간 시작 날짜
- Not After: 유효 기간 종료 날짜
- Subject: 인증서 소유자
- Subject Public Key Info: 공개 키 정보
- Public Key Algorithm: 사용된 공개 키 알고리즘
- Subject Public Key: 소유자의 공개 키
- Issuer Unique Identifier (Optional): 발급자의 고유 식별자
- Subject Unique Identifier (Optional): 소유자의 고유 식별자
- Extensions (Optional): 확장 정보 (예: SAN, Key Usage 등)
Certificate Signature Algorithm: 서명에 사용된 알고리즘
Certificate Signature: 서명 데이터
인증 기관(CA)은 서버 인증서를 발급할 때 인증서 내용에 디지털 서명을 추가하여 이를 보장합니다. 이 서명은 PKCS#1 v1.5 패딩과 같은 알고리즘을 사용하여 데이터를 암호화합니다. 인증서에는 도메인 이름이 Subject 필드에 명시되며, SAN (Subject Alternative Name) 확장을 통해 여러 도메인을 인증할 수 있습니다.
인증 체계는 계층적으로 구성되며, 가장 상위에는 Root CA가 있습니다. Root CA는 자기 서명(self-signed)된 인증서를 보유하고 있으며, 그 하위에 위치한 Intermediate CA에게 인증서를 발급합니다. 이로 인해 신뢰의 연쇄(trust chain)가 형성되며, 최종적으로 서버 또는 클라이언트에 도달하게 됩니다.
- Root CA: 신뢰할 수 있는 루트 인증서로, 브라우저나 운영체제의 신뢰 저장소에 저장됩니다.
- Intermediate CA: 루트 CA에서 발급받은 인증서로, 여러 개의 Intermediate CA가 존재할 수 있습니다. 이를 통해 관리 및 보안성을 높입니다.
- End Entity Certificate: 최종 사용자에게 발급된 인증서로, 웹사이트 서버와 같은 최종 인증서입니다.
클라이언트는 인증서 검증을 통해 서버의 신뢰성을 확인합니다:
- 인증서 체인 확인: 클라이언트는 서버가 제공한 인증서를 검사하여, 그 인증서가 신뢰할 수 있는 CA에 의해 서명되었는지 확인합니다. 이는 각 인증서의
Issuer
필드와Signature
필드를 통해 검증됩니다. - 유효성 검사: 클라이언트는 인증서의 유효 기간과 취소 목록(CRL)을 확인하여 해당 인증서가 여전히 유효한지 검증합니다. 이 과정에서 인증서의
Not Before
와Not After
날짜를 비교하여 현재 날짜가 그 범위에 포함되는지 확인합니다. - 도메인 이름 확인: 클라이언트는 인증서의 도메인 이름과 실제 연결된 서버의 도메인 이름이 일치하는지 확인하여 신원을 검증합니다.
ServerKeyExchange
서버가 익명 Diffie-Hellman 또는 Ephemeral Diffie-Hellman 알고리즘을 사용할 경우, 클라이언트와 서버는 ServerKeyExchange
메시지를 통해 추가 정보를 교환해야 합니다. 이 메시지는 공유 비밀 키 생성을 위한 추가 파라미터를 포함합니다. 서버는 이 단계에서 자신이 생성한 DH 파라미터를 클라이언트에게 전달합니다. 이로써 클라이언트는 서버의 공개 키를 사용하여 공유 비밀 키를 생성할 수 있게 됩니다.
CertificateRequest
서버가 클라이언트 인증을 요구할 경우 CertificateRequest
메시지를 전송합니다. 이 메시지는 클라이언트에게 인증서를 요구하는 내용을 포함하며, 지원되는 인증서의 종류를 명시합니다.
ServerHelloDone
서버가 모든 초기 메시지를 전송한 후, ServerHelloDone
메시지를 전송하여 클라이언트에게 서버의 응답이 끝났음을 알립니다. 이 단계에서 클라이언트는 서버의 설정에 따라 암호화된 연결을 수립할 준비를 합니다.
ClientKeyExchange
클라이언트는 ClientKeyExchange
메시지를 전송하여 공유 비밀 키를 생성합니다. 이 메시지는 클라이언트가 생성한 비밀 키인 프리마스터 시크릿(Pre-Master Secret)이 포함됩니다. 이 키는 서버의 공개 키를 사용하여 암호화된 후 서버에 전달됩니다. 서버는 자신의 개인 키로 이 값을 복호화하여 공유 비밀 키를 생성합니다.
CertificateVerify
클라이언트가 인증서를 제공한 경우, 클라이언트는 CertificateVerify
메시지를 전송하여 서버에 자신의 신원을 증명합니다. 이 메시지는 클라이언트의 개인 키로 서명되어, 서버는 이를 통해 클라이언트의 인증서를 검증합니다.
ChangeCipherSpec
클라이언트와 서버는 ChangeCipherSpec
메시지를 전송하여 이후의 메시지가 암호화되어 전송될 것임을 알립니다. 이 메시지는 암호화 매개변수의 변경을 통지하며, 핸드셰이크의 마지막 단계로, 이제부터 암호화된 데이터 전송이 시작됩니다.
Finished
Finished
메시지는 핸드셰이크 프로세스가 완료되었음을 알립니다. 이 메시지에는 핸드셰이크 과정에서 설정된 암호화 매개변수에 기반한 검증 정보가 포함되어, 상대방이 올바르게 핸드셰이크를 수행했는지 확인합니다. 클라이언트와 서버는 서로에게 Finished
메시지를 전송하여 핸드셰이크를 마무리합니다.
암호화된 데이터 전송 (Encrypted Data Transmission)
TLS 핸드셰이크가 완료되면 클라이언트와 서버는 암호화된 데이터를 주고받을 수 있습니다. 이후 통신에서 사용되는 대칭 키는 핸드셰이크 과정에서 합의된 암호화 스위트에 따라 결정됩니다.
TLS 레코드 계층
TLS 프로토콜은 애플리케이션 계층 데이터를 안전하게 전송하기 위해 레코드 계층을 사용합니다. 이 계층은 데이터를 암호화하고 무결성을 확인하며, 데이터를 작은 블록으로 분할하여 전송합니다. 각 TLS 레코드에는 다음과 같은 정보가 포함됩니다:
- 콘텐츠 타입(Content Type): 전송되는 데이터의 유형 (예:
handshake
,application data
,alert
). - 버전(Protocol Version): TLS 버전 (예: TLS 1.2, TLS 1.3).
- 길이(Length): TLS 레코드의 길이.
- 페이로드(Payload): 암호화된 애플리케이션 데이터를 포함.
TLS는 페이로드를 암호화하고, HMAC (keyed-hash message authentication code, hash-based message authentication code)를 사용하여 데이터의 무결성을 검증합니다. HMAC는 비밀 키와 해시 함수를 결합하여 메시지의 진위성을 보장하며, 이를 통해 데이터가 중간에 조작되거나 변조되는 것을 방지합니다.
재개
한 번 TLS 핸드셰이크가 완료되면 클라이언트와 서버는 기존 세션을 재개할 수 있습니다. 이를 통해 핸드셰이크 과정을 다시 수행하지 않고, 이전에 합의한 암호화 매개변수를 사용하여 빠르게 연결을 설정할 수 있습니다. TLS 세션 재개는 세션 ID 또는 세션 티켓을 사용하여 이루어집니다:
- 세션 ID: 서버가 클라이언트에 제공한 세션 ID를 사용하여 클라이언트가 세션을 재개합니다.
- 세션 티켓: TLS 1.3에서 사용되는 방법으로, 서버가 클라이언트에 암호화된 티켓을 제공하고, 클라이언트는 이를 사용하여 세션을 재개합니다.
댓글남기기