사용자가 은행 웹사이트에 접속하여 아이디와 비밀번호를 입력하여 로그인을 할 때, 일반 텍스트 형식으로 전송된다. 따라서 해커가 네트워크 트래픽을 탐지하면 아이디와 비밀번호를 쉽게 추출해 사용자의 은행 계좌를 해킹할 수 있다.
그래서 보통은 전송되는 데이터를 임의의 숫자와 알파벳으로 이루어진 shared key(or symmetric key)라는 것으로 암호화한다.
데이터에 임의의 숫자와 알파벳을 추가하고 인식할 수 없는 포맷으로 암호화하는 것이다.
이제 데이터를 서버로 보내면 탐지하고 있던 해커가 데이터를 얻지만 아무것도 할 수 없다. 마찬가지로 서버도 데이터를 받았지만 아무것도 알 수 없다.
앞서 이용했던 shared key 없이는 데이터를 해독할 수 없으니 shared key의 복사본도 서버로 보내서 서버가 메시지를 해독하고 읽을 수 있게 해야 한다. 하지만 shared key도 같이 네트워크로 전송되면 해커도 탐지하여 데이터를 해독할 수 있다.
지금까지 살펴본 방식을 대칭 암호화(Symmetric Encryption)라고 부른다. 암호화 방식 중 하나이고, 암호화한다는 점에서 안전하지만, 데이터를 받는 쪽에서도 같은 shared key를 사용해야 하기 때문에 해커가 shared key에 알고 데이터를 해독할 위험이 있다.
그래서 나온 것이 비대칭 암호화이다.
비대칭 암호화 방식은 public key와 private key를 사용한다. 때로 public key를 public lock으로 표현하기도 한다.
이제 public key와 private key을 사용해보자.
먼저, SSH 접속을 보안하는 사례를 살펴보자. 우리는 터미널에서 아래의 명령어를 이용하여 public key와 private key를 만들 수 있다.
$ ssh-keygen
$ ls
id_rsa id_rsa.pub
id_rsa
는 private key이고, id_rsa.pub
은 public key이다.
마무리로 서버에 /.ssh/authorized_keys
경로에 public key를 추가한다. 이제 private key를 통해 SSH로 서버에 접속할 때, 서버에 있는 public key를 통해 접속할 수 있다.
다시 은행 웹사이트 사례로 가보자. 먼저 서버에서 public key와 private key를 만든다.
# Create private key
$ openssl genrsa -out my-bank.key 1024
# Create public key
$ openssl rsa my-bank.key -pubout > my-bank.pem
이후에 사용자가 HTTPS를 통해 웹 서버에 처음 접속하면 서버에서 public key를 보낼 것이다. 당연히 해커는 모든 트래픽을 탐지하고 있어서 public key의 복사본을 가지고 있다.
사용자의 브라우저는 서버에서 제공된 public key를 이용하여 사용자의 shared key를 암호화한다. 그리고 암호화된 shared key를 서버로 보낸다.
최종적으로 해커도 서버와 똑같이 서버의 public key와 서버의 public key로 암호화된 shared key를 가지고 있다.
서버는 서버의 private key로 메시지를 해독하고 shared key를 얻는다. 반대로 해커는 서버의 public key로 암호화된 shared key를 풀 수 있는 서버의 private key가 없기 때문에 메시지에서 얻을 수 있는 shared key를 얻을 수 없다.
이제 shared key를 이용하여 암호화해서 서버로 보내면 서버에서 암호화된 shared key를 해독하여 사용할 수 있다.
다시 해커는 사용자 계정을 해킹할 새 방법을 찾고 있다. 그래서 은행 웹사이트와 똑같은 웹사이트를 만든다. 그리고 똑같이 해커의 서버에서 public key와 private key를 만들고, 네트워크를 조정하여 은행 웹사이트로 접근하면 해커가 만든 웹사이트로 보내진다.
사용자가 웹사이트 주소에 접근하면, 해커의 서버에서 public key를 보낸다. 동일한 방식으로 그 key로 shared key를 암호화하고, 암호화된 shared key가 해커의 서버에 보내진다. 이로써 해커는 shared key를 이용하여 사용자의 정보를 빼낼 수 있다. (그리고 화면에는 해킹되었다는 표시가 뜰 수도..)
우리는 서버에서 받은 public key가 진짜 은행 서버에서 나온 key인지 어떻게 알 수 있을까? 그래서 나온 것이 서버에서 public key를 보낼 때 단지 public key를 보내는 것이 아니라 public key를 가진 인증서(Certificate)를 보내는 것이다.
인증서는 어디서 발급되었는지에 대한 정보를 갖고 있다. 해당 서버의 public key, 서버의 위치 등이 있다.
Certificate:
Data:
Serial Number: 420327018966204255
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=Kubernetes
Vaildity
Not After: June 28 13:41:28 2023 KST
Subject: CN=my-back.com
Subject Alternative Name:
DNS: myback.com, DNS: i-bank.com, DNS: we-bank.com
Subject Public Key Info:
00:b9:b0:55:24:fb:a4:ef:77:73:7c:9b
모든 인증서에는 이름이 있고, 인증서를 발급하는 사람이나 대상이 매우 중요하다. ID를 검증하도록 도와주기 때문이다.
하지만 이런 인증서는 누구나 만들 수 있다. 구글이라고 하면서 직접 생성할 수도 있다.
해커도 동일하게 인증서를 발급했다고 해보자. 인증서가 진짜인지 어떻게 확인할까? 일반적으로 인증서를 생성했다면 직접 서명해야 한다. 하지만 해커도 복사하여 가짜 서명을 할 수 있다.
그럼 사용자의 브라우저가 신뢰할 수 있는 합법적인 웹 서버 인증서를 어떻게 만들까? 그래서 나온 것이 인증서에 서명하고 유효성을 확인해주는 인증 기관(Cretificate Authority, CA)이다.
동작하는 방식은 순서대로 아래와 같다.
먼저 private key를 이용하여 Certificate Signing Request(CSR)을 생성한다.
$ openssl req -new -key my-bank.key -out my-bank.csr -subj "/C=US/ST=CA/O=MyOrg, Inc./CN=mydomain.com"
이후에 인증 기관으로 보내면 인증 기관에서 유효성 검사 후, 인증서에 서명해서 다시 보내준다.
해커도 같은 방법으로 인증서에 서명을 받으려고 하지만 인증서 유효성 검사 단계에서 실패한다.
이제 사용자의 브라우저에는 신뢰할 수 있는 CA가 서명한 인증서가 있다. 그렇다면 CA 자체가 합법적이라는 것을 사용자의 브라우저가 어떻게 알까?
인증 기관에서도 public key와 private key가 존재한다. 인증 기관은 인증서에 서명할 때 private key를 사용한다. 그리고 모든 브라우저에는 인증 기관의 public key를 이용하여 인증 기관이 직접 서명한 인증서임을 확인한다.
이제 모든 과정을 정리해보자. 메시지를 암호화하려면 public key와 private key로 비대칭 암호화를 해야 한다.
여기서 한가지 확인해야할 것은 클라이언트는 서버의 유효성을 확인할 수 있었지만, 서버는 클라이언트인지 해커인지 확실히 알 수 없다. 서버는 어떻게 확인할 수 있을까? 사용자의 브라우저에서도 public key와 private key를 만들면 된다. 본인 확인을 위해 서버와 동일하게 클라이언트도 private key를 이용하여 CSR를 생성한다. 이후에 인증 기관에 보내서 public key를 가진 서명된 인증서를 받는다. 마지막으로 받은 인증서를 서버로 보내서 클라이언트의 유효성을 확인한다.
모든 key를 정리해보자.
지금까지 살펴본 모든 인프라를 Public Key Infrastructure(PKI)라고 부른다.
끝으로, crt
와 pem
확장자에 대해 알아보자. 일반적으로 public key를 가진 인증서는 crt
이나 pem
확장자로 쓰인다. 위에서 살펴봤던 사례에서 서버와 클라이언트 모두 crt
이나 pem
확장자인 파일을 가지고 있었다.
server.crt
or server.pem
client.crt
or client.pem
그리고 private key는 key
나 pem
확장자로 쓰인다.
server.key
or server-key.pem
client.key
or client-key.pem
Private key는 항상 key
라는 단어가 붙어있다.