JDK/JRE 6 버전은 기본적으로 TLS 1.2 버전을 지원하지 않아 업그레이드가 필요하지만,
당장 작업이 여의치 않을 경우, JCE 및 BouncyCastle를 설치하여 사용 가능합니다.
1. 사이트에서 허용하는 Cipher와 내 서버에 설치된 JDK/JRE에서 지원하는 cipher 목록을 비교
맞는 cipher가 없는 경우 handshake 오류가 발생합니다.
1) 사이트 허용 TLS, Cipher 정보 확인
위 사이트에서 URL 조회 후, Cipher Suites 항목의 # TLS 1.2 (suites in server-preferred order) 목록에 나옵니다.
2) 설치된 JDK/JRE 의 Supported Cipher 확인 (샘플 코드)
import java.util.Map;
import java.util.TreeMap;
import javax.net.ssl.SSLServerSocketFactory;
public class SecurityListings {
public static void main(String[] args) {
SSLServerSocketFactory ssf = (SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
TreeMap<String, Boolean> ciphers = new TreeMap();
for (String cipher : ssf.getSupportedCipherSuites())
ciphers.put(cipher, Boolean.FALSE);
for (String cipher : ssf.getDefaultCipherSuites())
ciphers.put(cipher, Boolean.TRUE);
System.out.println("Default Cipher");
for (Map.Entry<String, Boolean> cipher : ciphers.entrySet())
System.out.printf(" %-5s%s%n", (cipher.getValue() ? '*' : ' '), cipher.getKey());
}
}
2. JDK_HOME/jre/lib/security/java.security 백업 후, 수정
1) provider 추가 (맨 위에 놓지 않으면 CBC만 나오고 GCM 등의 cipher가 안 나올 수 있습니다.)
security.provider.1=org.bouncycastle.jce.provider.BouncyCastleProvider
security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
기존 provider는 +2 씩하여 변경 (기존 1은 3, 2는 4등 으로)
2) crypto.policy 확인 (JCE)
crypto.policy=limited 로 되어 있으면 crypto.policy=unlimited 로 변경
3. /jre/lib/ext 에 아래 BouncyCastle 파일 업로드
https://www.bouncycastle.org/download/bouncy-castle-java/#latest
bcprov-jdk15to18-x.y.z.jar
bctls-jdk15to18-x.y.z.jar
bcutil-jdk15to18-x.y.z.jar
4. /jre/lib/security에 JCE 파일 업로드 (필요 시)
https://www.oracle.com/java/technologies/jce-6-download.html
이미 있는 경우, 별도 업로드 하지 않고 2)번의 crypto.policy=unlimited 만 적용해도 무방합니다.
5. 테스트
1) BouncyCastle 정상 설치 여부 확인 (예시)
import java.security.Security;
public class BC {
public static void main(String[] args) {
if (Security.getProvider("BC") == null) {
System.out.println(providerName + " NOT FOUND");
} else {
System.out.println(providerName + " FOUND");
}
}
}
2) https(TLS v1.2) 호출 가능 여부 확인 (예시)
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.nio.charset.Charset;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
public class HttpsTest {
public static void main(String[] args) {
int connectTimeout = 10000;
int readTimeout = 10000;
String apiUrl = "https://APIURL";
try {
//System.setProperty("org.bouncycastle.jsse.client.assumeOriginalHostName", "true"); // certificate_unknown(46) 발생 시
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, null, new java.security.SecureRandom());
URL urlObj = new URL(apiUrl);
HttpsURLConnection conn = (HttpsURLConnection) urlObj.openConnection();
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
conn.setConnectTimeout(connectTimeout);
conn.setReadTimeout(readTimeout);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type","application/json");
conn.setRequestProperty("Accept-Charset","UTF-8");
conn.setDoOutput(true);
conn.setDoInput(true);
String jsonParams = "{\"name\": \"value\"}";
OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream(),Charset.forName("UTF-8"));
wr.write(jsonParams);
wr.flush();
BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())));
StringBuilder response = new StringBuilder();
String output;
while((output = br.readLine()) != null) {
response.append(output);
}
System.out.println("response: " + response.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
※ System.setProperty("org.bouncycastle.jsse.client.assumeOriginalHostName", "true"); 이 부분은 테스트 시 아래와 같은 에러 메시지가 나올 때 넣어주면 됩니다.
org.bouncycastle.jsse.provider.ProvTlsClient notifyConnectionClosed
org.bouncycastle.tls.TlsFatalAlert: certificate_unknown(46)
Caused by: java.security.cert.CertificateException: Unable to construct a valid chain
Caused by: java.security.cert.CertPathBuilderException: No issuer certificate for certificate in certification path found.