본문 바로가기
JAVA/[JAVA] 바구니

SSL/TLS 서버 통신 (JSSE, TrustManager)

by oncerun 2021. 11. 6.
반응형

JSSE

 

Java Secure Socket Extensing 통칭 JSSE는 안전한 인터넷 통신을 가능하게 하는 자바 패키지 및 FrameWork입니다.

 

TLS/SSL을 구현한다라는 것은 응용 계층의 프로토콜인 HTTP, SMTP, FTP, Telent을 사용하는 클라이언트와 서버 간의 

데이터의 안전한 통신을 보장하는 안전한 네트워크 애플리케이션을 개발하는 것과 같습니다.

 

이 JSSE 패키지에는 java.security, java.net 를포함하고 있습니다. 또한 javax라는 표준 확장 패키지도 존재하고 있습니다.

 

* Extension package
  sun사에서 공식적으로 간주한 패키지를 표준 확장 패키지라하며 그렇지 않은 것을 비표준 패키지라고 합니다. 

표준 확장 패키지에는 보통 java 뒤에 x가 붙은 javax로 시작합니다. 이러한 표준 확장 패키지는 자바에 기본으로 포함되는 기본 라이브러리 패키지인 일급 패키지로 승격될 수 있었는데 여기서 문제가 하나 발생합니다.

일급 패지로 승격합은 javax에서 java로 패키지 이름이 변경되는 일이 발생한다는 것이었고 이것은 기존 javax로 작성된 코드가 java로 모두 변경되어야 함을 뜻했습니다. 이러한 개발자의 원성이 받아들여지면서 지금의 javax로 시작하는 이름을 그대로 유지하게 되었습니다. 

 

JSSE 패키지에는 확장 소켓 클래스와 Trust 및 Key 관리자를 제공하며 소켓의 생성 동작을 캡슐화하기 위해 소켓 팩토리 프레임워크를 제공하여 이를 보완합니다.

 

javax.net.ssl.SSLSocketFactory 클래스는 보안 소켓을 생성하기 위한 객체 팩토리입니다. 소켓 생성 자체가 캡슐화되어있기 때문에 우리는 소켓 팩토리를 생성하여 SSL 소켓 인스턴스를 얻어야 합니다. 그게 프레임워크의 규칙이죠.

 

이때 사용하는 메소드는 SSLSocketFactory.getDefalut()를 통하여 기본 소켓 팩토리를 얻을 수 있습니다.

기본 소켓 팩토리는 서버에 대한 인증만 할 수 있도록 default로 설정됩니다.

SSLSocketFactory의 인스턴스가 생성되었다면 SSLSocket의 인스턴스의 createSocket() 호출하는 것으로 

소켓 인스턴스를 생성할 수 있습니다.

 

String url = "통신 목적지 주소";

SocketFactory factory = SSLSocketFactory.getDefault();
Socket scoket = factory.createSocket(url);

 

 

자바는 기본 인증서 저장소에서 서버가 보낸 인증서가 신뢰하는 CA 기고나에서 생성된 것인지 확인한 후 통신을 허락합니다. 

SSLhandshake과정을 생각하면 됩니다. 당연히 통신하려는 서버의 인증서를 검증하는 절차를 다루어야 하는데 

클라이언트가 웹브라우저인 경우에는 브라우저에 저장된 인증서 체인 혹은 CA기관을 통해 인증서를 검증하면 되지만

서버 TO 서버, 내부망의 상황에서는 요청을 보내는 서버(클라이언트)는 요청을 받는 서버의 인증서를 검증하기 위해 다양한 방법을 사용해야 합니다.

 

기본적으로 자바는 서버가 보낸 인증서를 신뢰하지 않습니다. 그렇기 때문에  SSLhandshake과정에서 오류가 발생하고 만약 /etc/hosts 파일에 해당 호스트가 존재하지 않으면 별도의 DNS가 없다면 Hostname관련 오류도 발생하고 자살

 

하지 않기 위해 우리는 해당 인증서를 자바 인증서 보관소로 가져와서 통신을 진행해야 합니다.

기본적으로 자바 인증서 보관소는 javax.net.ssl.trustStore 시스템 속성이 정의되지 않으면 

$JAVA_HOME/lib/security/cacerts or jssecacerts에서 인증서를 찾습니다.

 

TrustManager 

 

TrustManager는 javax.net.ssl 패키지에 존재하는 인터페이스입니다.

 

TrustManager는 원격 호스트에서 제공하는 TLS/SSL의 인증서의 자격 증명(인증서 체인)을 검사하고 해당 인증서를 신뢰하고 수락할지 여부를 결정하는 책임을 가지고 있습니다. 

 

일반적으로 TrustManager는 SSL 서버가 인증되었는지 여부를 결정하기 위해 SSL 클라이언트를 사용하지만 클라이언트 인증도 필요할 때 SSL 서버에서 사용할 수 있습니다.

 

