본문 바로가기

IT 실무 (환경구성)

서버 인증서 설치의 내부 메카니즘/원리, 폐쇄망 엔지니어를 위한 글

반응형

 인증서(Certificate)는 공개키 기반의 암호화 통신인 SSL(Secure Socket Layer)을 사용하는 엔지니어들에게 친숙하면서도, 사실은 깊게 들어가면 어려운 존재다. 해당 암호화 방식이 광범위하게 사용됨에 따라 같이 소개된 이 인증서에 대해 전체적으로 모든 것을 이해하여 작업하는 사람 보다는, 주어진 인증서 체계를 간신히 서버에 적용하여 돌아가는 수준으로 관리하는 경우가 흔하기 때문이다. 개별 소프트웨어들은 또 그들대로 이 복잡한 인증체계를 나름의 방식으로 소화하고 있어서 더욱더 그렇다. 자바가 다르고 윈도우가 다른 식이다. 이런 것들이 교차해서 문제가 생기는 상황에 맞닥뜨리면 그래서 이질적인 것들이 서로 더 혼란을 주게 되는 상황도 종종있다.

 

 그러나 여기에 얽힌 전체 관계를 이해하면, 이로 인해서 발생하는 다양한 오류들에 대한 대처가 훨씬 쉬워진다. 그리고 개발과정에서도 사실은 수시로 만나는 이 SSL 오류를 어떻게 이해할지 감을 잡을 수 있다.

 

1) 왜 우리는 SSL 인증서를 서버에 설치해야 하고, 실제 이것이 어떻게 사용되는가?

 꼭 기억하자. 인증서는 해당 기관의 간단한 정보(이름 등)와 공개키를 담고 있다는 점이 핵심이다. 그리고 이 내용 전체를 요약변환(hashing)한 해쉬값을 '신뢰받는 기관(Certificate Authority)'이 가진 개인키로 암호화 해준 값(서명값, Signature)을 갖고 있다.

 

 그리고 이 서명값이 제대로 검증되지 않으면, 이 인증서와 그 인증서 안의 공개키는 쓸모없게 된다. 믿을 수 없는 값이기 때문이다. 이 말이 어렴풋이라도 이해가 가지 않으면, 일단 공개키 기반의 암호체계를 이해하는 것이 먼저다. 하지만 이 주제는 다른 글에 맡긴다. 뒤의 블로그를 숙지하기 바란다.

https://soul0.tistory.com/372

 

SSL 용어정리 및 공개키 개인키 그리고 인증서 원리와 통신방식

SSL 용어정리 및 공개키 개인키 그리고 인증서 원리와 통신방식 SSL 통신 SSL이란 무엇일까? 궁금했습니다. 공개키와 개인키도 궁금했습니다. 도데체 하는일이 뭐고, 뭐가 개인키고 뭐가 대칭키고,

soul0.tistory.com

https://m.blog.naver.com/alice_k106/221468341565

 

154. [Security] SSL과 인증서 구조 이해하기 : CA (Certificate Authority) 를 중심으로

이번 포스트에서는 인증서의 구조와 동작 원리에 대해 알아보고, 이것이 실제 SSL 기반의 보안 연결에서...

blog.naver.com

 

 아무튼 결론적으로 해당 기관의 공개키를 담고 있다는 나의 서버 인증서와 함께, 이를 인증해준(발급해준) 상위기관의 인증서도 함께 서버에 설치해야만 클라이언트와 SSL통신을 할 수 있다.

 

 아니 뭐라고? 내 인증서도 버거운데 무슨 상위기관의 인증서도 같이 설치하란 말인가?

 

 일단 여기서 표현하는 그 인증서 셋트를 살펴보자. 소위 서버 인증서(server certificate), 체인 인증서(chain certificate), 루트 인증서(root certificate)라고 불린다. 사실 이렇게 나란히 있을 필요도 없고 루트 인증서가 바로 서버 인증서를 만들어 줄 수 있기도 한데, 하다보니 사실 이렇게 구성되기 쉽게 된것이다. 일단은 크롬 브라우저에서 네이버에 접속한 후에 주소 주변의 자물쇠 버튼을 눌러 이 인증서 셋트를 살펴보자.

