안녕하세요! 서버 관리나 자바(Java) 기반 애플리케이션 개발을 하다 보면 한 번쯤 마주치게 되는 당혹스러운 오류가 있습니다. 바로 javax.net.ssl.SSLHandshakeException입니다. 웹 브라우저에서는 잘 접속되는 사이트가 유독 내가 만든 프로그램이나 서버에서만 연결되지 않을 때, 이 오류는 개발자의 밤을 지새우게 만들기도 하죠.
오늘은 SSL/TLS 통신의 핵심인 핸드쉐이크 과정에서 왜 오류가 발생하는지, 그리고 실무에서 즉시 적용할 수 있는 단계별 해결책을 전문가의 시선에서 상세히 정리해 드리겠습니다.
1. SSLHandshakeException이란 무엇인가? 발생 원인 분석
SSLHandshakeException은 클라이언트(내 프로그램)와 서버가 안전한 통신 세션을 수립하기 위한 '악수(Handshake)' 단계에서 협상이 결렬되었음을 의미합니다. HTTPS 통신이 이루어지려면 양측이 암호화 방식, 인증서 유효성 등을 확인해야 하는데, 이 과정에서 신뢰 관계를 구축하지 못하면 예외가 발생합니다.
주요 발생 원인은 다음과 같습니다.
- 인증서 신뢰 문제: 서버의 SSL 인증서가 공인된 기관(CA)에서 발급되지 않았거나, 클라이언트의 TrustStore에 등록되지 않은 경우.
- 프로토콜 버전 불일치: 서버는 TLS 1.2 이상을 요구하는데 클라이언트는 구버전인 TLS 1.0을 사용하는 경우.
- 암호화 알고리즘(Cipher Suite) 불일치: 양측이 지원하는 암호화 방식이 서로 겹치지 않을 때.
- 시스템 시간 오류: 클라이언트나 서버의 시간이 현재 시간과 크게 달라 인증서 유효 기간을 벗어난 것으로 판단될 때.
2. 단계별 해결 방법: 인증서 추가 및 환경 설정
가장 빈번하게 발생하는 'Untrusted Certificate' 문제를 해결하는 정석적인 방법은 서버의 인증서를 자바 실행 환경(JRE/JDK)의 신뢰 목록에 수동으로 등록하는 것입니다.
Step 1: 서버 인증서 추출하기
먼저 대상 서버의 인증서를 파일(.cer 또는 .crt)로 내려받아야 합니다. 브라우저(크롬 기준) 주소창의 자물쇠 아이콘을 클릭하여 '인증서 정보' -> '세부 정보' -> '파일로 복사'를 통해 내보낼 수 있습니다.
Step 2: keytool을 이용한 TrustStore 등록
자바에 기본 포함된 keytool 명령어를 사용하여 추출한 인증서를 cacerts 파일에 추가합니다. 터미널(또는 CMD)을 관리자 권한으로 실행한 후 아래 명령어를 입력하세요.
keytool -import -alias "myserver" -keystore "C:\Program Files\Java\jdk-11\lib\security\cacerts" -file "server_cert.cer"
참고로, 자바 cacerts의 기본 비밀번호는 changeit입니다. 등록 후 애플리케이션을 재시작하면 더 이상 핸드쉐이크 오류가 발생하지 않습니다.
3. 자바 버전 및 TLS 프로토콜 호환성 문제 해결
오래된 자바 버전(Java 7 이하)을 사용 중이라면 최신 웹사이트의 TLS 1.2 또는 1.3 보안 요구사항을 충족하지 못할 수 있습니다.
TLS 버전 명시적 지정
애플리케이션 실행 시 JVM 옵션을 통해 특정 TLS 버전을 사용하도록 강제할 수 있습니다. 최신 표준인 TLS 1.2 사용을 권장합니다.
-Dhttps.protocols=TLSv1.2
Unlimited Strength Jurisdiction Policy 설치
구버전 자바에서는 강력한 암호화 알고리즘 사용에 제한이 걸려 있는 경우가 있습니다. 이 경우 오라클 홈페이지에서 'JCE Unlimited Strength Jurisdiction Policy' 파일을 다운로드하여 lib/security 폴더에 덮어씌워야 합니다. (Java 8u161 버전 이후로는 기본 활성화되어 있으므로 업데이트를 권장합니다.)
4. 개발 환경에서의 임시 방편: SSL 검증 우회 (주의 요망)
로컬 개발 환경이나 내부망 통신 시, 인증서 문제를 즉시 해결하기 어려운 경우 코드를 통해 SSL 검증을 강제로 통과시키는 방법이 있습니다. 단, 이는 보안상 매우 취약하므로 실제 운영 환경(Production)에서는 절대 사용해서는 안 됩니다.
// 모든 인증서를 신뢰하도록 TrustManager 설정 (자바 예시)
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; }
public void checkClientTrusted(X509Certificate[] certs, String authType) { }
public void checkServerTrusted(X509Certificate[] certs, String authType) { }
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
위 코드는 보안 연결의 본질인 '상대방 확인' 과정을 생략하므로, 중간자 공격(MITM)에 노출될 위험이 큽니다. 가급적 앞서 설명한 '인증서 등록' 방식을 우선적으로 검토하시기 바랍니다.
핵심 요약: SSLHandshakeException 해결 체크리스트
| 원인 구분 | 해결책 |
|---|---|
| 인증서 미등록 | 서버 인증서를 Java cacerts에 import |
| TLS 버전 불일치 | JVM 옵션에 TLSv1.2 설정 추가 |
| 시간 동기화 오류 | 서버 및 로컬 시스템 시간 최신화 |
| SNI 미지원 | 자바 버전 업그레이드 (Java 7u2 이상) |
'오류 > 안드로이드' 카테고리의 다른 글
| Read timed out 오류 해결 방법 [안드로이드 스튜디오] (0) | 2026.02.20 |
|---|---|
| MultiDex keep file missing 오류 해결 방법 [안드로이드 스튜디오] (0) | 2026.02.18 |
| Read timed out 오류 해결 방법 [안드로이드 스튜디오] (0) | 2026.01.30 |
| Execution failed for D8 오류 해결 방법 [안드로이드 스튜디오] (0) | 2026.01.29 |
| Could not find method compile() 오류 해결 방법 [안드로이드 스튜디오] (0) | 2026.01.28 |