TrustManagers는 TrustManagerFactory를 사용하거나, TrustManager 하위 클래스를 구현하여 생성합니다.

 

해당 인터페이스를 구현한 구현체 X509 TrustManager 클래스가 있습니다.

X509 인증서형식

 

 

용어를 몇 가지 정리하고 가야겠습니다.

 

1. PKI : public key Infrastructrue의 약자로 공개키 기반 구조를 뜻합니다.

 

2. X.509 : 공개키 인증서와 인증 알고리즘을 사용하기 위한 PKI의 표준입니다.  주로 인증서의 작성, 교환을 위한 표준 규격입니다.

 

3. PKCS (Public Key Cryptography Standards) : Private Key를 저장하는 문법에 관한 표준입니다.

 

4. CRL( Certificate Revocation List), OCSP (Online Certificate Status Protocol) : 인증서 유효성 점검을 위한 표준/방법

 

5. CA ( Certificate Authority) : 인증기관

 

6. ASN.1 ( Adstract Syntax Notation One) : 추상 구문 기법 , 네트워크 상의 데이터 교환을 정의한 프로토콜

 

7. RSA ( Rivest-Shamir-adleman) : 공개키 암호 시스템의 하나

 

8. ECDSA (Elliptic Curve Digital Signature Algorithm) : 타원 곡선을 이용한 전자서명 알고리즘

 

확장자 

 종류  확장자  설명
 DER (Distinguished Encoding Representation)  .der  ASN.1을 표현하는 방식의 종류. (바이너리로 저장됨)
 PEM (Privacy Enhanced Mail)  .pem  Base64로 인코딩된 ASCII 텍스트
 (표준으로 더 자주 사용됨)

 

내용

종류  확장자  설명
 Private Key  .key  개인키
 Certificate  .cer (Windows 주로 사용)
 .crt (*NIX 주로 사용)
 인증서
 PKCS #12  .p12, .pfx  하나의 파일에 개인키, 인증서 등을 같이 저장하는 방식에 대한 표준
 Certificate Signing Request  .csr  인증서 발급을 위해 내 개인키 서명을 CA에게 보내기 위한 파일
 Serial  .srl  CA가 인증서를 발급할 때 Serial 을 관리하기 위한 파일

 

 

서버 인증 무시하느냐, 공인인증서로 인증하느냐 아니면 사설인증서로 인증하느냐에 따라 구현에 차이가 있습니다.

 

서버에서 인증 과정을 결정하는 부분은 SSLContext.init() 메서드를 이용하여 진행합니다.

 

init() 메서드의 매개변수에 null을 전달함으로써 인증 과정을 무시하고 서버의 응답을 받을 수 있습니다. 

URL url = new URL(urlString);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();

SSLContext ctx = SSLContext.getInstance("SSL");
ctx.init(null,null,null);
conn.setSSLSocketFactory(ctx.getSocketFactory());

 

그리고 JRE를 설치하면 기본적으로 jre/lib/security/cacerts라는 파일이 존재합니다. 이 파일에는 공인 인증된 인증서 저장소 파일이 있습니다. 해당 인증서 저장소를 이용하여 서버의 인증서를 검사하게 할 수 있습니다.

 

 

1. HTTPS URL connection을 얻는다.

2. new HostnameVerifier()을 통해 인터페이스를 구현한 인스턴스를 사용자 정의에 따라 구현한다.

 

SSLContext ctx = SSLContext.getInstance("SSL");

ctx.init(null, new TrustManager[] { new X509TrustManager() {

          @Override
         public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
          // client certification check
         }

         @Override
         public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {

          // Server certification check
          
          try{
          	KeyStore trustStore = KeyStore.getInstance("SSL");
            String cacertsPath = System.getProperty("java.home") + " /lib/security/cacerts";
          	trustStore.load(new FileInputStream(cacertsPath), "changeit".toCharArray());  
          	
            //defaultAlogorithm은 X.509
            TrustManagerFactory tmf= TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(trustStore);
            TrustManager[] tms = tmf.getTrustManagers();
            ( (X509TrustManager) tms[0].checkServerTrusted(chain, authType);
            
          }catch ( KeyStoreException e) {
          	//keyStore Exception 처리
          }catch ( NoSuchAlgorithmException e){
          	
          }catch ( IOException e ) {
          }                         
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {

                return null;

        }
	}}, null);

    conn.setSSLScoketFactory(ctx.getSocketFactory);

.....

 

반응형

'JAVA > [JAVA] 바구니' 카테고리의 다른 글

finalizer와 cleaner 사용을 피하라  (0) 2022.11.07
Exception  (0) 2022.09.25
짧)[JAVA] 객체지향 세계  (0) 2021.04.25
[JAVA] JAVA Serialize  (0) 2021.04.23
[JAVA] Stream  (0) 2021.04.19

댓글