naver 접속 후 바로옆 자물쇠 버튼을 누른후 인증서를 눌러보면 해당 인증서의 추가 정보를 볼 수 있다.

 

그러면 아래와 같이 이 사이트가 내려준 인증서, 중간 인증서, 최상위 인증서를 트리 모양으로 차례로 확인할 수 있다.

현재 네이버 인증서의 상위 인증 기관들과 최상위 인증 기관의 전체 인증서를 볼 수 있다

 네이버와 SSL 통신을 하려면, 보내고 싶은 내용을 네이버의 공개키를 가지고 암호화해서 네이버에 보내야 한다. (그리고 네이버는 이 암호문을 확인하기 위해서 자신의 개인키로 이 암호를 복호화해서 쓴다.) 이때 이 필요한 공개키는 저 맨 하단의 "*.www.naver.com" 이라는 인증서에 포함되어 있는 것이다. 익히 알고 있는대로 네이버 서버가 자신의 공개키를 공개하고 있는 셈이다. (그리고 사실을 얘기하자면, 모든 통신 내용을 이 공개키 방식으로 암호화하는건 아니다. 공개키 암호화는 연산량이 많기 때문에 기존의 오래된 암호화 방식인 대칭키만 이 공개키 암호화로 보낸다. 이후에는 대칭키로 암호화하여 통신한다. 여하튼 이렇게 하면 아무리 도청해도 키를 알아낼 수가 없다.)

 

 그러나 이 공개키의 무변조에 대한 정당성 여부는 상위 기관의 공개키를 가지고 검증할 수 있다. 무슨 소리냐면, 상위 기관의 개인키를 가지고 서명값(signature)이 생성되어 있으므로 상위 기관의 공개키로 복호화해서 비교해보면 되기 때문이다.  위 소개한 블로그 글에서 자세히 나온다.

 

 그런데 그 상위기관의 공개키의 정당성을 알려면 같은 이유로 또 그 상위 기관의 인증서를 찾아가야한다. 이렇게 순식간에 Root CA까지 올라간다. (이것이 그 유명한, 빠져나갈 수 없는 인증 체계의 강력함이다. 이런 상호 확인 방법은 깰 수 없으며 소위 Chain of Trust 라고 불린다.)

 

 그런데 Root CA는 누가 인증을 하는가? 더이상 위가 없기 때문에 바로 자기 자신이 한다(self signed). 이 최상위 루트 인증서는 자신의 개인키로 자신의 인증서의 해쉬값을 암호화해서 최종 인증서를 만든다. 그리고 이 인증서를 인정하게 만드는 방법은 바로 이 최종의 Root 인증서를 OS 회사, 브라우저 회사 등 클라이언트 역할을 하는 모든 소프트웨어 회사에 제공해서 기본 탑재하게 하는 것이다. 물론 여기에는 Java도 포함된다. Java도 범용 OS상에서 SSL통신을 하기 때문이다. 이렇게 함으로써 최종 인증기관이 신뢰받는 인증기관 임을 증명한다. 

 

 그래서 VeriSign이나 위의 Sectigo같은 회사들은 MicroSoft나 Oracle, Google같은 회사에 자신들의 Root CA 인증서를 제공해서 미리 탑재시킨다. 여기에 모든 인증서 발급 비지니스의 비밀이 숨어있다. 이렇게 수많은 회사와 협약을 맺어 클라이언트에 신뢰받는 인증서로 등록되면서, 다른 사이트들의 인증서를 발급할 수 있게 된 것이다.

 

 이 미리 설치된 인증서들이 우리가 자주 접하는 그 단어인, 소위 신뢰받는 인증서, 영어로는 trusted certificates라고 하는데, 설명한대로 실제로 클라이언트들에서 미리 직접 깔려있는 것을 확인할 수 있다. 크롬에서 그 신뢰받는 인증서들을 보려면   "설정->개인정보 및 보안->더보기->인증서 관리" 를 선택하자 (익스플로러도 인터넷 옵션->설정->내용->인증서에서 확인할 수 있다.)

 

