본문으로 건너뛰기
김신건의 로그

[Spring Boot] SSL/HTTPS 설정

· 수정 · 📖 약 2분 · 593자/단어 #spring-boot #ssl #tls #https #security
Spring Boot SSL, Spring Boot HTTPS, TLS, keystore, PEM Spring Boot, SSL Bundle, mutual TLS, mTLS spring

정의

Spring Boot 에서 HTTPS / TLS 설정 방법. JKS (legacy), PKCS12, PEM (modern), SSL Bundles (Spring Boot 3.1+) 네 가지 형식.

운영에선 보통 reverse proxy (nginx, ALB, Cloud LB) 가 TLS terminate 하지만, 직접 처리 시나리오:

  • localhost 개발 (HTTPS 강제 정책)
  • 내부 서비스 간 mTLS
  • 임베디드 배포 (proxy 없음)

가장 단순한 형식 (JKS / PKCS12)

server:
  port: 8443
  ssl:
    enabled: true
    key-store: classpath:keystore.p12
    key-store-password: ${KEYSTORE_PASSWORD}
    key-store-type: PKCS12          # 또는 JKS
    key-alias: tomcat
    key-password: ${KEY_PASSWORD}   # private key 별도 비번 (보통 keystore 와 동일)

keystore 생성 (개발용 self-signed):

keytool -genkeypair \
  -alias tomcat \
  -keyalg RSA \
  -keysize 2048 \
  -storetype PKCS12 \
  -keystore keystore.p12 \
  -validity 365 \
  -dname "CN=localhost, OU=Dev, O=MyCo, L=Seoul, S=KR, C=KR" \
  -storepass changeit

PEM 형식 (Spring Boot 3.1+)

OpenSSL / Let’s Encrypt 친화적:

server:
  port: 8443
  ssl:
    enabled: true
    certificate: classpath:cert.pem
    certificate-private-key: classpath:key.pem
    trust-certificate: classpath:ca.pem       # mTLS 시

PEM 파일이 가장 modern. 운영 환경에서 자동 갱신 (cert-manager 등) 과 자연스럽게.

SSL Bundles (Spring Boot 3.1+, 권장)

여러 SSL 설정을 묶어 재사용:

spring:
  ssl:
    bundle:
      pem:
        web-server:
          keystore:
            certificate: classpath:cert.pem
            private-key: classpath:key.pem
          truststore:
            certificate: classpath:ca.pem
      jks:
        legacy-client:
          keystore:
            location: classpath:legacy.jks
            password: ${LEGACY_PASSWORD}
            type: JKS

server:
  ssl:
    bundle: web-server          # 위 bundle 참조

장점:

  • 같은 cert 를 여러 컴포넌트가 공유 (server + WebClient + Kafka client)
  • hot reload 지원 (cert 만료 / 교체 자동 감지)
  • 환경별 bundle 분리

WebClient / RestClient 에서 사용

@Bean
RestClient.Builder restClientBuilder(SslBundles bundles) {
    SslBundle bundle = bundles.getBundle("backend-mtls");
    return RestClient.builder()
        .requestFactory(new SslBundleClientHttpRequestFactory(bundle));
}

mTLS 환경에서 클라이언트 cert 자동 첨부.

HTTPS 강제 (HTTP → HTTPS redirect)

Spring Boot 는 기본 단일 connector. 두 connector 동시 (8080 + 8443):

@Bean
public ServletWebServerFactory servletContainer() {
    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
        @Override
        protected void postProcessContext(Context context) {
            SecurityConstraint securityConstraint = new SecurityConstraint();
            securityConstraint.setUserConstraint("CONFIDENTIAL");
            SecurityCollection collection = new SecurityCollection();
            collection.addPattern("/*");
            securityConstraint.addCollection(collection);
            context.addConstraint(securityConstraint);
        }
    };
    tomcat.addAdditionalTomcatConnectors(redirectConnector());
    return tomcat;
}

private Connector redirectConnector() {
    Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
    connector.setScheme("http");
    connector.setPort(8080);
    connector.setSecure(false);
    connector.setRedirectPort(8443);
    return connector;
}

Spring Security 로 더 깔끔:

@Bean
SecurityFilterChain http(HttpSecurity http) throws Exception {
    return http
        .requiresChannel(c -> c.anyRequest().requiresSecure())
        .build();
}

