본문 바로가기
안드로이드

[SSL] Retrofit2 HTTPS 통신과 TLS 1.0 / 1.1 서비스 제한 조치

by 디지털노마더 2020. 7. 14.

사내 Android 프로젝트로 Retrofit2를 이용하여 API 통신을 구현하였다. 

HTTPS 도메인에서도 정상적으로 통신이 이루어져서 결과값을 도출해내고 있었다.

 

그런데, HTTPS(443) API에서 'SSL HandShake Error'가 발생하면서 오류가 발생했다.

왜 그 동안 정상적으로 네트워크 통신을 하던 게 갑자기 이러는 걸까?

 

우선, 에러 메시지에서 말하는 SSL에 대해서 알아야 한다.

 

SSL이란?

SSL(Secure Sockets Layer)은 Certificate Authority(CA)라 불리는 서드 파티로부터 서버와 클라이언트의 인증을 하는 데 사용된다. 주로 전송계층과 응용계층 사이에서 보안조치를 하는데 사용하게 된다. 우리들이 많이 접하게 되는 HTTPS://는 SSL을 사용하는 경우를 의미한다. 

 

구글링 해보면 다양한 개발자분들이 2가지 안을 제시해놓는다.

참고 : https://namjackson.tistory.com/26

 

 

1. 인증서 없이 HTTPS 사이트 우회 접속 통신

  (이는 일시적으로 테스트는 가능하지만, 최근 들어 구글에서는 권장하지 않는 방법이다.)

 

2. 정상적인 SSL 인증서를 가지고 HTTPS 통신 구현

  (인증서를 별도로 가지고 있다면 사용)

 

  res > raw 폴더 내에, 가지고 있는 인증서 파일(R.raw.localhost)을 추가한다.

  아래의 소스 코드를 참고하여, SSL 연결을 시도한다. 

/**
      * localhost라는 인증서 정보로 ssl 연결을시도합니다.
      * @param context
      * @return
      */
   public static SSLSocketFactory getPinnedCertSslSocketFactory(Context context) {
       try {
           CertificateFactory cf = CertificateFactory.getInstance("X.509");
           InputStream caInput = context.getResources().openRawResource(R.raw.localhost);
           Certificate ca = null;
           try {
               ca = cf.generateCertificate(caInput);
               System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
           } catch (CertificateException e) {
               e.printStackTrace();
           } finally {
               caInput.close();
           }
 
           String keyStoreType = KeyStore.getDefaultType();
           KeyStore keyStore = KeyStore.getInstance(keyStoreType);
           keyStore.load(null, null);
           if (ca == null) {
               return null;
           }
           keyStore.setCertificateEntry("ca", ca);
 
           String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
           TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
           tmf.init(keyStore);
 
           SSLContext sslContext= SSLContext.getInstance("TLS");
           sslContext.init(null, tmf.getTrustManagers(), null);
 
           return sslContext.getSocketFactory();
       } catch (NoSuchAlgorithmException e) {
           e.printStackTrace();
       } catch (IOException e) {
           e.printStackTrace();
       } catch (KeyStoreException e) {
           e.printStackTrace();
       } catch (KeyManagementException e) {
           e.printStackTrace();
       } catch (Exception e) {
           e.printStackTrace();
       }
       return null;
   }

 

Retrofit2를 생성하는 단계에서 아래와 같이 OkHttpClient를 선언해준다.

public static Retrofit createRetrofit(Context context) {
        return new Retrofit.Builder()
                .baseUrl(SERVER_URL)
                .client(
                        new OkHttpClient.Builder()
                        .sslSocketFactory(SSLUtil.getPinnedCertSslSocketFactory(context))   //ssl                         
                        .build())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())                  // Rxandroid 
                .addConverterFactory(GsonConverterFactory.create())                         // JSON
                .build();
    }

 

하지만.. 나는 이렇게 시도해도 결과는 동일했다.

 

도대체 무엇인 문제인 건가? 오타가 있는 건가? 도메인에 문제가 있나? 

이런저런 생각을 하던 중에 호출하는 도메인 URL을 직접 접속해보았다.

 

그런데, 크롬 브라우저 개발자 도구(F12)를 확인해보니 아래처럼 경고창이 콘솔에 발생하였다.

TLS 1.0 관련한 오류를 구글링 하던 중에, TLS 1.0 / TLS 1.1의 보안 취약점으로 인해 지원을 중지한다는 공지를 Chrome Platform Status 사이트에서 확인할 수 있었다.

 

보통의 경우는 아래 3가지 정도의 사례로 HTTPS가 정해진다.

 

1. https ssl 이 적용된 사이트(공인된 CA의 인증서)

2. https ssl 이 적용되지 않은 사이트

3. https ssl  적용되었지만, 인증서가 보장되지 않은 사설 인증(사설 CA) 일 경우

 

하지만, 내가 겪은 사례는 TLS 버전에 대한 문제로 +@로 추가되는 사례이다.

해당 문제는 API 도메인 서버 TLS 버전을 업데이트(1.2 or 1.3) 해야 된다.

 

결론적으로 TLS 버전 문제로 인해 SSL HandShake Error가 발생했던 것이다.

 

구글링 하던 중 TLS 관련 이슈에 대한 중요한 공지글을 확인했다.

나는 이러한 중요한 사실을 왜 이제야 알게 된 것인가.. 이제야 복잡했던 머릿속의 퍼즐이 맞춰지는 느낌이다.

 

” 주요 브라우저 업체들이 2020년 상반기에 지원을 중지할 예정이었으나, 코로나 19 바이러스로 기존에 발표한 TLS 1.0 및 1.1 프로토콜 지원 중지 일정이 변경되고 있습니다. “

초기 SSL Protocol을 기반으로 개발된 TLS는 클라이언트-서버 간의 통신 채널을 보호하고 암호화하기 위해 사용되는 암호화 프로토콜입니다.

TLS는 현재 1.0, 1.1, 1.2, 1.3(최신)으로 총 4개 버전이 존재합니다. 이 중 구 버전인 TLS 1.0, 1.1은 POODLE과 BEAST와 같은 다양한 공격에 취약하며, 프로토콜 취약점을 이용한 하이재킹(hijacking)이 발생할 수 있습니다.

변경된 TLS 1.0 TLS 1.1 프로토콜 지원 중지 일정

 

서비스 개발에는 클라이언트 분야뿐만 아니라, 네트워크 통신에도 관심을 기울여야겠다는 깨달음을 얻는다.

 

 

 

 

 

 

 

 

댓글