크롬의 신뢰받는 인증서들을 살펴보자
크롬에 미리 배포 설치된 중간 인증 기관과 루트 인증 기관을 볼 수 있다

 상기의 인증서 들은 모두 사용자가 수기로 새로 추가하거나 제거할 수도 있다. 사용자의 마음이다. 그리고 이 신뢰받는 인증서들은 윈도우 업데이트시에도 마이크로소프트사에서 강제로 새로 추가할 수 있다. 삭제도 된다. 모든 것은 사용자가 조절할 수 있으나 대부분 건드리지 않는다. 

 

 이제 폐쇄망 이야기를 해보자. 위의 각 클라이언트에 미리 설치된 신뢰받는 인증서들은 처음에 대개 약 20년 정도 만료 기간으로 발급되어 설치되는데, 폐쇄망 내부에 오랫동안 OS 업데이트를 안하면 만료가 얼마남지 않은 인증서들이 기어코 만료가 되는 불상사가 벌어질 수 있다. 이미 초기 설치될때도 한 10년밖에 안남았는데, 10년이 훌쩍 지났는데 아무도 업데이트를 안한 것이다. 폐쇄망의 PC들이나 기기들이 인증서가 낡아질 수 있는 이유이다. 그래서 어쩌다보면 이 인증서를 수동으로 업그레이드해줘야 할 필요가 있을 수 있다. 윈도우의 경우에는 중앙에서 누군가 별도 수기 관리해주기도 하지만 다른 클라이언트(자바같은) 들은 개발자가 직접하지 않으면 안되는 경우도 많다. (그렇다 자바는 윈도우와는 따로 자신들만의 신뢰받는 인증서를 관리한다. 여기서 이런 녀석들을 그냥 클라이언트라고 통칭해보자.)

 

 클라이언트가 인증서를 검증해나가는 과정 중에 Root CA 인증서가 없거나 만료되거나, Chain 인증서를 찾아가는 중에 역시 없거나 만료된 인증서를 만나면 우리가 급작스럽게 흔히 만나는 "신뢰받지 못한 사이트" 경고를 받게된다. 서버는 멀쩡한 인증서를 갖고 있는데 클라이언트의 Root인증서가 갱신되지 않아 이럴 수가 있는 것이다. (이때 또한 다행히도 중간 Chain 인증서가 PC에 신뢰받는 인증서로 등록되어 있으면 대개 더이상 그 상위의 인증서를 검증하지는 않는다. 그래서 인증서에 문제가 생겼으면 어느 인증서가 만료가 되었는지 등 확인이 필요하다. 모두 서버 잘못은 아니다.)

신뢰받지 못하는 사이트 오류의 대표작

 그런데 아까 이야기한 꼭 공인된 기관이 아니더라도, 인증기관 역할은 사실은 누구나 할 수 있다. openssl등 잘 알려진 도구로 인증서도 얼마든지 self signed된 최종 Root CA 인증서를 자체 발급할 수 있는 것이다. 하지만 문제는 이렇게 만든 최상위 인증서를 내 서버에 접속하는 모든 사용자 PC(나 클라이언트 기기)에 일부러 가져다가 신뢰받는 인증서로 한대 한대 등록해야 하는것이 함정이다.

 

 만약에 수기로 모든 클라이언트에 내 사설 Root CA 인증서를 신뢰받는 인증서로 설치할 수 있다면 구태여 몇백만원씩 주고 공인된 기관으로부터 인증서를 사지 않아도 된다. 따라서 제한된 네트워크 하에서 특정 그룹의 PC만으로 사용 중이라면, 조금의 수고스러움을 각오하고 사설인증서로 운영하는 것도 방법인데, 공인 인증 기관들이 최대 3년을 기한으로 발급해주는 것과 다르게 사설은 내 마음대로 인증 기간도 수십년으로 늘려서 발급할 수 있는 장점도 있다.

 

 하지만 밝힌대로 모든 클라이언트에 그 인증서를 설치하는게 매우 번거로운 일이 아닐 수 없다. 언제 어떻게 클라이언트가 새로 추가될지 알 수 없다. 새 PC를 사가지고 오면 어찌할것인가. 그리고 클라이언트는 브라우저만 있는 것도 아니다. 누군가는 맥이나 GUI 리눅스로 접속할 지도 모른다. 그러면 여지없이 이 보안 인증서 경고를 만나게 된다. 그 클라이언트의 신뢰받는 인증서에는 사설 인증서가 있을리 만무하기 때문이다. 그래서 서버 관리자는 공인된 인증서를 구매하고자 하는 욕구에 시달릴 수 있다. 클라이언트들 관리가 힘들기 때문이다. 따라서 서버/클라이언트를 완전히 통제할 수 있는 것이 아니면 어쩔 수 없이 몇십만원씩 주고 이 인증서를 공인기관에 의뢰해서 받게된다.

 

 

 이제 조금더 진도(인증서 발급 등)를 나가기 전에 PC client를 중심으로 살펴보았던 것을 java로도 살펴보자. 기본적으로 PC와 다를바 없지만 약간 다른 체계를 갖고 있고 SSL을 쓰는 모든 언어의 실행기들도 마찬가지다. 그것들은 앞서 밝힌대로 대부분 제각기 신뢰받는 인증서 목록을 자체에 갖고 있다. java는 현대의 가장 널리 사용되는 프로그래밍 언어 중에 하나이기 때문에 인증서를 다루다보면 반드시 java 인증서 문제를 만나게 된다. (python은 python의 그것을 만나게 된다)

 

