최근 회사에서 해양에서 Application간의 데이터 교환 표준인 SECOM을 적용하는 과정에서
필수적인 요소인 server - client간의 mutual TLS에 대해 공부한 내용과 적용을 위한 KeyStore, TrustStore의 세팅등을 정리해보려고 합니다.
네트워크 기본 개념 정리
mutualTLS에 대해 학습하기전 기본적인 TCP, HTTP, HTTPS 및 포트에 대한 기본 개념을 정리하고자 한다.
1. OSI 7 Layer 관점에서의 통신 구조

네트워크 통신은 OSI 7 Layer 모델을 기반으로 계층적으로 동작한다.
- Application Layer(L7): HTTP, WebSocket과 같은 애플리케이션 프로토콜이 동작하는 계층
- Transport Layer(L4): TCP/UDP와 같이 프로세스 간 데이터 전달을 담당하는 계층
- Network Layer(L3): IP 기반 라우팅 및 목적지 호스트 전달을 담당하는 계층
- Data Link Layer(L2): 동일 네트워크 내 MAC 기반 전달 및 APR 동작 계층
즉, HTTP/WebSocket과 같은 프로토콜은 TCP 연결 위에서 동작한다.
2. TCP란 무엇인가?
TCP(Transmission Control Protocol)는 Transport Layer(L4)에서 동작하며,
서버와 클라이언트 간 신뢰성 있는 연결을 제공하는 프로토콜이다.
그리고 이 연결은 지속성을 갖는 세션이다.
TCP는 다음 기능을 제공한다.
- 연결 지향 통신(Connection-oriented)
- 데이터 순서 보장 및 손실 시 재전송
- 흐름 제어(Window 기반)
즉, TCP는 연결을 만들고, 빠짐없이 순서대로, 유실되면 재전송 하는 방식의 연결을 수립한다.
또한 TCP 통신에서는 각 세그먼트에 대해 Seq/Ack 번호를 기반으로 송수신 상태를 관리한다.
반면 TCP와 대조되는 UDP는 빠르다는 장점이 있지만, 데이터 유실/순서 꼬임을 고려하지 않는다.
3. TCP 3-way Handshake
TCP 기반 통신은 데이터를 송수신하기 전에 먼저 연결을 수립해야 한다. 이 과정이 TCP 3-way handshake이다.
TCP 3-way Handshake는 아래와 같은 과정을 일컬으며
- Client -> Server: SYN(연결 요청)
- Server -> Client: SYN, ACK(요청 수락)
- Client -> Server: ACK(연결 확립)
HTTP 통신에서 언급되는 "3-way handshake"는 HTTP 자체가 아닌,
HTTP 요청을 보내기 위한 TCP 연결 수립과정을 의미한다.
4. Port의 기본 개념 및 운영 환경
IP 주소는 통신 대상 호스트를 식별하고, Port 번호는 해당 호스트 내에서 통신 대상 프로세스를 식별한다.
즉, IP는 "어느 서비스인가?", Port는 "그 서버 안에서 어느 어플리케이션인가"를 의미한다.
일반적으로 프로토콜이 사용하는 기본 포트는 관례적으로 정해진 값이며, 아래와 같다.
HTTP 기본 포트: 80
HTTPS 기본 포트: 443
URL에 포트가 명시되지 않으면 클라이언트는 기본 포트를 자동으로 사용한다.
단, 서비스는 반드시 기본 포트에서만 동작해야 하는것은 아니며, 나와 같은 Java 백엔드 개발자들이 많이 사용하는
SpringBoot는 8080 포트를 기본값으로 하는 HTTP 서버로 동작한다.
운영 서비스에서는 URL에 포트가 명시되지 않는 경우가 대부분이다.
예를들어 백엔드 개발자가 8080 포트로 어플리케이션을 배포하였지만 클라이언트는 포트번호를 명시하지 않고도
해당 서비스에 요청을 보낼 수 있는 이유는 외부에서는 기본 포트(443)로 요청을 받고
Reverse Proxy 또는 Load Balancer가 내부 서비스 포트(8080)로 전달하기 때문이다.
5. HTTP와 HTTPS의 차이
HTTP는 평문 기반의 어플리케이션 프로토콜로 요청/응답 구조를 가진다.
HTTPS는 HTTP 프로토콜 상위에서 TLS 암호화를 구현한 것으로 HTTPS를 사용하는 웹 사이트는 TLS 암호화를 이용한다.
즉 HTTPS = HTTP + TLS를 의미한다.
TLS는 OSI 7 Layer중 5~6 계층 사이, 좀 더 정확하게는 4계층(Transport Layer)위에서,
7계층(Application Layer)아래에서 동작하는 보안 프로토콜이다.
즉, 일반적으로 HTTPS = HTTP + TLS라고 하지만 구조적으로 봤을때는 아래와 같다.
- TLS 연결이 먼저 수립되면
- 그 안에 HTTP를 넣어서 보내는것

