이번엔 외부시스템과 인터페이스 통신을 하는데, SSL 통신을 한다. 근데 뭐 고객사에서는 SSL 통신을 하는데 인증서도 안주고 도메인으로 통신하는 것도 아닌 IP 통신인지라 당연히 PKIX 오류가 발생하기 마련.

이 오류는 SSL 통신시 인증서 검증에서 실패했다는 뜻으로, 우리가 흔히 웹 브라우저로 인터넷 하다가도 '신뢰 할 수 없는 인증서입니다~ 무시하고 계속할래~?' 하는 식으로 묻는것도 이와 같은 맥락이라고 볼 수 있다. 

curl 에서는 -k 옵션 하나로 간단히 무시할 수 있는데, JAVA 에서는 쉽지 않았다. 이하는 try catch 부분.

 

java 1.8 및 SpringBoot 2.7.8 에서 수행했다.

 

오류는 다음과 같다.

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

 

대충 curl 호출부는 다음과 같다. 이렇게 호출하면 성공한다.

curl -H 'Content-Type: application/json' -H 'Authrization: Basic toke' -k -x POST 'https://111.111.111.111:443/test/v1/auth/token' ;

 

curl 호출시 -k 옵션은 인증서 검증 우회하겠다는 의미이다.

 

이하는 java 소스코드.

전반적으로 httpURLConnection과 같은 양상을 띄지만 SSL 통신 설정부라거나, 기타 잡다한 설정들이 더 추가가 되어있다.

 

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package com.test.common.utils;
 
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
 
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
 
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import com.fasterxml.jackson.databind.ObjectMapper;
 
public class HttpsURLConnector {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    public JSONObject postURL ( String urlData , Map<String, Object> headers , Map<String, Object> param) {
        JSONObject jsonResult = new JSONObject();
        
        //Http 요청시 필요한 URL 주소의 변수 선언
        String totalUrl = "";
        totalUrl = urlData.trim().toString();
        
        // http 통신 객체 선언
        URL url = null;
        HttpsURLConnection httpConn = null;
        
        // http 통신 응답에 대한 변수
        String resData = "";
        BufferedReader buff = null;
        StringBuffer strBuff = null;
        
        // 호출 결과값에 대한 변수
        String returnData = "";
        try {
            ObjectMapper mapper = new ObjectMapper();
            String paramData = mapper.writeValueAsString(param);
            
            // url.openConnection을 시도하기 전에 아래 설정을 완료한다
            TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
 
                public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
                }
 
                public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
                }
            } };
            
            SSLContext sslContext;
            sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, null);
            
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
            
            // 파라미터로 들어온 url을 사용하여 connection 시도
            url = new URL(totalUrl);
            httpConn = (HttpsURLConnection) url.openConnection();
            httpConn.setHostnameVerifier(new HostnameVerifier( ) {
                
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    // return true로 해주어야 모든 SSL 검증을 무시함
                    return true;
                }
            });
            
            // http 요청 설정
            httpConn.setRequestMethod("POST");
            httpConn.setRequestProperty("Content-Type""application/json; utf-8");
            httpConn.setRequestProperty("Accept""application/json");
            
            // header 파라미터 설정
            if ( headers != null ) {
                for ( Map.Entry<String, Object> header : headers.entrySet()) {
                    httpConn.setRequestProperty(header.getKey(), header.getValue().toString());
                }
            }
            
            // Body부 생성 - doOutPut을 true로 주어야함
            httpConn.setDoOutput(true);
            try (OutputStream os = httpConn.getOutputStream() ){
                byte requestData[] = paramData.getBytes("utf-8");
                os.write(requestData);
                os.close();
            } catch ( Exception e) {
                logger.error("== OutputStream ERROR ::::: " + e.getMessage());
            }
            
            //커넥션~
            httpConn.connect();
            
            // 응답에 대한 데이터를 Buffer로 받아 처리
            buff = new BufferedReader(new InputStreamReader(httpConn.getInputStream(), "UTF-8"));
            strBuff = new StringBuffer();
            while ( ( resData = buff.readLine() ) != null ) {
                strBuff.append(resData); // StringBuffer에 응답받은 데이터를 순차적으로 저장함
            }
            
            returnData = strBuff.toString();
                
            //https 응답코드
            String resCode = String.valueOf(httpConn.getResponseCode());
            JSONParser parser = new JSONParser();
            Object obj = new Object();
            try {
                obj = parser.parse(strBuff.toString());
            } catch (Exception e) {
                logger.error("StringBuff Parser ERROR ::::: " + e.getMessage());
            }
            jsonResult = (JSONObject) obj;
        } catch (Exception e) {
            logger.error("TOTAL Error ::::: " + e.getMessage());
        }
        
        return jsonResult;
    }
}
cs

 

TrustAllCerts 부분과 setHostnameVerifier 부분을 잘 염두에 두면 큰 문제 없이 성공할 수 있다.

가장 중요한 부분은 url.openconnection  하기 전에 trustAllCerts를 선언해주어야 한다는 점이었다....

블로그 이미지

김생선

세상의 모든것을 어장관리

,