2) java client에서 인증서 확인하기

 

 java는 역시 그 성격에 걸맞게 윈도우 OS와는 별도의 인증서들을 보유하고 있다. java는 OS를 가리지 않는 범용이지 않는가. 윈도우에 의존할 필요가 없이 독립된 체계를 갖고 있다(이렇게 OS무관하게 돌아가는 많은 언어들이 유사하다.)

그리고 자바가 가진 체계는 바로 jks(java key store)라는 인증서 정보 저장 파일 형식을 통해서 이루어진다.

 

 아래화면은 ./jre/lib/security/cacerts라는 java 기본 jks파일의 정보를 keytool(자바 툴 중 하나다)을 사용해 확인해본 것이다. keytool 사용법은 구글링하면 다양하게 참조할 수 있다. 이 jks파일에 포함된 인증서의 목록을 열람하거나 신규 인증서를 추가(import)하고, 있던 인증서도 꺼낼 수(export) 있다. 아까 브라우저에서 보았던 것들의 자바 버전이라고 보면 된다.

 

java에 포함된 keytool을 통해 cacerts에 등록된 신뢰받는 인증서들을 살펴보자. 기본 암호는 없이 엔터만 치면된다

 java key store라는 포맷의 파일은 나름 암호를 입력해서 접근이 통제되도록 설계되어 있다. jks에는 신뢰받는 인증서 외에도 개인키 등을 저장할 수 있도록 되어 있기 때문이다. 그렇다 jks는 클라이언트말고도 서버용으로 작동하기 위한 모든 기능을 다 갖고 있어야 하기 때문에 이렇게 개인키도 같이 저장될 수 있게 설계된 것이다.

 

 인증서는 공개되는 정보라 암호로 보호되지 않아도 상관없으나 개인키는 전혀 그렇지 않다. 가끔씩 인증서를 다루다보면, 이 암호를 묻는 경우가 종종 나와서 왜 이렇게 처리하는지 헷갈릴 수 있는데 요점은 간단하다. 인증서 세계에서는 개인키와 연관되어 저장되면 무조건 암호를 걸 수 있도록 제공하는게 관례이다(혹은 필수이다).

 

 왜냐하면 개인키란 상대편이 내 공개키로 암호화해서 보낸 정보를 풀 수 있는 키이므로 절대 공개되어서는 안되는 계정/암호 같은 것이기 때문이다. 암호화지 않은 개인키는 평문으로 공개된 내 계정의 암호와 같다. 제 3자가 탈취/유출하면 해킹될 각오를 해야 한다. 그래서 암호화하여 보관하는 것이 관례이고 필수인 것이다. 우리가 은행의 공인인증서를 별도 파일로 저장할때(주로 pfx) 암호를 묻는 이유이다. 거기에는 내 인증서 뿐만 아니라 내 개인키까지 같이 들어있다.

 

 

 상기 자바의 keytool을 사용해, 언급했듯이 jks 파일 안에 보관된 특정 인증서를 import하거나 확인 할 수 있다. 개별 인증서의 alias나 인증서 이름은 바뀔 수 있으나 상기 지문(fingerprint - 인증서 hash값)이라는 것을 통해 해당 인증서의 동일성을 확인/대사 할 수 있으니 참조하자. 그리고 java의 jks는 다양한 파일을 지정해 사용할 수 있으나, 따로 명기하지 않으면 위의 cacerts라는 파일이 기본으로 사용된다(\jre\lib\security 폴더에 있다)

 

 결국 이 java가 SSL 통신하는데 사용하는 서버의 최상위 인증서들은 모두 이 cacerts에 잘 등록되어 있어야 "신뢰받지 않는 인증서 오류를" 피할 수 있다. 당신이 혹시 SSL certificate warning 오류를 만났다면 바로 이 등록 관계가 잘못되어 있기 때문에 발생하는 오류이다. 어디선가 인증서가 지워졌거나 만료되어 있거나 미리 심어져 있지 못한 것이다. 이럴때는 이 jks파일에 keytool을 이용해 필요한 인증서를 모두 추가해주면 된다. 인증서 추가시는 해당 인증서를 서명한 인증서를 먼저 추가해주도록 한다. 인증서가 추가될때마다 유효한지 내부 자체 검증을 하기 때문이다. 예를 들어 Root 인증서->Chain 인증서->서버 인증서 순서이다.

 

