1. 서론: 웹 서비스 보안과 HTTPS 필수화
웹 서비스에서 HTTPS(HTTP over TLS)는 더 이상 권장 사항이 아닌 절대적인 필수 요건입니다. Google Chrome 등 주요 웹 브라우저는 평문 HTTP 통신을 사용하는 모든 웹사이트에 대해 ‘주의 요함(Not Secure)’ 경고를 띄우며, 검색 엔진(SEO) 평가 시에도 HTTPS 적용 사이트에 명확한 가산점을 부여합니다.
무엇보다 평문 HTTP 통신은 중간자 공격(Man-in-the-Middle, MITM)이나 네트워크 스니핑을 통해 사용자의 비밀번호, 세션 토큰(JSESSIONID), 결제 정보가 고스란히 유출될 수 있는 치명적인 보안 취약점을 내포하고 있습니다.
이번 포스팅에서는 Tomcat WAS 환경에서 SSL/TLS 인증서(PKCS#12, JKS)를 설치하는 최신 표준 설정 방법, HTTP(80) 요청의 HTTPS(443) 강제 리다이렉트, HSTS 보안 헤더 강화, 그리고 실무 트러블슈팅 포인트까지 종합적으로 정리합니다.
2. 인증서 포맷 비교: JKS vs PKCS#12 (.p12)
Java 생태계에서 SSL 인증서를 관리할 때 사용되는 두 가지 핵심 키스토어 포맷입니다.
| 항목 | JKS (Java KeyStore) | PKCS#12 (.p12 / .pfx) |
|---|---|---|
| 표준성 | Java 독자 규격 (오라클 종속적) | RFC 7292 국제 표준 (범용 규격) |
| Java 버전 지원 | Java 8 이전 기본 포맷 | Java 9 이후 공식 표준(Default) |
| 타 도구 호환성 | keytool 외 C/Python/OpenSSL 등과 호환 난해 |
OpenSSL, IIS, Apache 등 모든 플랫폼 호환 |
| 보안 강도 | 독자 암호화 알고리즘 | 강력한 공개키 기반 암호화 표준 |
[!TIP] 최신 Tomcat 9, 10, 11 버전에서는 국제 표준이자 산업 표준인 PKCS#12 (.p12) 포맷을 사용할 것을 강력히 권장합니다.
Let’s Encrypt / OpenSSL (PEM) 인증서를 P12로 변환하기
상용 인증 기관이나 Let’s Encrypt에서 발급받은 PEM 파일(cert.pem, privkey.pem)은 openssl 명령어로 P12 키스토어로 묶어 Tomcat에 적용할 수 있습니다.
openssl pkcs12 -export \
-in fullchain.pem \
-inkey privkey.pem \
-out conf/keystore.p12 \
-name tomcat \
-CAfile chain.pem \
-caname root
# 키스토어 접근 비밀번호를 묻는 프롬프트에 안전한 패스워드를 입력합니다.
3. Tomcat server.xml HTTPS 커넥터 설정
Tomcat의 메인 설정 파일인 conf/server.xml을 열어 <Service name="Catalina"> 블록 내에 443 포트 커넥터를 추가합니다. 최신 Tomcat은 <SSLHostConfig>와 <Certificate> 태그를 계층적으로 구성하는 방식을 표준으로 채택하고 있습니다.
server.xml 상세 설정
<!-- HTTPS (SSL/TLS) 커넥터 설정 -->
<Connector port="443"
protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="200"
SSLEnabled="true"
scheme="https"
secure="true">
<SSLHostConfig protocols="TLSv1.2+TLSv1.3"
ciphers="TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_AES_128_GCM_SHA256,
TLS_AES_256_GCM_SHA384"
honorCipherOrder="true">
<Certificate certificateKeystoreFile="conf/keystore.p12"
certificateKeystorePassword="your_secure_password"
certificateKeystoreType="PKCS12"
type="RSA" />
</SSLHostConfig>
</Connector>
핵심 보안 파라미터 해설
protocols="TLSv1.2+TLSv1.3": POODLE, BEAST 등 심각한 취약점이 발견된 레거시 프로토콜(SSLv2, SSLv3, TLSv1.0, TLSv1.1)을 원천 차단하고 보안이 검증된 TLS 1.2 및 TLS 1.3만 활성화합니다.ciphers지정: Perfect Forward Secrecy(PFS)를 지원하는 타원곡선 기반 암호화 스위트(ECDHE)를 우선순위에 배치합니다.honorCipherOrder="true": 클라이언트가 제안한 암호화 스위트 대신 서버 측에서 지정한 안전한 암호화 알고리즘 우선순위를 따르도록 강제합니다.
4. HTTP(80) ➔ HTTPS(443) 자동 리다이렉트 구성
사용자가 브라우저 주소창에 http://myservice.com으로 접근하더라도 안전한 HTTPS 프로토콜로 자동 전환되도록 설정합니다.
1) server.xml의 HTTP 커넥터에 redirectPort 지정
conf/server.xml의 80번 포트 커넥터에서 redirectPort="443"으로 지정되어 있는지 확인합니다.
<Connector port="80"
protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="20000"
redirectPort="443" />
2) web.xml에 보안 제약 조건(Security Constraint) 추가
conf/web.xml (전역 적용) 또는 개별 애플리케이션의 WEB-INF/web.xml 하단에 다음 XML 블록을 추가합니다.
<security-constraint>
<web-resource-collection>
<web-resource-name>Force HTTPS Access</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<!-- CONFIDENTIAL: 암호화된 전송(HTTPS)만 허용하고 HTTP 요청 시 자동 302 리다이렉트 유도 -->
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
5. HSTS(HTTP Strict Transport Security) 보안 강화
리다이렉트만으로는 최초 접속 시 평문 HTTP 요청이 오가는 취약점 구간이 존재할 수 있습니다. 브라우저에게 “앞으로 1년간 무조건 HTTPS로만 접속하라"고 명령하는 HSTS 응답 헤더를 추가합니다.
conf/web.xml에 HttpHeaderSecurityFilter를 활성화합니다.
<filter>
<filter-name>httpHeaderSecurity</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
<init-param>
<param-name>hstsEnabled</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>hstsMaxAgeSeconds</param-name>
<param-value>31536000</param-value> <!-- 1년 -->
</init-param>
<init-param>
<param-name>hstsIncludeSubDomains</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>httpHeaderSecurity</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
6. 실무 아키텍처 비교: 단독 Tomcat vs Nginx SSL 오프로딩
실무 대규모 서비스에서는 Tomcat 자체에서 SSL 핸드셰이크를 처리하기보다 앞단에 Nginx나 로드 밸런서를 두는 SSL Termination (오프로딩) 패턴을 압도적으로 선호합니다.
subgraph PatternB ["패턴 B: Nginx SSL 오프로딩 (권장 아키텍처)"]
direction TB
C2[Client] == "HTTPS (Port 443)" ==> N2[Nginx / ALB: SSL 복호화]
N2 -- "HTTP (Port 8080 고속 내부망)" --> T2[Tomcat WAS: 순수 비즈니스 로직 전념]
end
- SSL 오프로딩의 장점: 무거운 비대칭키 암호화 연산을 Nginx(C언어 기반 고속 OpenSSL)에 위임하여 Java JVM의 CPU 연산 부담을 대폭 절감하고, 인증서 갱신 시 Tomcat 재기동 없이 Nginx 리로드(
nginx -s reload)만으로 무중단 갱신이 가능합니다.
7. 트러블슈팅 및 검증 체크리스트
- 리눅스 1024 이하 포트 바인딩 에러: 리눅스에서는 root 권한이 없는 일반 계정(
tomcat)이 443, 80 포트를 직접 열 수 없습니다.authbind유틸리티를 사용하거나 리버스 프록시(Nginx 443 ➔ Tomcat 8443) 포트 포워딩을 적용하세요. - 비밀번호 불일치 오류:
java.io.IOException: Keystore was tampered with, or password was incorrect오류 발생 시keytool -list -keystore keystore.p12명령어로 키스토어 암호가 일치하는지 먼저 검증하세요. - OpenSSL 핸드셰이크 검증:
반환값에
openssl s_client -connect localhost:443 -tls1_3Verify return code: 0 (ok)가 나오는지 확인하여 인증서 체인 누락 여부를 점검할 수 있습니다.
8. 결론
Tomcat의 네이티브 SSL/TLS 적용은 PKCS#12 표준 포맷과 최신 SSLHostConfig 문법을 이해하면 손쉽게 구축할 수 있습니다. 단독 WAS 환경이라면 본 가이드의 server.xml과 web.xml 보안 제약 설정을, 분산 프로덕션 환경이라면 앞단 로드 밸런서 기반의 SSL 오프로딩을 고려하여 최적의 보안 파이프라인을 구축해 보시기 바랍니다.