-
Notifications
You must be signed in to change notification settings - Fork 1
✅ 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
로 변경했습니다.
- 결과
정상적으로 200을 반환한다.
HttpClient
로 접근 가능한 사이트를 찾아보니 https://google.com
, https://www.woowahan.com
, ...
사이트는 가능하다는 점을 알게 되었습니다.
이에 위 사이트들의 인증서를 비교해보니 아래와 같았습니다.
그래서 우리 인증서를 신뢰할 수 있는 기관 인증서를 사용한다면 해결할 수 있다! 라는 결론에 도달했습니다.
방법을 열심히 강구하던 중 우아한형제들 페이지의 SSL 인증 기관에 CloudFlare가 있는 것을 확인하게 되었습니다.
Let's Encrypt에서 발행한 SSL 인증서 대신 CloudFlare 인증서를 사용하면 해결할 수 있지 않을까? 라는 아이디어에 인증서 발급 주체를 CloudFlare로 변경했습니다.
결과는? ❌ 실패! 동일한 HandShakeException이 발생했습니다.
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);
}
}
결과는?
- 회사 측에 해결 요청을 보냈습니다...
- 며칠 후 인프라 팀에서 서비스 도메인에 대한 예외 등록을 완료했다는 전달을 받았습니다.
- 테스트 결과 바로 통과
- 인트라넷의 화이트리스트는 쉽게 뚫을 수 없습니다.
- 💡 How To? Chat-GPT 리뷰어 도입하기
- 💡 How To? 도메인 설정하기
- 💡 How To? NGINX로 CORS 설정하기
- 💡 How To? JavaScript SDK 개발하기
- 💡 How To? Java SDK 개발하기
- 💡 How To? AWS Lambda로 부하 테스트 진행하기
- 💡 How To? Terraform + Locust로 부하 테스트 진행하기
- ⚙️ Refactoring: 로그 저장을 한번에 하자!
- 🚨 완벽하지 않은 샌드박스 테스트가 불러온 폭풍
- 💡 How To? 디버깅용 경쟁이 발생하지 않는 Long 카운터 만들기!
- 💡 How To? Queue의 poll과 push는 왜 한 스레드에서 담당하게 했을까?