3) 인증서를 등록하는 과정에서 만나는 파일들, pem 이나 cert, jks 등 이야기

 

 인증서를 다루는 포맷은 대단히 복잡한데, 이것만 기억하면 된다.

 

 텍스트로 된 인증서 관련 정보는 모두 'pem(Privacy Enhanced Mail)'이라는 형식이다. 저장할때 확장자는 상관없다. 텍스트로 된 pem은 대략 이렇게 생겼다.

 

pem 포맷, 인터넷에서 발췌했다
pem은 이렇게 몇개 정보가 연이어 합쳐져있을 수도 있다

맨 상위의 BEGIN XXXXXXXXXX 가 바로 이 pem 포맷으로 된 정보의 실제 종류를 가리키는데 위의 CERTIFICATE는 인증서를 뜻한다. 인증서를 텍스트로 변환(구체적으로는 base64방식)하여 담고 있는 것이다.

 

이 pem형식의 각 상단 해더 종류는 아래와 같은데, 이 중 일반 엔지니어가 만날일이 있는 녀석은 매우 소수로 제한되어 있다.

pem의 해더들

PRIVATE_KEY (개인키), RSA PRIVATE KEY(RSA를 위한 개인키-사실상 개인키와 같다), CERTIFICATE(인증서), CERTIFICATE REQUEST 혹은 NEW CERTIFICATE REQUEST(인증서 요청서) 가 그것이다.

 

 여기서 잠깐 더 나아가기 전에 이 "인증서 요청서(CSR)"를 살펴보자. 인증서를 발급받는 사람만 다루게 되는 이 인증서 요청서는 신기하게도, 인증서를 요청하는 요청서이다. 아니 인증서를 요청하는 문서는 따로 텍스트로 보내면 되지 이것이 과연 왜 여기 공식 포맷 리스트에 끼어있을까?

 

 사실은 이 녀석도 공개키 기반 시스템의 중요한 핵심 포맷중의 하나이기 때문이다. 공개키 기반 암호체계에서 인증서를 받으려면 먼저 개인키와 공개키를 내가 생성한 후에, 개인키는 숨기고 인증기관에 내 일반 정보와 공개키를 보내야 한다. 바로 이때 사용하는 인증서 요청서 포맷이 바로 이 CERTIFICATE REQUEST 이다. 이 안에는 내 기관의 정보와 공개키가 정의되어 들어있다. 즉 개인키/공개키를 요청자가 미리 생성하고, 공개키만 보내도록 강제하는 포맷이다. 왜냐하면 개인키가 공개되면 암호 체계가 깨지기 때문이다.

 

 이 과정을 잘 이해하지 못하는 경험없는 담당자들은 소비자로서 서비스 제공사에 인증서를 발급받는 절차를 진행할때 당혹스러워하는데, 자꾸 인증서 발급 서비스를 제공해야할 파트너인 인증서 업체에서, 이 인증서 요청서(CSR)를 내게 만들어 달라고 요구하기 때문이다. "아니 인증서 요청서(CSR)도 자신들이 다 만들어주면 될것을 뭘 따로 고객한테 달라고 하나?" 라고 생각할 수 있지만, 개인키는 누구에게도 공유해서는 안되는 것을 생각해보면, 고객이라도 반드시 이 인증서 요청서를 생성하고 개인키는 숨긴 상태에서 공개키만 보내 인증서를 발급받아야 하므로 필연적인 절차이다.

 

 그래서 인증서 생성 업체의 담당자들은 이 사정을 모르는 고객이 자꾸 인증서 요청서를 알아서 만들어 달라고 하면 황당할 수 있겠다(만 실제로 그렇게 친절하게 만들어서 개인키도 같이 주기도 한다. 어쩔 수 없겠다. 그 중요한 개인키가 이렇게 되면 인증서 생성 업체 담당자가 갖고 있게 된다. 나중에 다시 요구될 것을 대비해 계속 보관까지 하고 있어야 하는 당혹스러운 상황이된다. 고객님 암호가 제게 있어요! 같은 상황이다.)

 

 인증서를 발급받아야하는 담당자는 따라서 공개키/개인키를 만들어서 인증서 요청서를 만들어야 한다는 사실을 이해하고 이 작업을 수행 할 수 있는 사람이 필요하다.

 

 

 다시 본 이야기로 돌아가보자.

 