따라서 HTTPS 통신 과정을 정리하면 다음과 같다.
- TCP 3-way handshake(Transport Layer, L4)
- TLS handshake(보안 연결 수립)
- TLS 연결 완료
- HTTP request/response(Application Layer, L7)
6. WebSocket 통신의 계층 구조
WebSocket은 초기 연결 시 HTTP Upgrade handshake를 수행한 뒤,
동일한 TCP 연결을 유지하면서 프레임 기반 양방향 통신을 수행한다.
WebSocket 연결 흐름은 아래와 같다.
- TCP 3-way handshake
- HTTP Upgrade 요청
- WebSocket Frame 송수신
- Close Frame 이후 TCP FIN/ACK 종료
TLS(v 1.2, v 1.3) Handshake
Client와 Server가 보안 연결을 맺기 위한 절차인 TLS Handshake 과정에 대해 조금 더 자세히 다뤄보려고 한다.
현재 주로 사용되는 TLS 1.2와 최신 표준인 TLS 1.3은 진행방식에 약간의 차이가 있다.
TLS 핸드셰이크는 TLS 암호화를 사용하는 통신 세션을 실행하는 프로세스입니다.
TLS 핸드셰이크 중에, 통신하는 양측에서는 메시지를 교환하여 서로를 인식하고 서로를 검증하며
사용할 암호화 알고리즘을 구성하고 세션 키에 합의한다.
TLS 핸드셰이크는 HTTPS 작동 원리의 근간을 이룬다.
TLS 핸드셰이크 과정 중에는 클라이언트와 서버가 함께 다음을 수행합니다.
- 암호화 키 교환
- 서버 인증(필요시 클라이언트 인증 포함)
- 암호화 방식(cipher_suite) 협상
- 세션 키 생성(이후 해당 키로 데이터를 암호화하여 송/수신 함)
Process
아래 다이어그램은 조금 더 상세한 TLS handshake 과정을 나타낸다.

- 'client hello' 메시지: 클라이언트가 서버로 '헬로' 메시지를 전송하면서 핸드셰이크를 시작한다.
이 메시지에는 아래 항목이 포함된다.
- 지원하는 TLS 버전
- cipher_suite 목록(ex. AES, RSA, ECDHE 등)
- client_random(무작위 바이트 문자열) - 'server hello' 메시지: 'cient hello' 메시지에 대한 응답으로 서버가 아래의 메시지를 전송한다.
- 자신의 인증서
- 서버에서 선택한 cipher_suite
- server_random(서버에서 생성한 무작위 바이트 문자열) - 서버 인증서 전송: 서버의 SSL/TLS 인증서(보통 X.509)를 클라이언트에게 전송한다
- 서버 인증서 검증: 클라이언트가 서버의 TLS/SSL 인증서를 인증서 발행 기관을 통해 검증한다.
이를 통해 서버가 인증서에 명시된 서버인지, 그리고 클라이언트가 상호작용 중인 서버가 실제 해당 도메인의 소유자인지를 검증한다. - 예비 마스터 암호 전송: 클라이언가 pre-master secret(예비 마스터 암호)라고 하는 무작위 바이트 문자열을 전송한다.
pre-master-secret은 공개키로 암호화 되어 있으며, 서버가 개인키로만 해독할 수 있다. - 예비 마스터 암호 해독: 서버가 자신의 개인키를 이용해 예비 마스터 암호를 해독한다.
- 클라이언트, 서버 세션 키 생성: 클라이언트와 서버가 모두 client_random, server_random, 예비 마스터 암호를 이용해
세션 키를 생성한다. 이는 모두 같은 결과가 나와야 한다. - 클라이언트 준비 완료: 클라이언트가 세션 키로 암호화된 '완료' 메시지를 서버에 전송한다.
- 서버 준비 완료: 서버가 세션키로 암호화된 '완료' 메시지를 전송한다.
- 안전한 대칭 암호화 성공: 핸드셰이크가 완료되고, 세션 키를 이용해 암호화된 통신이 계속 진행된다.
TLS 1.3
TLS 1.3은 핸드셰이크가 더 빠르게 단축되어 1RTT(한 번의 왕복)만에 가능하도록 한다.
TLS 1.2와 비교하여 주요한 차이점은 아래와 같다.
- RSA 기반 ClientKeyExchange 폐기 → 모두 (EC)DH 기반 키 교환 사용
- cipher_suite 협상 + 인증서 전달 + 키 교환 + 인증까지 서버가 한 번에 응답
- 중간 단계(HelloDone, ChangeCipherSpec 등) 생략됨

