HOWTO · Java
javax.net.ssl.SSLHandshakeException 해결 방법: 핸드셰이크 중 원격 호스트가 연결을 닫음
Java SSL/TLS 핸드셰이크가 원격 호스트에 의해 닫힌 이유를 진단한 뒤, 프로토콜, 인증서, 클라이언트, 네트워크 또는 서버 측 증거를 바탕으로 해결책을 선택하고 검증하세요.
이 페이지의 내용
javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake는 상대편이 연결을 닫아 Java의 SSL/TLS 핸드셰이크가 완료되지 않았다는 뜻입니다. 이 메시지는 실패가 발생한 지점을 알려 주지만, 그 자체로 원인까지 밝혀 주지는 않습니다. 프로토콜이나 암호 스위트 호환성, 인증서 또는 신뢰 문제, 클라이언트 설정, 네트워크 중간 장비, 서버 정책 등이 원인일 수 있습니다.
예외가 의미하는 것
SSL/TLS 핸드셰이크 중 클라이언트와 서버는 애플리케이션 데이터를 주고받기 전에 사용할 프로토콜과 암호 스위트를 협상하고 서버의 신원을 확인합니다. 이 과정에서 상대편이 연결을 닫으면 Java는 SSLHandshakeException을 보고합니다. 상대편은 접속하려던 서버일 수도 있고 프록시, 로드 밸런서 또는 다른 네트워크 장비일 수도 있습니다. 따라서 오류 문구만으로 애플리케이션 서버가 직접 연결을 닫았다고 단정할 수 없습니다.
이 예외는 핸드셰이크가 실패했다는 증거이지, 인증서가 만료되었거나 Java가 너무 오래되었다는 증거는 아닙니다. 먼저 정확한 엔드포인트, Java 런타임, 클라이언트 라이브러리를 확인하세요. 명령줄에서 확인한 Java 버전과 IDE, 애플리케이션 서버, 컨테이너 또는 서비스가 실제로 사용하는 런타임은 다를 수 있습니다.
원격 호스트가 연결을 닫은 이유 진단하기
관찰한 사실을 바탕으로 원인을 좁혀 가세요.
- 호스트 이름, 포트, 실패 시각, 프로세스가 실제로 사용하는 Java 런타임, HTTP 또는 기타 클라이언트 라이브러리를 기록하세요. 엔드포인트와 포트가 정확한지 확인하고, 애플리케이션과 같은 경로를 통해 서비스에 연결할 수 있는지 확인하세요.
- 클라이언트와 서버가 지원하는 TLS 프로토콜 및 암호 스위트를 비교하세요. 서버나 로드 밸런서의 설정과 클라이언트 라이브러리에 실제 적용된 설정을 확인하세요. 양쪽에서 공통으로 사용할 수 있는 선택지가 없을 때 프로토콜 또는 암호 스위트 불일치를 의심할 수 있습니다.
- 해당 호스트 이름으로 제공된 서버 인증서를 살펴보세요. 유효 기간, 호스트 이름 일치 여부, 인증서 체인을 확인하세요. 그런 다음 Java 프로세스가 설정된 트러스트스토어를 통해 발급자 체인을 신뢰하는지 확인하세요. 인증서를 살펴보는 일과 트러스트스토어를 변경하는 일은 별개의 작업입니다.
- 프록시, 방화벽, TLS 검사 장비 또는 다른 네트워크 제어 장치가 연결을 종료하거나 필터링하는지 확인하세요. 클라이언트가 사용하는 경로의 로그와 동작을 같은 시각의 서버 측 로그와 비교하세요.
Java TLS 디버그 로깅으로 협상 세부 정보를 확인할 수 있지만, 로깅을 켜는 방법과 출력은 Java 클라이언트 및 실행 방식에 따라 다릅니다. 해당 클라이언트나 런타임에 문서화된 진단 설정을 사용하고, 연결 세부 정보가 노출될 수 있으므로 로그를 안전하게 보관하세요. 기록된 협상 내용을 서버 설정과 비교하고, 예외 문구만으로 근본 원인을 추정하지 마세요.
확인된 원인에 맞는 수정 적용하기
한 번에 설정 하나씩 변경하고, 진단 결과가 뒷받침할 때만 수정하세요. 프로세스가 알려진 호환성 문제나 보안 업데이트 누락이 있는 런타임을 사용한다면, 애플리케이션에서 지원하는 배포 절차에 따라 런타임을 업데이트하고 프로세스가 실제로 어떤 런타임을 불러오는지 확인하세요. 서버 측에서 연결을 닫는 문제에 Java 업데이트가 항상 해결책인 것은 아닙니다.
프로토콜 협상 문제가 확인되면 애플리케이션에서 사용하는 특정 HTTP 클라이언트, 소켓 또는 프레임워크를 통해 프로토콜을 설정하세요. https.protocols 시스템 속성이 모든 Java TLS 클라이언트에 적용되는 것은 아닙니다. 아래 API 예제는 SSLParameters 객체를 만들고 프로토콜 목록에 TLSv1.2를 지정한 뒤 저장된 값을 읽습니다. Java API 호출만 보여 주는 예제입니다. 실제 클라이언트에서는 해당 클라이언트의 소켓이나 설정에 적절한 매개변수를 적용해야 하며, 상대편도 호환되는 프로토콜을 지원해야 합니다. 예제를 실행하세요. 제공된 실행 기록의 출력은 Configured protocol: TLSv1.2이며, 바이트 일치 보존 대상인 코드 블록의 Configured protocol 라벨은 영어 그대로 표시됩니다.
import javax.net.ssl.SSLParameters;
public class TlsProtocolExample {
public static void main(String[] args) {
SSLParameters parameters = new SSLParameters();
parameters.setProtocols(new String[] {"TLSv1.2"});
System.out.println("Configured protocol: " + parameters.getProtocols()[0]);
}
}
인증서 검사에서 호스트 이름, 유효 기간 또는 체인 문제가 드러나면 엔드포인트가 제공하는 인증서나 서버 설정을 바로잡으세요. 체인은 올바르지만 Java 프로세스에 적절한 신뢰 앵커가 없다면, 해당 프로세스가 선택한 트러스트스토어를 확인하고 조직의 신뢰 관리 절차를 따르세요. 핸드셰이크가 실패했다는 이유만으로 인증서를 가져오지 마세요. 검증되지 않은 인증서를 추가하면 신원 확인이 약해질 수 있습니다. keytool로 Java 키스토어를 확인하거나 변경할 수 있지만, 사용할 파일과 옵션, 필요한 권한은 런타임과 배포 환경에 따라 다릅니다. 경로를 임의로 가정한 명령을 복사하는 대신 해당 환경의 문서를 참고하세요.
증거가 프록시, 방화벽 또는 원격 정책을 가리키면 관련된 경로나 설정만 담당자와 함께 조정하세요. 클라이언트 쪽에서 상대편이 연결을 닫은 이유를 확인할 수 없다면 서버 관리자에게 서버 측 TLS 로그, 지원되는 프로토콜과 암호 스위트 설정, 인증서 구성 및 최근 변경 사항을 비교해 달라고 요청하세요.
수정 확인 및 서버 측 문제 에스컬레이션
같은 애플리케이션 런타임, 엔드포인트, 네트워크 경로에서 같은 요청을 다시 시도하세요. 핸드셰이크가 성공하고 예상한 애플리케이션 응답을 받아야 연결이 완료된 것입니다. 설정을 바꾸기만 한 것으로는 해결 여부를 확인할 수 없습니다. 예외가 계속 발생하면 시각, 엔드포인트, 런타임 및 클라이언트 버전, 관련 클라이언트 진단 정보와 해당 시각의 서버 또는 프록시 로그를 수집해 어느 쪽에서 연결을 닫는지 비교하세요. 로그를 공유하기 전에 자격 증명과 기타 민감한 값을 가리세요. 서버 측 증거를 직접 확인할 수 없다면 클라이언트 설정을 또 바꾸면 해결될 것이라고 가정하지 말고, 이 정보를 서버 또는 네트워크 관리자에게 전달하세요.