HSTS (Strict-Transport-Security)

@Bean
SecurityFilterChain http(HttpSecurity http) throws Exception {
    return http
        .headers(h -> h
            .httpStrictTransportSecurity(hsts -> hsts
                .includeSubDomains(true)
                .maxAgeInSeconds(31536000)))
        .build();
}

브라우저가 HTTPS 만 사용하도록 강제. 한 번 받으면 1년 (max-age) 동안 캐싱.

⚠️ HSTS 가 설정되면 사용자가 cert 문제로 HTTP 로 강제 전환 못 함. cert 만료 / DNS 변경 시 위험.

mutual TLS (mTLS)

서버 + 클라이언트 양쪽 cert 검증:

server:
  ssl:
    enabled: true
    bundle: server-mtls
    client-auth: need        # need (필수), want (요청은 함), none (비활성)

need 면 클라이언트 cert 없으면 connection 거부. 내부 서비스 간 인증에 사용.

내부 통신 = JWT 보다 mTLS 가 더 강력 (네트워크 레벨 인증).

SNI (Server Name Indication)

하나의 IP / port 에 여러 cert (도메인별):

server:
  ssl:
    server-name-bundles:
      - server-name: "api.example.com"
        bundle: api-cert
      - server-name: "admin.example.com"
        bundle: admin-cert

Spring Boot 3.2+ 지원. multi-tenant 또는 multi-domain.

Let’s Encrypt 자동화

운영에선 cert-manager (K8s), Caddy, certbot 같은 도구가 cert 발급 + 갱신.

K8s 패턴:

# Ingress 에 cert-manager annotation
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
    - hosts: [api.example.com]
      secretName: api-tls-secret
  rules: [...]

Spring 은 plain HTTP 만 받고 Ingress 가 TLS terminate. 가장 흔한 운영 패턴.

디버깅

# cert 유효성
openssl x509 -in cert.pem -text -noout

# keystore 내용
keytool -list -keystore keystore.p12 -storetype PKCS12

# 서버 cert 검증
openssl s_client -connect api.example.com:443 -showcerts

# Spring 로그
logging:
  level:
    org.springframework.boot.web.embedded: DEBUG
    org.apache.tomcat.util.net.SSLUtilBase: DEBUG

함정과 베스트 프랙티스

  • production: reverse proxy 가 TLS terminate: Spring 은 plain HTTP. cert 관리 분리.
  • 개발: self-signed + localhost CN: 브라우저 경고 무시 (chrome --ignore-certificate-errors)
  • PKCS12 > JKS: PKCS12 가 표준, JKS 는 Java legacy
  • PEM + SSL Bundles (Spring Boot 3.1+): 가장 modern
  • HSTS 는 신중하게: 한 번 활성화 후 cert 문제 시 사용자 lock-out
  • mTLS 는 내부 서비스 간: 외부 노출 API 에는 OAuth2 가 적합
  • cert 갱신 자동화 필수: 수동 = 잊음 → outage
  • Spring Boot 의 SSL 변경 hot reload: SSL Bundles 사용 시 가능 (재시작 불필요)

관련 위키

이 글의 용어 (4개)
[Spring Boot] Embedded Web Server 설정과 튜닝spring
정의 Spring Boot 의 starter 가 내장 서버를 함께 번들. 별도 WAR 배포 / 외부 Tomcat 불필요. 기본 매핑: - → Tomcat (기본) - → Nett…
[Spring Security] OAuth2 Authorization Code Flow + PKCEspring
정의 가 OAuth2 Login (소셜 로그인) 의 개요를 다룬다면, 이 페이지는 표준 Authorization Code Grant 의 전체 흐름과 PKCE, refresh to…
[Spring] RestClient, WebClient, RestTemplatespring
정의 Spring HTTP 클라이언트 3종: - RestTemplate: 동기, 전통 (5.0+ maintenance) - WebClient: reactive, async/syn…
[Spring] Security: Filter Chain, JWT, OAuth2spring
정의 Spring Security는 인증·인가의 사실상 표준. 서블릿 필터 체인 위에 다양한 인증/권한 처리를 모듈식으로. 6.0+ 부터 Lambda DSL 표준, Bean 방식…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기