이 pem형식의 인증서가 *.crt(certificate) 라는 형태로 저장되어 있을때도 있고, 인증서 요청서가 *.csr(certificate signing request)이라고 저장되어 있을때도 있다. 또한 이 pem형식은, 개인키를 담고 있을때는 앞서 밝힌 대로 암호화 되어있을 수 있다. 그런데 암호화되어 있다고 해도 따로 바이너리로 저장되는게 아니라, 예컨데, PRIVATE_KEY라고 역시 헤더는 같이 되어있고 그 다음줄에 암호화 방식이 명기되어 있으면 이것은 암호화된 개인키이며, 접근하려면 암호를 요구받게 된다. 암호화 방식이 명기되어 있지 않으면 암호화되지 않은 개인키이다.

암호화된 개인키 예시다. 해더 바로 밑에 암호화 방식이 명기되어 있다.

기타 pfx같은 포맷의 형태들은 모두 binary이다(메모장에서 봐도 읽을 수 없는 문자들만 들어있다). 이녀석들은 개인키를 보관할때는 무조건 암호화하기 때문에, 처리될때 암호가 필수적으로 요구된다. 앞서 밝힌대로 우리가 금융 거래용 공인인증서를 export/import 할때 계속 암호를 묻는 것도 그 이유이다(그 안에 개인키가 있다). 자세히 보면 이럴때는 이 pfx포맷으로 주로 처리한다.

 

 마지막으로 jks는 앞서 설명했던 java key store파일 형식이다. 해당 파일 안에는 앞서 밝힌 대로 신뢰받는 인증서나 개인키 등을 모두 담아둘 수 있다. java와 강하게 연계된 서버(tomcat같은)들은 이 jks형태로도 인증서를 읽어와 처리하도록 설정할 수 있다. java 가족이겠다.

 

4) 자 이제 서버에 이 인증서를 등록해보자. 이때 서버는 무엇을 요구하는가?

 

 클라이언트가 SSL을 통신을 위해 서버에 접속하면 서버는 자신에게 등록된 인증서와 기타 상위 인증서들을 내려준다.

 

