티스토리 뷰
회사에서 Zencoder API을 사용하게 되어 자바에서 작업을 시작하려는데, 아래와 같이 SSLHandshakeException이 발생해서 뭔가 문제인지 구글링을 하게 되었습니다. 이미 아시는 분들도 많지만, 다시 한번 정리를 해봤습니다.
-
Zencoder API 작업 요청 주소
Exception 발생 화면
2. 개발 환경
실제 작성한 코드는 많지 않고 테스트를 쉽게 하려고 간단하게 유닛 테스트로 작성했습니다. github에 올린 코드를 참조해주세요.
-
OS : Mac OS
-
IDE: Intellij
-
Java : JDK 1.8
-
Source code : github
-
Software management tool : Maven
3. 해결책
이 문제를 해결하는 방법은 크게 2가지가 있습니다.
-
코딩상에서 직접 인증서 유효성 체크 하지 않기 (비추)
-
해당 인증서 자바 keystore에 저장하기 (추천 방식)
3.1 코드 상에서 직접 인증서 유효성 체크 하지 않기
자바 코드로 인증서 체크를 하지 않도록 HttpsConnection의 설정을 변경하는 방식입니다. 아래 코드에 대한 자세한 설명은 생략하도록 하겠습니다.
@Test
public void test_disable_certificate_from_code() {
disableCertificateCheck(); //#1
Assertions.assertThatCode(this::connectHttps).doesNotThrowAnyException();
}
private void disableCertificateCheck() {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (GeneralSecurityException e) {
}
}
3.2 해당 인증서 자바 keystore에 저장하기 (추천 방식)
자바 keystore에 인증서를 등록하는 방법에는 크게 2가지가 있습니다. 명령어 창에서 하던지 아니면 Portecle GUI 프로그램을 사용해도 상관없습니다.
3.2.1 Portecle GUI 사용하기
자바 keystore에 인증서를 등록하기전에 유닛 테스트를 실행하면, SSLHandshakeException이 발생합니다.
@Test
public void test_after_import_certificate() {
Assertions.assertThatCode(this::connectHttps).doesNotThrowAnyException();
}
Portecle은 keystore를 관리해주는 자바로 짠 GUI 프로그램입니다. 자바로 짜여 있어서 플롯폼 상관없이 어디서든 실행할 수 있습니다.
1. 다운로드 한후 압축 풀기
아래 링크에서 프로그램을 다운로드한 후 압축을 원하는 폴더에 풀어줍니다.
2. Portecle 실행
인증서 등록 후 저장 시 root 권한 필요하므로 sudo로 프로그램을 실행합니다.
># sudo java -jar portecle.jar
3. 접속 사이트에서 인증서를 다운로드합니다.
메뉴에서 Examine > Examine SSL/TSL Connection…을 클릭하고 접속하려는 사이트 주소를 입력 후 OK 버튼을 클릭합니다.
클릭 후에 인증서를 볼 수 있습니다. 이 내용을 저장하려면 PEM Encoding 버튼 클릭 후 Save 버튼을 눌려 저장합니다.
|
|
4. 자바 keystore에 등록하기
원하는 자바 버전의 $JAVA_HOME/lib/security/cacerts 파일을 열어서 새로운 인증서를 추가하고 저장하면 끝납니다.
설치된 자바 홈 폴더를 확인하고 싶으면 java_home 명령어로 확인할 수 있습니다.
># /usr/libexec/java_home -V
메뉴에서 열기 버튼을 클릭해서 cacerts 파일을 찾아 오픈하면 암호를 입력하게 되어 있습니다. 디폴트 암호 값은 changeit 입니다.
|
|
새로운 인증서를 추가하기 위해 메뉴 임포트 버튼을 클릭하고 다운로드한 인증서를 선택합니다.
파일 선택 이후 여러 질문에 Yes 버튼을 클릭하면 새로운 인증서가 추가된 것을 목록에서 확인할 수 있습니다.
다시 유닛 테스트를 실행하면 Exception 없이 잘 실행되는 것을 확인할 수 있습니다. 자 그면, 명령어 창에서 등록하는 방법을알아보겠습니다.
3.2.2 명령어창에서 자바 keystore에 인증서 임포트하기
명령어 창에서도 인증서를 다운로드하고 등록할 수 있습니다.
1. 인증서 다운로드하기
># openssl s_client -connect app.zencoder.com:443 | tee appzencoder.certlog
># openssl x509 -inform PEM -in appzencoder.certlog -text -out appzencoder.certdata
># openssl x509 -inform PEM -text -in appzencoder.certdata
2. 자바 keystore에 새로운 인증서 추가하기
># sudo keytool -importcert -file ./appzencoder.certdata -alias app.zencoder.com -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit
입력이후 질문이 나오면 yes 를 입력하면 등록이 완료됩니다.
부록
1. Bash Script로 자동 import하기
인증서 등록이후 영구적으로 사용가능할 줄 알았는데, 시간이 좀 지나면 expire되어 다시 재등록을 해야 하는듯하네요. 귀찮아서 그냥 script을 만들었습니다. 참조해서 사용하시면 좋을 것 같아요.
2. JUnit 실행시 SSL 인증서 체크 Disable 시키기
개인적으로 저는 JUnit Test 실행시 종종 발생하는 이슈라서 https 사이트에 접속하는 경우에 대해서만 JUnit Rule을 추가하여 해결 하였습니다. JUnit Rule에 대한 내용은 사전에 포스팅한 JUnit Rule이란을 참조해주세요.
API에 접속하는 유닛 테스트에 @ClassRule을 선언하고 새로 정의한 DisableSSLCertificateCheckRule을 disableSSLRule로 선언하였습니다. 테스트 실행전에 해당 Rule이 실행되어 SSL 체크를 무효화 시킵니다.
4. 참고
-
Java의 keystore에 SSL 인증서 import 하기
-
Certificate 다운로드 방법
'java' 카테고리의 다른 글
JUnit Rules이란 (0) | 2019.02.12 |
---|---|
아마존 S3 Bucket API 사용법 (0) | 2019.01.12 |
Java Jayway JsonPath 사용법 (0) | 2019.01.09 |
Lombok 기본 사용법 익히기 (0) | 2018.12.16 |