티스토리 뷰
프로그램 언어/Spring
Rest API 요청 시 PKIX path build failed 에러 발생관련 이슈 처리 방법(경험담 포함..)
광돌ol 2023. 5. 29. 15:07728x90
반응형
문제 발단
해당 A 사이트의 데이터를 B 사이트에 api로 데이터를 호출하여 처리하는 방식을 요청받아 작업을 진행하였다.
일반적인 HttpURLConnection 을 이용하여 호출하는 방식을 선택하여 작업을 하였다. 로컬과 개발서버의 통신 과정에서는
큰 문제가 없음을 확인하고 실서버에 배포하여 확인을 하였다. 그런데 PKIX path build failed 이슈가 발생하였다.
PKIX path build failed 원인
-
해당하는 도메인의 인증서에서 에러가 발생하여 문제가 되는 것
-
웹브라우저에서 신뢰하는 인증서 인경우에도 발생
인증서는 유효한데 발생하는 이유?
ssl 연결 시 HANDSHAKE가 어떤 방식으로 이루어지는지 보면 된다
-
클라이언트가 CA로부터 발급받은 인증서로 서비스하는 서버와 연결
-
서버가 전송한 인증서 CA로부터 제대로 발급받은 것인지 확인
-
클라이언트는 CA와 공개키 목록을 보유(신뢰하고 있음)
-
신뢰할 수 있는 CA로부터 발급된 인증서라고 확인을 마치면 CA의 공개키를 이용하여 인증서 복호화
-
세션키 값을 만들어 인증서에 저장된 서버의 공개키로 암호화하여 서버로 전송 진행
-
세션키로 암호화된 요청 전송
PKIX 에러는 위에 빨간줄로 칠해진 부분에서 발생하게 된다.
해결방안 : 여러 서버 수정 방법이 있지만 해당 요청관련해서만 유효한 것을 원하는 경우에는 우회하여 요청하는 방식을 사용하면 된다.
public class SSLBypassApp {
public String getHtmlBody(String uri) throws Exception {
CloseableHttpClient httpClient = getApacheSslBypassClient();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
HttpMessageConverter<?> stringHttpMessageConverter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
List<HttpMessageConverter<?>> httpMessageConverter = new ArrayList<>();
httpMessageConverter.add(stringHttpMessageConverter);
restTemplate.setMessageConverters(httpMessageConverter);
URI targetUrl = UriComponentsBuilder.fromUriString(uri).build().toUri();
HttpHeaders headers = new HttpHeaders();
Charset utf8 = Charset.forName("UTF-8");
MediaType mediaType = new MediaType("text", "html", utf8);
headers.setContentType(mediaType);
headers.set("User-Agent", "mozilla");
headers.set("Accept-Language", "ko");
// gzip 사용하면 byte[] 로 받아서, 압축을 풀고 decoding 해야 한다.
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> responseEntity = restTemplate.exchange(targetUrl.toURL().toString(), HttpMethod.GET, entity, String.class);
String result = responseEntity.getBody();
return result;
}
//헤더 추가용~
public String getHtmlBody(String uri,Map<String,String> requestHeaders) throws Exception {
CloseableHttpClient httpClient = getApacheSslBypassClient();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
HttpMessageConverter<?> stringHttpMessageConverter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
List<HttpMessageConverter<?>> httpMessageConverter = new ArrayList<>();
httpMessageConverter.add(stringHttpMessageConverter);
restTemplate.setMessageConverters(httpMessageConverter);
URI targetUrl = UriComponentsBuilder.fromUriString(uri).build().toUri();
HttpHeaders headers = new HttpHeaders();
Charset utf8 = Charset.forName("UTF-8");
MediaType mediaType = new MediaType("text", "html", utf8);
headers.setContentType(mediaType);
headers.set("User-Agent", "mozilla");
headers.set("Accept-Language", "ko");
for(Map.Entry<String, String> header : requestHeaders.entrySet()) {
headers.set(header.getKey(), header.getValue());
}
// gzip 사용하면 byte[] 로 받아서, 압축을 풀고 decoding 해야 한다.
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> responseEntity = restTemplate.exchange(targetUrl.toURL().toString(), HttpMethod.GET, entity, String.class);
String result = responseEntity.getBody();
return result;
}
public CloseableHttpClient getApacheSslBypassClient()
throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
return true;
}
};
return HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier())
.setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, acceptingTrustStrategy).build()).build();
/* 람다식 부분
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] x509Certificates, String s)
throws CertificateException {
return true;
}
};
}
}
사용 예시
// 깊은 복사.
TestVO tempVO = (TestVO)test.clone();
String token = tempVO.getAccessToken(); // 로그인 접근 토큰;
String header = "Bearer " + token; // Bearer 다음에 공백 추가
String apiURL = tempVO.getProfileDoUrl(); // 요청할 url
Map<String, String> requestHeaders = new HashMap<>();
requestHeaders.put("Authorization", header);
SSLBypassApp bypasser = new SSLBypassApp(); //우회 클래스 선언
String htmlContent = bypasser.getHtmlBody(apiURL,requestHeaders); // api 요청
JSONObject json = new JSONObject();
json = (JSONObject)(new JSONParser()).parse(htmlContent);
728x90
반응형
'프로그램 언어 > Spring' 카테고리의 다른 글
hazelcast-client 적용하기1(이론편) (1) | 2024.02.26 |
---|---|
Spring boot 3.x.x 에서의 Swagger적용 방법 (gradle 기준) (1) | 2023.11.28 |
Spring(Boot) FilterRegistrationBean를 사용 시 @Autowired 사용주의 (0) | 2023.05.12 |
Srping(boot) Filter 설정 방법 (0) | 2023.05.12 |
ThymeleafParser를 이용한 동적 pdf 파일 생성 (0) | 2023.05.03 |
250x250
반응형
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- Java
- 이미지
- ncp
- 리눅스
- centos7
- dockerfile
- 격리수준
- Cache
- mybatis
- 네이버 클라우드
- 캘린더
- leatcode
- 도커
- Lock
- Quartz
- spring
- 스케줄러
- 권한
- 개념 이해하기
- MySQL
- LocalDate
- 캐시
- insert
- Linux
- 알고리즘
- 정의
- docker
- hazelcast
- 컨테이너
- dfs
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
글 보관함