- 'client hello' 전송: 클라이언트는 client hello 메시지를 보낸다.
안전하지 않은 cipher_suite(RSA 등) 지원이 1.3 버전에서는 제거되었으므로, 사용 가능한 암호 모음의 수가 크게 줄었다.
client hello에는 pre-master-secret을 계산하는데 사용되는 매개 변수도 포함되어 있다.
기본적으로 클라이언트는 서버가 선호난 키 교환 방법을 알고 있다고 가정한다.
이렇게 하여 핸드셰이크의 전체 과정이 줄어들었다. - 마스터 암호 생성: 이 시점에서 서버는 client_random, client_parameters, cipher_group을 받았으므로 서버는 server_random을 자체적으로 생성할 수 있으므로 이미 server_random을 보유하고 있다.
따라서 서버는 master_secret을 생성할 수 있다. - 'server hello' 전송 및 완료: server hello에는 서버의 인증서, digital signature, server_random,
선택한 cipher_group이 포함된다.
서버는 이미 master-secret을 가지고 있으므로 완료됨 메시지도 전송한다. - 'client hello' 완료: 클라이언트가 서명 및 인증서를 확인하고 master-secret을 생성한 후 완료됨 메시지를 서버에게 전송한다.
- 안전한 대칭 암호화 성공: 핸드셰이크가 완료되고, 세션 키를 이용해 암호화된 통신이 계속 진행된다.
mTLS 1.3
mTLS는 TLS의 확장형으로 서버뿐만 아니라 클라이언트 또한 인증서를 사용해 인증받는 양방향 인증 방식이다.
일반적인 TLS보다 널리 쓰이지는 않지만 서버와 클라이언트의 신원이 양방향으로 인증된 보안이 중요한
비교적 폐쇄적인 환경에서 유용하게 활용할 수 있다.
Client - Server 뿐만 아니라 Server - Server간의 양방향 인증을 위해 사용가능하다.
TLS 1.3을 기준으로 mTLS 옵션을 적용하면 다음과 같은 절차를 통해 서버와 클라이언트는 양방향 인증을 진행한다.

