2025년 3월 27일
이론
조회 : 42|2분 읽기
JAVA 통신에서 SSL
사설 SSL with JAVA
몇 시간 전, 아무 문제 없던 자바 애플리케이션에서 메일 서버와의 통신이 갑자기 실패하기 시작했습니다.
로그를 보니 SSL 관련 예외가 발생하고 있었고, 이전까지는 없던 이슈였기 때문에 원인을 파악하는 데 시간이 걸렸습니다.
로그를 보니 SSL 관련 예외가 발생하고 있었고, 이전까지는 없던 이슈였기 때문에 원인을 파악하는 데 시간이 걸렸습니다.
운영 환경에서 이런 SSL 이슈는 장애로 이어졌고 후에 인증서와 관련된 오류 처리를 빠르게 하기 위해 정리해봅니다.
✅ 상황 설명
해당 애플리케이션은 메일 서버와 HTTPS 통신을 하고 있었습니다.
그런데 몇 시간 전부터 메일 발송 및 상태 확인 요청이 전부 실패하기 시작했고 로그에는 다음과 같은 에러 메시지가 찍히고 있었습니다.
그런데 몇 시간 전부터 메일 발송 및 상태 확인 요청이 전부 실패하기 시작했고 로그에는 다음과 같은 에러 메시지가 찍히고 있었습니다.
bash1javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: 2PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: 3unable to find valid certification path to requested target
자바 환경에서 볼 수 있는 SSL 에러로, 서버가 제공한 인증서를 신뢰할 수 없을 때 발생하는 예외입니다.
🔍 원인 분석
처음엔 인증서 만료를 의심했지만, 메일 서버 인증서는 여전히 유효한 상태였습니다.
알고 보니 메일 서버 앞단에 새로운 보안 솔루션 장비(SSL 프록시) 가 추가되었고, 이 장비가 메일 서버의 앞단을 수행하고 있었습니다.
문제는 이 솔루션이 사용하는 인증서가 공인 루트 CA가 아닌, 해당 솔루션 자체에서 발급한 인증서였다는 점입니다.
JVM은 기본적으로 $JAVA_HOME/lib/security/cacerts 내 truststore에 등록된 인증서만을 신뢰하기 때문에 자바 애플리케이션은 이 인증서를 신뢰하지 않았고, SSL 핸드셰이크에 실패했던 것입니다.
🛠 해결 방법
1️⃣ 해당 도메인의 SSL 인증서 추출
우선 문제가 된 도메인에서 사용 중인 인증서를 직접 추출해야 했습니다.
다음 명령어로 서버가 제공하는 인증서를 저장할 수 있습니다:
다음 명령어로 서버가 제공하는 인증서를 저장할 수 있습니다:
bash1echo | openssl s_client -connect mail.server.domain:443 | openssl x509 -outform PEM > ssl-mail.crt
- mail.server.domain:443 < 실제 서버 도메인과 포트를 입력해야 합니다.
- 실행하면 ssl-mail.crt 파일이 생성되며, 바로 이 인증서를 등록해야 합니다.
2️⃣ 인증서를 JVM truststore에 등록
이제 인증서를 자바의 truststore(cacerts)에 등록합니다.
keytool을 사용해 다음과 같이 등록할 수 있습니다:
keytool을 사용해 다음과 같이 등록할 수 있습니다:
bash1sudo keytool -import -trustcacerts \ 2 -keystore $JAVA_HOME/lib/security/cacerts \ 3 -storepass changeit \ 4 -alias ssl-mail.crt \ 5 -file ssl-mail.crt
- 기본 비밀번호는 보통 changeit입니다.
- -alias는 등록된 인증서를 식별하기 위한 이름으로, 중복되지 않게 설정합니다.
- 등록 시 "신뢰하시겠습니까?"라는 질문이 나오면 yes 입력.
3️⃣ 등록 확인
등록이 정상적으로 되었는지 확인하려면 다음 명령어를 사용합니다:
bash1sudo keytool -list \ 2 -keystore $JAVA_HOME/lib/security/cacerts \ 3 -storepass changeit \ 4 -alias ssl-mail.crt
또는 전체 목록을 보고 싶다면:
bash1sudo keytool -list \ 2 -keystore $JAVA_HOME/lib/security/cacerts \ 3 -storepass changeit | grep ssl-mail
4️⃣ 애플리케이션 재시작
JVM은 truststore를 애플리케이션 시작 시점에 로딩하기 때문에,
등록 후에는 반드시 애플리케이션을 재시작해야 변경 사항이 적용됩니다.
등록 후에는 반드시 애플리케이션을 재시작해야 변경 사항이 적용됩니다.
bash1# 예: systemd 서비스인 경우 2sudo systemctl restart my-java-service 3 4# 단순 jar 파일이면 5Ctrl + C 로 종료 후 다시 실행
✅ 결과 확인
재시작 후 SSL 통신이 정상적으로 이루어지는 것을 확인했습니다.
이전까지 실패하던 메일 발송, 수신 API가 모두 정상 동작했고
더 이상 SSLHandshakeException이 발생하지 않았습니다.
이전까지 실패하던 메일 발송, 수신 API가 모두 정상 동작했고
더 이상 SSLHandshakeException이 발생하지 않았습니다.
🧼 운영 팁: 등록된 인증서 삭제 방법
일정 기간 후 더 이상 해당 인증서가 필요 없어졌거나, 테스트용으로 임시 등록한 경우 삭제가 필요할 수 있습니다.
bash1sudo keytool -delete \ 2 -alias ssl-mail.crt \ 3 -keystore $JAVA_HOME/lib/security/cacerts \ 4 -storepass changeit
삭제 후에도 역시 JVM이나 애플리케이션 재시작이 필요합니다.
🧠 마무리하며
이슈를 겪으면서 다음과 같은 점들을 다시 한 번 확인하게 되었습니다:
- Java는 truststore에 등록된 인증서만 신뢰합니다.
- 외부 시스템 구조(예: SSL 프록시, 보안 장비)가 변경되면 SSL 에러가 발생할 수 있습니다.
- 에러 메시지가 다소 난해할 수 있지만, PKIX path building failed → 인증서 체인 문제를 의미합니다.
- 인증서 등록 후에는 반드시 애플리케이션을 재시작해야 적용됩니다.
- 운영 환경에서는 인증서 등록/삭제 작업 시 신중하게 진행해야 합니다. 실수로 기존 공인 인증서를 덮어쓰는 일은 없어야 합니다.