티스토리 뷰

728x90
반응형
문제 발단

해당 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
반응형
250x250
반응형
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
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
글 보관함