가만히 생각해보면 이때 클라이언트가 필요한 인증서는 '서버 인증서', '서버 인증서의 상위 체인 인증서들'이고 최상위 인증서는 클라이언트가 갖고 있어야 하는게 정상이다. 어차피 클라이언트가 갖고 있지 않은 최상위 인증서는 내려줘봐야 인정받지도 못하기 때문이다.

 

 클라이언트가 혹시 운좋게도 내 인증서(서버 인증서) 바로 상위의 체인 인증서를 신뢰받는 인증서로 등록해서 갖고 있으면, 사실 내 고유의 인증서만 내려줘도 상관없으나, 대부분은 chain CA 인증서는 클라이언트에 없다. Root CA인증서만 PC에 갖고 있다. 최상의 인증기관의 인증서가 장사가 되는 이유이다. (개인키로 암호화 해주는것 하나로 이렇게 장사를 할 수 있다니 놀라울 따름이다. 가격은 왜 또 그렇게 비싼지. 그래서 Google같은 곳에서 무료로 인증서를 발급해주는 Let's Encrypt를 지원한다.)

 

 그리고 또 하나 앞서 밝힌대로 주의할 것은 java key store는 인증서를 등록할 때마다 그 상위 인증서로 검증을 하기 때문에, Root CA 인증서부터 차례로 인증서가 등록되어야 하는 특징이 있다.

 

 

 이런저런 정황으로 서버에 설치 및 등록할 인증서는 대개 내 인증서에서부터 최상위 Root CA인증서까지 모두 포함하는게 인지상정이다. 구태여 따지자면 Root CA 인증서는 필요 없을때도 있고, 클라이언트 따라서 일부 Chain CA 인증서도 필요 없을 수 있다는 점만 알아두면 된다. (나중에 오류가 발생했을때 원인 판단을 위해 이런 상식적인 상황 이해가 중요하다.)

 

 그리고 또 하나 그동안 숨겨왔던 내 개인키(서버 개인키)가 필요하다. 개인키를 서버에 등록해줘야 클라이언트가 내서버의 공개키로 암호화해서 보내온 내용을 해독할 수 있기 때문이다. 일반적으로 통신 내용 전체를 암호화해서 보내는게 아니라 암호키만 이 공개키방식으로 통신하고 나머지는 그냥 암호키를 대칭키로 하여 암호화 통신한다는 건 또 하나의 팁 지식이다.

 

 

 이제 이 내용을 정리해보자. pem포맷으로 따져보자면, 서버에 등록할 인증서 정보는 아래처럼 긴 리스트가 필요하다.

 

-------------BEGIN CERTIFICATE------------------      (Root CA인증서)

XXX

-------------END CERTIFICATE------------------

-------------BEGIN CERTIFICATE------------------      (Chain CA #1 인증서)

XXX

-------------END CERTIFICATE------------------

...

-------------BEGIN CERTIFICATE------------------        (Chain CA #2 인증서 - 상황에 따라서 없거나 더있을 수 있다)

XXX

-------------END CERTIFICATE------------------

-------------BEGIN CERTIFICATE------------------      (내 인증서)

XXX

-------------END CERTIFICATE------------------

-------------BEGIN PRIVATE KEY------------------        (개인키, 암호화되어 있을 수도 없을 수도 있다)

XXX

-------------END PRIVATE KEY------------------

 

이것이 온전히 등록되어야 비로소 서버 인증서 등록이 마무리 되고, 여기에 사용된 Root CA 인증서가 클라이언트에 잘 심어져있어야 상호간의 공개키 기반 SSL통신이 완벽하게 이루어지게 된다. 이것이 바로 완벽한 상태이다.

 

 

이 인증서 등록에 있어서 어떤 서버는 다 합쳐서 올리기도 하고, 어떤 서버는 조각조각(Chain인증서, RootCA인증서, 개인서버 인증서, 개인키) 올리기도 한다. 개인키만 따로 올리기도 하고 jks로 다 합쳐서 올리기도 한다. 개인키는 암호화 유무에 따라 암호를 따로 입력해 등록해야할 수도 있다.

 

 jks는 관리 암호가 전체 하나니 jks 암호만 등록하면 개인키는 없어도 되는 경우도 있다. 그래서 대체 어디 암호를 어떻게 등록해야하는가는 상황에 따라 다르다. (개인키를 암호화해서 저장하지 않은 경우도 있는데, 이러면 암호를 요구하지 않을 수도 있다)

 

 하지만 결국 이런 것들의 조합이라는 점은 동일하다. 이는 상기 설명한 지식을 가지고 각 서버의 등록 가이드를 참조로 하면 손쉽게 대응할 수 있다.

 

 이제 적용을 했다고 해보자. 좀 아쉬운 것이 서버에 이 인증서 전체가 잘 등록되어 있는지 완전히 확인할 수 있는 일반적인 방법을 사실은 애매하다는 것이다. 왜 애매한가 그리고 방법은 없을까? 다음으로 넘어가보자.

 

5) 서버 인증서가 잘 등록 되었는지 확인하는 방법과 응용

 

 서버에 접속하면 클라이언트에 어떤 인증서 정보를 내려보내주는지 궁금하지 않는가? https용으로 인증서를 등록한 경우에는 PC 브라우저를 통해 확인하는 방법이 있는데 여기에는 함정이 있다.

 

 어차피 브라우저는 서버가 내려주는 인증서 중에 자기 쪽에 chain CA 인증서나 Root CA 인증서 중 하나만 매칭되면 서버가 내려주는 다른 인증서들을 무시하기 때문에, 브라우저에 표시된 인증서 chain이 서버에 있는 것과 같으리라는 보장이 없다. 이 때는 브라우저의 신뢰받는 인증서들을 적당히 삭제하다보면, 실제 서버에서 내려주는 인증서를 유추해 찾아낼 수 있을 때도 있다. (만료 일자만 다르고 발급 기관은 동일한 두개의 상위 인증서가 있을 수도 있어서 이게 좀 헷갈리는 경우가 있다.)

 

 그래서 이때 제일 확실한 인증서 설치 적합성 시험에 대한 방법은 openssl 툴을 쓰는 것이다. 실제 서버가 내려주고 있는 인증서 정보들을 확인할 수 있다. 모든 SSL에 대해서 작동한다.

https://langui.sh/2009/03/14/checking-a-remote-certificate-chain-with-openssl/ 발췌

 이 인증서 등록 과정에서 chain CA 인증서를 빼먹고 등록하지 않은 것으로 판명되었다면 어떤 일이 벌어질까? chain CA 인증서를 이미 클라이언트에 강제로 심은 클라이언트는 문제없이 통신되는데, 그렇지 않은 클라이언트에서는 오류가 발생한다. 대개 인증서 설치의 전체 과정에 대한 이해가 없이 이런저런 시행착오 끝에 설치를 완료하다보면 흔히 발생하는 문제이다. 구글링 검색해서 이것저것 해봤다가 간신히 작동하게 되었는데, 상황이 변화면 오류가 생기게 된다. 따라서 문제가 생기면 아래 절차가 필수이다.

 

   A. 우선 서버의 인증서가 제대로 설치되었는지 확인한다. 상기 openssl s_client 명령을 참고한다.

       인증서 체인이 잘 연결되어 있어야 한다. 중간에 빠지거나 이상하게 되어있으면 안된다.

   B. 그 다음은 클라이언트의 신뢰받는 인증서를 확인한다. fingerprint등을 잘 비교하면 일치 여부를 확인할 수 있다.

       (브라우저의 인증서에서는 '자세히' 버튼으로 fingerprint(지문) 확인이 가능하다)

   C. 혼란을 유발할 수 있는 불필요한 과잉 인증서는 제거해두는 편이 향후를 위해 더 좋다. 내 PC에서는 되는데 다른 사람 PC에서는 안되는 경우를 방지할 수 있다.

 

 브라우저에 기 등록된 인증서를 파일로 따로 백업해두고 싶은 경우는 crt형식(위 pem파일 포맷의 인증서)으로 export하면 되며, 나중에 따로 해당 파일을 더블클릭하여 설치하면 간단하게 인증서 저장소에 다시 추가할 수 있으므로 PC 인증서 삭제를 너무 무서워하지 않아도 된다.

 

 오늘은 여기서 설명을 마치도록 한다. 신뢰받지 않은 인증서 오류를 만나지 않도록 이 문제로 고생하는 많은 엔지니어 분들에게 건투를 빈다.

 

 

 

 

반응형