Skip to content

✅ What a SSLHandshakeException

KyungMin Lee edited this page Aug 28, 2024 · 3 revisions

🚨 문제 발생!

  • Java SDK를 통해서 Log를 전송하는 과정에서 SSLHandshakeException이 발생함.
Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

🛠️ 원인 찾기

  • 원인을 찾기 위해 예외 상황 재현 과정에서 특이한 점을 발견했습니다.

    • 회사 내부망으로 접속한 경우 예외가 발생한다.
    • curl이나 브라우저에서 보내는 요청에 대해서는 정상적으로 처리된다.
    • Java의 HttpClient를 활용한 요청을 보낼 때만 예외가 발생한다.
      • 모든 API 요청에 대해 예외가 발생한다.
  • 정리하자면 아래와 같은 상황이 되었습니다.

회사 내부망 인터넷
브라우저 & Curl
Java

테스트 코드

HttpClient httpClient = HttpClient.newBuilder()
            .build();

HttpRequest httpRequest = HttpRequest.newBuilder()
    .uri(URI.create("https://api.logbat.info/test/count"))
    .header("Content-Type", "application/json")
    .GET()
    .build();

HttpResponse<String> response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
System.out.println("Response: " + response.body());

Warning

테스트 편의를 위해 URL을 https://api.logbat.info/test/count로 변경했습니다.

  • 결과
HttpClient Result

CURL 결과는?

정상적으로 200을 반환한다.

curl Result

🔍 실마리 찾기

연결되는 사이트 찾기

HttpClient로 접근 가능한 사이트를 찾아보니 https://google.com, https://www.woowahan.com, ... 사이트는 가능하다는 점을 알게 되었습니다.

이에 위 사이트들의 인증서를 비교해보니 아래와 같았습니다.

image image

그래서 우리 인증서를 신뢰할 수 있는 기관 인증서를 사용한다면 해결할 수 있다! 라는 결론에 도달했습니다.

⛳️ 우리 힘으로 해결해보자!

인증서 발행 주체를 변경해보자!

방법을 열심히 강구하던 중 우아한형제들 페이지의 SSL 인증 기관에 CloudFlare가 있는 것을 확인하게 되었습니다.

image

Let's Encrypt에서 발행한 SSL 인증서 대신 CloudFlare 인증서를 사용하면 해결할 수 있지 않을까? 라는 아이디어에 인증서 발급 주체를 CloudFlare로 변경했습니다.

image

결과는? ❌ 실패! 동일한 HandShakeException이 발생했습니다.

SSL 우회 로직을 추가하자!

HttpClient를 생성할때 우회 로직을 포함해서 생성하도록 했습니다.

private HttpClient createHttpClient() {
    try {
        TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            public void checkClientTrusted(X509Certificate[] certs, String authType) {
            }

            public void checkServerTrusted(X509Certificate[] certs, String authType) {
            }
        }
    };
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init( (KeyManager[]) null, trustAllCerts, new SecureRandom());
        return HttpClient.newBuilder().sslContext(sslContext).build();
    } catch ( Exception e ) {
        throw new RuntimeException("Failed to create HttpClient with disabled SSL verification", e);
    }
}

결과는? ⚠️ 성공! 하지만.. 보안 부분을 직접적으로 무시하고 처리한다는 것이 타당한가? 에 대한 회의를 진행하고 보류하기로 했습니다.

최종적으로는...

  • 회사 측에 해결 요청을 보냈습니다...

💡 결과는?

  • 며칠 후 인프라 팀에서 서비스 도메인에 대한 예외 등록을 완료했다는 전달을 받았습니다.
    • 테스트 결과 바로 통과

🧪 결론

  • 인트라넷의 화이트리스트는 쉽게 뚫을 수 없습니다.
Clone this wiki locally