- ClientHello: 클라이언트는 다음과 같은 정보를 전송한다.
- TLS 버전
- cipher_suite 목록
- key_share(ECDHE 공개키) 등 - ServerHello: 서버는 다음과 같은 정보를 전송하고 서버와 클라이언트는 ECDHE를 통해 shared secret을 생성한다.
- 선택된 cipher_suite
- key_share(서버의 ECDHE 공개키) - EncryptionExtension: ALPN, SNI등의 확장된 옵션을 암호화된 채널로 보낸다
- CertificateRequest: 서버가 클라이언트에게 인증서를 요청(허용하는 CA 목록, 요구하는 인증서 속성 포함)
- Certificate, CertificateVerify: 인증서 및 디지털 서명
- Finished: 핸드셰이크 종료
mTLS vs TLS
위에서 잠깐 언급했듯이 mTLS는 일반 TLS와는 다르게 양방향 인증을 요구한다.
클라이언트가 서버를 신뢰할 뿐 아니라 서버도 클라이언트의 신원을 확인해야하는 폐쇄적인 환경에서 자주 사용된다.
그래서 mTLS는 일반 공개 웹 환경이 아닌 내부 폐쇄망에서 자주 사용된다.
일반적인 공개 웹 환경의 인증서는 Let's Encrypt, DigiCert, GlobalSign등의 공인(CA)의 서명을 통해 브라우저와 운영체제에 내장된 루트 인증서 체계에 의해 신뢰를 확보하지만 mTLS에서 사용되는 인증서는 서버와 클라이언트의 합의가 있다면 공인 인증이 필수적이지 않다.
| 항목 | 일반 TLS(HTTPS) | mTLS(mutual TLS) |
| 사용 환경 | 공개 인터넷 환경(ex. 웹사이트, Open API) | 폐쇄된 내부 네트워크 |
| 인증 방향 | 단방향(서버 -> 클라이언트) | 양방향(서버 <-> 클라이언트) |
| 서버 인증서 | 공인 CA에서 발급(Let's Encrypt, DigiCert 등) | 내부 CA 또는 자체 서명 인증서도 사용 가능 |
| 클라이언트 인증서 | 사용하지 않음(ID/PW, 토큰 기반 인증 등으로 대체) | 필수(클라이언트 -> 서버 인증 필요) |
| 루트 인증서 필요 여부 | 브라우저/OS가 신뢰하는 루트 CA 필요 | 서버, 클라이언트가 직접 신뢰할 CA 지정 가능 (공인 루트 CA 없이 가능) |
| 신뢰 방식 | 루트 → 중간 CA → 서버 인증서(공개 인증 체계) | 신뢰하는 CA 또는 클라이언트 인증서를 직접 TrustStore에 등록 |
| 배포 및 관리 | 공인 인증서 자동 발급/갱신 가능 (ex. Certbot) | 자체 배포, 수동 갱신 또는 사설 CA 관리 필요 |
| 보안 강도 | 일반 사용자 보호에 충분 | 기계간 인증 보안 수준 강화에 적합 |
Keystore & Truststore
mTLS 또는 일반적인 TLS를 사용하기 위해서는 Web Application Keystore와 Truststore의 세팅이 필요하다.
기본적으로 Java에서는 TLS 통신시 기본적으로 사용할 Keystore, Truststore가 이미 JDK 내부에 설정되어 있거나, 시스템 프로퍼티로 지정할 수 있도록 되어 있다.
| 항목 | Keystore | Truststore |
| 기본값 | 없음(null, 비어있음) | JDK에 내장된 cacerts 파일 사용 (루트 CA 목록 포함됨) |
| 용도 | 자신의 신원을 증명하기 위한 개인 키, 인증서 저장 | 상대방의 인증서 또는 CA 인증서를 저장하여 검증 |
| 포함 내용 | 개인 키 (Private Key) + 인증서 체인 (자신의 공개키 인증서) |
신뢰할 수 있는 공개키 인증서 또는 CA 인증서 |
| 사용 시점 | 자신이 서버 또는 클라이언트로서 인증을 받아야 할 때 (ex. HTTPS 서버 인증, mTLS 클라이언트 인증) |
상대방을 검증해야 할 때(ex. 서버가 클라이언트를 검증, 클라이언트가 서버를 검증) |
| 파일 확장자 | .jks ,.p12, .pem 등 | jks ,.p12, .pem 등 |
| 비밀번호 필요 여부 | 필요 (개인키 보호) | 필요 (스토어 접근 보호) |
정리된 Keystore와 Truststore의 기본 개념을 이해했다면 TLS, mTLS handshake를 통해 Web Application과 통신하는데에 큰 어려움은 없다. 이미 모든것이 구현되어있고 Keystore, Truststore의 세팅및 application.properites또는, application.yml 파일을 통해 TLS 옵션을 설정할 수 있다.
따라서 정리해보면 일반적인 TLS handshaking을 위해 서버는 자신의 인증서(공인 CA에서 발급된)를 Keystore에 넣으면 되고
mTLS 통신을 위해서 각 클라이언트와 서버는 서로의 인증서 혹은, 서로의 인증서의 상위 인증서를 Truststore에 넣으면 mTLS handshaking이 가능하다.
'개발' 카테고리의 다른 글
| WireShark로 Application간 네트워킹 분석하기 (0) | 2025.10.24 |
|---|---|
| [Kubernetes] Ingress, Ingress Controller, Load Balancer 흐름 정리 (0) | 2025.09.17 |
| 도메인 기반 디렉토리 구조로 변경하기 (0) | 2024.10.02 |
| WebBrowser의 실행 과정 (0) | 2023.11.15 |
| 표준적인 FirebaseFirestore 데이터 저장 방식 정리 (0) | 2023.07.24 |