본문으로 바로가기

보안프로그래밍

category 개발, IT 2008. 11. 20. 11:57

홍기훈 | 최재덕 | , 김덕우 |
필자들은 숭실대학교 대학원 통신망 보안 연구실에서 멀티미디어 기반의 보안 프로토콜을 연구하고 있다. 특히 VoIP 보안 기술에 관하여 연구하고 있는데 H.323을 위한 H.235와 SIP을 위한 SIP 보안 프로토콜을 구현하고 있다.

인터넷은 이제 단순한 정보 검색이란 기능 뿐만 아닌 우리 생활의 기반이 되어 가고 있다. 그러나 인터넷이 이렇게 진화할수록 가장 문제가 되는 것은 역시 보안이다. 얼마 전 슬래머 웜에 의한 인터넷 불통 사태가 발생하였을 때 각 업계에서는 이번 사건으로 인한 매출 감소와 피해액을 걱정하였다. 그러나 인터넷 기반의 이러한 피해는 이번과 같은 대형 사건이 발생했을 때만 나타나는 것은 아니다. 평상시 많은 사용자들이 인터넷을 통해 물건을 구매하거나 혹은 회원 가입시 사용자 정보와 신용카드 번호 등을 입력하고 있으며, 실제 금전적인 가치가 있는 정보들이 이동한다. 그러나 인터넷은 공개된 네트워크로 그 안전성을 보장하지 못하는 결함을 갖고 있기에 각 응용 서비스별 보안 대책은 필수적이다. 이 글에서는 네트워크 응용 서비스 개발자들이 자신들의 프로그램에 안전성을 강화하기 위해서 어떤 방법들을 적용할 수 있는지에 대해 언급하고자 한다.

어떤 방법으로 보호할 것인가
그러면 과연 어떤 방법으로 네트워크에 전송되는 데이터를 보호할 수 있을까? 쉽게 생각할 수 있는 방법은 암호화를 통한 데이터의 보호다. 그러나 이것은 단순히 암호 알고리즘만 가지고 보호할 수 없다. 암호화를 위해서는 키가 필요하고 메시지의 변조를 막기 위해서는 또 메시지 인증이 필요하다. 이러한 여러 가지 보안적 기능을 네트워크에 적용하여 설계한 것이 바로 보안 프로토콜이다.
보안 프로토콜의 대표적인 예로 TLS(Transport Layer Security), IPSec(IP Security) 등이 있으며, 이것들은 현재 보안 표준 프로토콜들이다. 그러나 이러한 보안 프로토콜들은 안전성을 기반으로 설계되었기 때문에 네트워크 개발자들이 구현하기에는 엄청난 작업이 될 수 있으며, 여러분이 만드는 네트워크 응용 프로그램보다 더욱 복잡한 형태가 될 수 있다. 따라서 이 글에서는 보안 전문가들이 작성해 놓은 공개 보안 프로토콜을 이용하여 적용하는 방법과 간단한 보안 프로토콜을 구현해 보는 두 가지 작업을 해보도록 하겠다.

기 개발된 보안 프로토콜 사용
네트워크 보안을 위해서 보안 프로토콜이 사용되는데 이러한 보안 프로토콜은 각 표준화 단체에서 표준을 정하여 전 세계적으로 사용하도록 하고 있다. 웹 서비스를 위해서 HTTP 프로토콜, 인터넷 폰을 위해서 SIP와 H.323 프로토콜을 사용하고 이러한 네트워크 서비스에 보안을 적용하기 위해 보안 프로토콜이 사용된다. 보안 프로토콜은 커비로스, IPSec, S/MIME, PGP, SSL/TLS 등이 있는데 이러한 보안 프로토콜은 네트워크 스택 상에서 보면 <그림 1>과 같은 위치를 차지하게 된다.
커비로스는 공개된 컴퓨터 네트워크에서 서비스의 요청을 인증하기 위한 안전한 방법을 제공하는 인증 프로토콜로서, MIT에서 Athena Project로 개발되었으며 이름은 고대 그리스 신화에서 하데스의 문을 지키는 머리가 셋 달린 개의 이름을 따서 붙여졌다. IPsec은 IP payload를 암호화하여 보호하는 프로토콜로 IP를 사용하는 모든 응용 프로그램에 적용이 가능하다. S/MIME은 전자메일을 위한 보안 프로토콜로서 MIME이 MIME 자체로서 어떠한 보안 서비스를 제공하지 않기 때문에 응용 계층에서 보안을 제공하는 대표적인 프로토콜이다. S/MIME은 우편뿐만 아니라 MIME 객체를 전송하는 모든 프로토콜에서 보안을 위해 사용 가능하다. PGP는 전자우편과 파일 저장 응용에 사용할 수 있는 기밀성과 인증 서비스를 제공한다. 파일과 메시지를 암호화하는 표준화된 방법을 선택해서 사용하고자 하는 기업을 포함해서 인터넷을 통해 전세계적으로 다른 사람들과 안전하게 통신하고자 하는 개인에 이르기까지 매우 다양한 영역의 응용성을 가진다. SSL/TLS은 웹 보안 프로토콜이다. TLS는 IETF(Intern et Engineering Task Force)에서 제안된 국제 표준 WWW 보안 프로토콜이며, SSL 프로토콜을 기반으로 하고 있으나 현재까지 알려진 SSL 3.0 프로토콜의 여러 문제점을 개선하여 우수한 안전성을 갖는 보안 프로토콜이다. SSL 2.0, SSL 3.0 그리고 TLS 1.0은 상호 연동이 가능하도록 구성되어 있고 이들을 통틀어 SSL/TLS 프로토콜이라 한다.

공개된 보안 라이브러리 사용
보안 라이브러리는 공개용과 상업용으로 나누어 볼 수 있다. 공개되었다는 것은 여러 방면에서 발견되는 보안 취약점에 대해서 그만큼 보완했다는 것을 의미하기 때문에 공격의 여지가 최소한으로 줄어든다는 것을 의미하기도 한다. ‘OpenSSL 프로젝트’는 대표적인 공개용 보안 라이브러리 제공 단체이다. OpenSSL에는 여러 암호 알고리즘 및 보안 프로토콜 관련 라이브러리를 무료로 사용할 수 있다. 경제적인 측면에서 여러 네트워크 응용 프로그램에 보안을 적용하기 힘든 중소기업들에게는 이러한 공개용 라이브러리의 사용은 일석이조라 할 수 있겠다.

◆ SSL/TLS 라이브러리
? Win32 Openssl : www.shininglightpro.com/search.php? searchname=Win 32+OpenSSL
? OpenSSL Project : www.openssl.org

상업용 라이브러리 사용
네트워크 응용 개발자들은 상업용 라이브러리의 사용도 고려해 볼 수 있다. 상업용 라이브러리는 공개용 라이브러리와는 달리 사용이 쉽고 라이브러리 제공 업체로부터 여러 가지 부가적인 지원을 받을 수 있다는 장점이 있다. 그러나 상업용이기 때문에 라이선스에 따른 경제적인 부담이 추가로 필요하기 때문에 두 라이브러리의 장/단점을 잘 고려하여 선택한 후 보안을 적용시켜야 할 것이다.

자체적인 보안 대책을 마련하자
네트워크 응용 프로그램을 위한 마지막 보안 방법으로 자체적인 보안 메커니즘을 정의하고 이를 사용하는 것이다. 물론 보안에 대한 전문적인 지식 없이 보안에 관한 메커니즘을 설계하고 적용한다는 것은 일반 개발자들에게는 다소 부담이 될 수 있다. 그러나 기존의 보안 프로토콜 중에서 핵심이 되는 부분만 간략화하여 사용해도 하나의 네트워크 응용 서비스에는 손색이 없는 보안 프로토콜이 될 수 있다. 이러한 방법은 앞에서 설명한 기 개발된 보안 프로토콜보다 모듈의 크기 면에서 커다란 도움이 될 수 있을 것이다. 예를 들어, 클라이언트에서 동작하는 프로그램이 웹을 기반으로 서버에서 클라이언트로 다운로드되어 실행되는 경우, 보안 모듈은 보호 대상인 네트워크 응용 프로그램보다 작아야 할 것이다. 그러나 보안 프로토콜은 여러 암호 알고리즘과 보안 협상 소스를 포함하고 있기 때문에 상당히 커다란 코드가 되며 오히려 보안을 적용할 네트워크 응용 프로그램보다 몇 배의 크기가 될 수도 있다.
또 한 가지 장점은 보안 초기화 시간을 줄일 수 있다는 것이다. 보안 프로토콜은 키 교환, 보안 협상(Security negotiation) 및 인증서 교환 등 많은 초기화 작업을 해 주어야 한다. 이러한 초기화 시간은 고객들에게 서비스 초기에 기다려야 하는 불편함을 야기하고, 특히 네트워크가 느리거나 서버가 많은 세션을 처리하고 있는 경우 지연 시간이 더욱 증가할 수 있다. 그러나 자체적인 보안을 사용하는 경우, 네트워크 응용 프로그램이 서비스 연결 요청을 주고받는 동안 키 교환이 수행될 수 있다. 물론 자체적인 보안 대책의 안전성은 기존의 표준 보안 프로토콜에 비해 떨어지는 것은 당연한 것이다.

공개 보안 라이브러리를 이용한 전송 데이터 보호
OpenSSL 프로젝트는 SSLeay에서 유래(Eric, A, Young)한 SSL v2/v3, TLS v1 프로토콜과 여러 암호 알고리즘을 구현한 공개된 라이브러리이다. SSLeay는 원래 SSL을 구현하기 위한 목적으로 설계되어 일반적인 암호화 기능 구현에는 잘 맞지 않는 경향이 있었으나 OpenSSL 그룹에서 꾸준한 업데이트를 통해 많은 기능이 추가되었다.

공개 프로젝트 소개 및 SSL 기술 소개
OpenSSL 프로젝트는 CA 관련 프로그램뿐만 아니라 S/MIME 유틸리티 기능도 제공하며 지속적으로 업데이트되고 있다. 현재 나와 있는 최신 버전은 지난 2002년 12월에 발표된 버전 0.9.7이다. OpenSSL 프로젝트에서 제공되는 암호 알고리즘 및 S/MIME을 적용하여 보안 채팅을 구현할 수도 있지만 이 글에서는 TLSv1을 사용한 간단한 보안 채팅 프로그램을 구현해 보기로 하자.
SSL/TLS 프로토콜은 클라이언트와 서버 사이에 인증 및 암호화 통신을 위해 사용되는 보안 프로토콜이다. SSL/TLS 프로토콜은 TCP 프로토콜 위에 위치하고 있으며, <그림 2>와 같이 HTTP, Telnet, FTP, SIP 등의 프로토콜 하단에 위치하여 SSL/TLS 프로토콜은 TCP를 이용하는 모든 종류의 응용에 적용이 가능하다. 예로 현재 우리가 사용하는 웹 브라우저는 SSL/TLS 프로토콜이 지원되며 이를 이용하여 전자상거래에 많이 이용되고 있다.
SSL/TLS는 공개키 인증서에 기반을 둔 인증 방법을 사용하고 익명모드(Anonymous), 서버 인증모드(Server authenticated), 그리고 클라이언트-서버(상호) 인증모드(Mutual authenticated)를 갖는다. 익명모드는 서버가 공개키 인증서를 발급받지 않은 경우에도 적용이 가능하다는 장점이 있으나 안전에 문제가 있기 때문에 현재 많이 사용되고 있지 않다. 서버 인증모드는 서버가 SSL/TLS 서버용 공개키 인증서를 가지고 있을 경우에만 가능한 모드로 SSL/TLS 프로토콜을 이용하는 사용자는 서버 인증모드 동작시 서버를 인증할 수 있다. 이 방식은 현재 가장 많이 이용되는 방법이다.
상호 인증모드(서버/클라이언트)는 사용자와 서버가 모두 공개키 인증서를 가지고 있을 경우에만 가능한 모드로 이 모드가 동작할 경우 사용자와 서버는 서로에 대해서 인증이 가능하다. 또한 SSL/TLS는 여러 종류의 암호 알고리즘을 지원하고 실행과정에서 이들을 선택하여 사용할 수 있다. 알고리즘의 선택은 클라이언트가 사용 가능한 암호 알고리즘의 리스트를 보내고, 서버가 받은 리스트 중에서 선택하는 방법이다.
SSL/TLS는 레코드 프로토콜·암호 규격 변경 프로토콜·경고 프로토콜·1핸드세이크 프로토콜로 구성되어 있다. 레코드 프로토콜은 TCP 프로토콜의 상위에서 동작하며 기밀성과 메시지 무결성 같은 보안 서비스를 제공하고, 암호 규격 변경 프로토콜은 메시지 전송 후 전송되는 메시지는 새롭게 협상된 Security 파라미터에 의해 보호됨을 알리는데 사용된다. 경고 프로토콜은 상대에게 SSL/TLS 관련 경고를 전달하기 위해 사용된다. 핸드세이크 프로토콜은 서버와 클라이언트 간에 각 인증 모드를 허용하고 암호와 MAC 알고리즘을 교환하며, 암호화 키들을 SSL/TLS 레코드에서 보내진 데이터에 기밀성과 메시지 무결성을 적용하기 위하여 사용된다.

라이브러리 사용 및 응용 프로그램의 적용
이제 OpenSSL 라이브러리를 사용하여 간단한 보안 채팅을 구현해보자. 먼저 구현하기 위해 필요한 것들을 알아보면 리눅스 기반의 채팅 프로그램 또는 윈도우 기반의 채팅 프로그램과 OpenSSL 라이브러리가 필요하다. SSL/TLS 보안 프로토콜은 TCP 기반에서 동작하기 때문에 채팅 프로그램뿐만 아니라 TCP 소켓을 이용한 모든 응용 프로그램에 적용이 가능하다. OpenSSL 라이브러리 생성을 리눅스와 윈도우에 따라 각각 생성한 후 이를 이용하여 TLS가 적용된 보안 채팅을 구현해보자.
보안 라이브러리 생성은 소스 컴파일을 하여 생성할 수도 있고 라이브러리 패키지를 이용할 수도 있다. OpenSSL에서는 여러 암호 알고리즘과 유틸리티가 있기 때문에 필요한 부분만을 골라서 보안을 적용하려는 사용자들에게는 소스 컴파일 방법을 추천해 주고 싶다. 라이브러리 패키지는 Win32 Openssl 또는 RPM을 사용하기 바란다.

OpenSSL 소스 컴파일로 라이브러리 생성하기
리눅스 환경에서 만들기
OpenSSL 소스를 사이트(www.openssl.org)에서 가장 최근 버전인 openssl-0.9.7.tar.gz를 다운받은 후 압축을 풀고 다음과 같이 컴파일을 한다. 이는 가장 기본적인 컴파일 방법이며 기타 다른 컴파일 옵션은 ../openssl-0.9.7/ 디렉토리의 INSTALL 파일을 참고로 컴파일을 한다.
$ ./config shared?
$ make ?
$ make test ?
$ make install ?
이렇게 하면 /usr/local/ssl/lib/ 디렉토리에 libcrypto.a와 libssl.a 라이브러리와 libcrypto.so.0.9.7과 libssl.so.0.9.7 라이브러리가 생성된다. 각각 암호 라이브러리와 SSL/TLS 관련 라이브러리 파일이다. 리눅스 설치시 기본적으로 OpenSSL이 설치되어 있기 때문에 /usr/lib/ 디렉토리에 구 버전의 라이브러리 파일들이 있을 것이다. 방금 컴파일하여 생성된 라이브러리 파일들을 /usr/lib/ 디렉토리에 복사해주면 리눅스 시스템에서 최근의 OpenSSL 라이브러리를 사용할 수 있다.

윈도우 환경에서 만들기
윈도우에서는 ActivePerl을 먼저 다운받아 설치를 해야 한다. ActivePerl은 사이트(www.activestate.com/Products/Active Pe rl/)에서 다운받을 수 있다. ActivePerl은 실행 파일로 바로 설치를 하면 된다. 다음에 openssl-0.9.7.tar.gz 다운받아 임의의 디렉토리(C:)에 압축을 푼 후 다음과 같이 컴파일을 한다. 윈도우에서 컴파일 관련 파일인 INSTALL.W32를 참고하여 기타 다른 컴파일 옵션을 적용할 수 있다.

C:openssl-0.9.7> perl Configure VC-WIN32 ?
C:openssl-0.9.7> msdo_ms ?
C:openssl-0.9.7> nmake -f ms tdll.mak ?
C:openssl-0.9.7> cd out32dll ?
C:openssl-0.9.7> ..ms est ?

이렇게 하면 C:openssl-0.9.7out32dll 디렉토리에 ssleay32.dll, libeay32.dll, ssleay32.lib, libeay32.lib 라이브러리와 openssl 실행 명령어 파일이 생성된다.
OpenSSL은 VC++ 환경 사용자를 위하여 사이트(www. iconsinc.com/~agray/ossldev/)에서 VC6ossl097.tar.gz 파일을 다운받아 openssl 프로젝트 파일을 생성할 수 있다. 먼저 openssl-0.9.7.tar.gz를 다운받아 압축을 풀고 VC6ossl097.tar.gz 파일을 C:openssl-0.9.7 디렉토리에 압축을 풀어 Msvc097 디렉토리(C:openssl-0.9.7Msvc097)가 생성되게 한다. 이것 또한 ActivePerl은 설치되어 있어야 한다. 다음과 같이 VC++ 환경에서 openssl 프로젝트 파일 생성을 위한 과정을 따른다.

C:openssl-0.9.7>perl Configure VC-WIN32 ?
C:openssl-0.9.7>msdo_ms.bat ?
C:openssl-0.9.7>perl msvc097doinc.pl ?

앞의 과정을 마치고 난 후 C:openssl-0.9.7Msvc097에서 openssl.dsw 파일을 열면 VC++ 환경에 openssl이 열리게 된다. 메뉴 바에서 「Build | Batch Build | Build」를 실행시켜 컴파일하면 C:openssl-0.9.7out32dll 디렉토리에 openssl 실행 명령어 파일과 라이브러리 파일이 생성된다.

OpenSSL 라이브러리를 응용 프로그램에 적용하기
윈도우용 OpenSSL 라이브러리 패키지
www.shininglightpro.com/search.php?search name=Win 32+OpenSSL에서 윈도우용 OpenSSL 라이브러리 패키지를 다운받을 수 있다. 파일 Win32 OpenSSL(0.9.7) v1.0 Final(OpenSSL. exe)을 다운받아 실행시키면 C:OpenSSL 디렉토리에 OpenSSL 관련 라이브러리 파일(ssleay32.lib, libeay32.lib) 및 헤더 파일과 실행 파일 등이 설치된다. 라이브러리 파일(ssleay32.dll, libeay32.dll)은 윈도우 시스템 라이브러리 디렉토리에 생성된다. 리눅스를 위한 openssl RPM 패키지들도 있지만, 필자는 리눅스에서는 소스 컴파일 방법을 추천해주고 싶다.
지금까지 운영체제와 라이브러리 생성 방법에 따라 사용자들 입맛대로 보안 라이브러리를 생성하였다. 이제 생성된 보안 라이브러리를 일반 네트워크 응용 프로그램에 적용하는 방법에 대해서 알아보자. 이 글에서는 앞선 언급한 바와 같이 일반 채팅 프로그램을 사용하여 보안을 적용할 것이다. TLS 적용을 위한 전반적인 구조는 <그림 3>과 같다. TCP 채널이 형성된 후 TLS는 TCP를 기반으로 보안 채널을 형성하게 된다. 메시지의 전송은 SSL_read(), SSL_write() 함수를 통해서 Application Data로 전송되는 모습을 볼 수가 있다.

TLS 채팅 서버 만들기
채팅 서버의 TLS 연결 요청 수락
이 글에서는 채팅 프로그램에서 TLS를 구현하기 위해 필요한 소스 부분만을 설명하겠다. 채팅 서버 소스를 임의의 디렉토리(/home/cjduck/TLS_chatserver)에 위치시킨다. 먼저 <리스트 1>은 채팅 서버에서 TCP 소켓 연결 부분의 일부분이다. 여느 소켓 프로그램의 한 부분으로 생각해도 무방하다. 채팅 서버 소스에서 ssl.h 헤더 파일을 include해주고 TLS 관련 변수 및 함수 선언을 해준다. TLS 보안 채널은 TCP 클라이언트 연결 요청 수락이 이루어진 후 TLS_accept() 함수를 통하여 형성된다.

채팅 서버의 TLS 관련 옵션 설정
<리스트 2>는 TLS를 적용하기 위해 필요한 옵션을 설정하는 함수 TLS_accept()를 구현한 부분이다. 이 외의 TLS 관련 함수들은 라이브러리를 통해서 제공된다. TLS_accept() 함수는 TLS 서버에서 설정해 줄 암호 알고리즘 및 인증서에 관련된 사항으로 구성된다. 서버의 인증서 및 개인키 파일은 cnsl_cert.pem 파일과 cnsl_key.pem 파일로 설정을 해준다. 서버에서 사용되는 인증서는 OpenSSL 프로젝트에서 가상 인증기관(CA)를 구성하여 발급된 인증서를 사용한 것이다. 인증서 생성 부분은 뒤에서 설명하기로 하자.
클라이언트에서 보내준 암호 알고리즘 리스트 중에서 서버에서는 SSL_CTX_set_cipher_list() 함수를 통해 ‘AES128-SHA’로 선택할 수 있다. AES 암호 알고리즘은 최근에 각광받고 있는 암호 알고리즘으로 현재 가장 추천되는 암호 알고리즘이다. 기타 다른 암호 알고리즘은 ssl2.h, ssl3.h, tls1.h의 각 헤더 파일에 define으로 정의되어 있으므로 참고하기 바란다.
TLS 채팅 서버 초기화
initialize_ctx() 함수에서는 TLS 보안 채널에서 사용될 암호 알고리즘 초기화 및 SSLv2/v3 또는 TLSv1 프로토콜 사용할 SSL_CTX를 초기화한다. 또한 서버에서는 TLSv1_server_method(), SSLv2_ser ver_method(), SSLv3_server_method()를 통해서 서버에서 적용하는 SSL/TLS 보안 프로토콜을 설정할 수도 있다.

TLS 보안 채널을 통해 메시지 암·복호화
앞에서와 같이 서버와 클라이언트 간에 보안 채널이 형성된 후에는 기존의 송수신 함수인 write(), read() 함수 대신에 SSL_ read()와 SSL_write() 함수를 통해서 메시지를 암호화하여 전송하게 된다.

TLS 채팅 서버 컴파일하기
이와 같이 구성한 후 OpenSSL 라이브러리 파일(libssl.a, libcrypto.a)을 /home/cjduck/TLS_chatserver/lib/ 디렉토리에 복사한다. 그리고 다음과 같이 OpenSSL 라이브러리 파일과 함께 컴파일을 한다.

[root@cnsl TLS_chatserver]$ gcc -o server server.c -L./lib -lssl -lcrypto

TLS 클라이언트 만들기
채팅 클라이언트의 TLS 연결 요청
이제 TLS 클라이언트 부분을 완성시켜 보자. 채팅 클라이언트 소스를 임의의 디렉토리(/home/cjduck/TLS_chatclient)에 위치시킨다. TLS 서버 부분과 마찬가지로 <리스트 5>는 채팅 클라이언트에서 TCP 소켓 연결 부분의 일부분을 나타내고 있다. 채팅 클라이언트 소스에서 ssl.h 헤더 파일을 포함시키고 TLS 관련 변수 및 함수 선언을 해준다. TCP connect()로 서버에 TCP 연결 요청을 한 후 TLS_connect() 함수를 통해서 TLS 보안 채널을 형성한다.
TLS 클라이언트 초기화
<리스트 6>은 TLS_connect() 함수 구현 부분이다. TLS 클라이언트에서 TLS를 구현하기 위해 해주어야 할 부분이다. 기타 SSL/TLS 관련 함수는 OpenSSL 라이브러리 파일에서 제공된다. 이 부분도 TLS 서버와 같이 알고리즘 초기화 및 SSLv2/v3 또는 TLSv1 프로토콜 사용할 SSL_CTX를 초기화한다. 또한 클라이언트에서도 TLSv1_server_method(), SSLv2_server_method(), SSLv3_ server_method()를 통해서 서버와 통신할 SSL/TLS 보안 프로토콜을 설정할 수 있다. 그리고 TLS 보안 채널을 통해 메시지를 암·복호화하는 데 있어 SSL_read(), SSL_write() 부분은 서버와 같이 해주면 된다.

TLS 클라이언트 컴파일
OpenSSL 라이브러리 파일을 동일하게 /home/cjduck/TLS_ chatclient/lib/ 디렉토리에 복사한 후 다음과 같이 컴파일을 하면 TLS 채팅 클라이언트가 완성된다.

[root@cnsl2 TLS_chatclient]$ gcc -o client client.c -L./lib -lssl -lcrypto

OpenSSL에서 X.509v3 인증서 만들기
가상 루트 CA 만들기
앞서 구현한 TLS 보안 채팅은 OpenSSL에서 제공하는 X.509v3 인증서를 사용하여 동작된다. 여기에서는 SSL/TLS 뿐만 아니라 공개키 기반구조에서 사용되는 인증서 생성 방법에 대해서 알아보기로 하자. OpenSSL의 openssl.0.9.7/apps의 CA.sh 파일로 쉽게 인증서를 생성할 수 있다. CA.sh 파일을 이용하여 가상 CA를 구성할 때 인증서 관련 설정 파일은 /usr/share/ssl/openssl.cnf 파일에서 설정한다.

CA로부터 인증서 발급
앞의 과정이 끝난 후 apps/demoCA에는 cacert.pem(CA 인증서 파일), cakey.pem(CA 개인키 파일)이 생성된다. apps/demoCA/ newcerts에는 앞으로 생성되는 사용자 인증서 파일이 생성되는 곳이다. 가상 CA로부터 인증서를 발급받을 때 인증서 관련 설정 파일은 /usr/local/ssl/openssl.cnf 파일에서 설정한다.

발급받은 인증서를 TLS 서버 인증서로 사용
생성된 인증서는 ../apps/demoCA/newcerts 디렉토리에 01.pem로 생성된다. 01.pem 파일은 ‘---- BEGIN CERTIFICATE ----’ 부분부터 ‘---- END CERTIFICATE ----’ 부분만을 저장하고 나머지 내용은 삭제하여 사용한다. 앞의 과정에서 생성된 인증서는 예를 들어 01.pem은 파일 이름을 cnsl_cert.pem로 하고, cnsl.key는 파일 이름을 cnsl_key.pem로 바꾸어서 SSL/TLS, S/MIME 등에서 인증서로 사용될 수 있다.

구현된 TLS 보안 채팅 확인하기
이제 실제 데이터 패킷을 캡처해 보안이 적용되었을 때와 그렇지 않을 때를 비교하여 확인해 보자. 인터넷에서 패킷 캡처 프로그램을 손쉽게 구할 수 있으므로 어느 것을 사용해도 무방하나 필자는 ‘Ethereal’이라는 프로그램으로 채팅 메시지를 확인해 보겠다. 이 프로그램은 www.ethereal.com에서 다운받을 수 있다. <화면 1>은 TCP 일반 채널과 TLS 보안 채널을 형성한 후 동일한 메시지를 전송하고 패킷을 확인한 모습이다.

<화면 2>와 같이 일반 TCP에서는 두 사용자 간에 주고받는 카드번호 또는 비밀번호에 대해서 쉽게 확인이 가능하다. 그러나 TLS가 적용된 보안 채팅에서는 두 사용자가 채팅을 하기 전에 TLS 보안 채널이 형성되는 것을 볼 수가 있고 이를 통해서 채팅 내용은 ‘Application Data’로 전송이 되어 메시지의 내용을 확인할 수가 없다.

네트워크 응용 프로그램을 위한 보안 프로토콜
지금까지 공개 보안 소스를 이용하여 네트워크 보안을 지원하도록 구성해 보았다. 지금부터는 자체적인 보안 방법을 강구하여 사용하는 방법을 알아보도록 하겠다. 필자는 네트워크 응용 프로그램을 위한 자체적인 보안 프로토콜을 구현하기 위해 다음과 같은 가정을 했다. 일반적으로 네트워크 응용 프로그램들은 가입자 기반으로 운영되며 이러한 가입자들을 인증하기 위해 패스워드를 사용한다. 초기에 서비스 요청을 위해 가입자는 패스워드를 입력하게 되고 이를 사용하여 서버는 사용자를 인증하고 서비스를 개시한다. 서비스 모델은 서버와 클라이언트 형태이며 이들 간에 키 교환과 패스워드 기반의 메시지 인증을 수행하고 공유된 키(대칭키)를 기반으로 전송되는 데이터를 암호화한다.

왜 대칭키 기반 암호 알고리즘을 사용하는가
현재 은행이나 증권 업계에서는 주로 공인인증서를 사용하는 PKI(Public-Key Infrastructure)를 사용한다. 그러나 이러한 공개키 기반의 암호 알고리즘들은 계산에 많은 시간을 필요로 한다. 따라서 네트워크를 기반으로 하는 실시간 응용 프로그램들은 서비스 속도 자체가 느려지거나 서버의 수용 능력이 감소될 수 있다. 또한 PKI는 기반 구조를 모두 갖춘 상태에서 사용해야 하기 때문에 인증서를 처리하는 과정이 필요하고 사용자 모두가 인증을 모두 발급받아야 한다는 가장 커다란 제약을 가지고 있다.
이에 반하여 대칭키 기반의 암호 알고리즘은 암호화 속도가 빠르고 같은 키로 암호화를 수행하기 때문에 PKI와 같은 기반 구조를 필요로 하지 않고 적용이 쉽다. 따라서 표준 보안 프로토콜에서도 실제 보호할 데이터는 대칭키 기반의 암호 알고리즘을 사용하고 대칭키를 보호하는데 공개키를 사용한다.

네트워크 보안 프로토콜 소개 및 구조
네트워크 보안은 네트워크를 통해 전달되는 데이터가 안전하게 원하는 목적지까지 도착하는 것을 목적으로 하며 여기에는 기밀성·무결성·인증을 제공해야 한다. 기밀성은 데이터를 암호화하여 해커와 같은 악의적인 인터넷 사용자들이 볼 수 없도록 하며, 무결성은 메시지가 변조되지 않았음을 증명하고, 인증은 정당한 사용자가 데이터를 보낸 것인가를 확인하는 작업이다. 이 글에서는 인증과 무결성을 동시에 보장하기 위해 MAC(Message Authentication Code)를 사용할 것이며 MAC에 사용되는 키는 사용자와 공유된 패스워드를 해시한 값을 이용할 것이다.
이러한 방법은 MAC에 사용자의 패스워드를 사용하기 때문에 사용자를 인증하고 동시에 메시지에 무결성도 보장된다. 기밀성을 제공하기 위해서는 대칭키 기반의 암호 알고리즘을 사용할 것인데 여기에 사용되는 키를 안전하게 공유하는 것이 가장 중요한 문제이다. 기존에 개발되어 있는 몇몇 네트워크 응용 프로그램을 보면 보안을 제공하기 위해 대칭키 기반의 암호 알고리즘을 사용하기는 하였으나 키 교환 알고리즘을 사용하지 않고 단순히 전송하는 메시지에 키를 숨겨 전송하고 있다. 이러한 눈속임 방법은 해커에 의해 쉽게 깨어질 수 있으며 안전성을 보장하지 못한다.

◆ 사용할 암호 알고리즘
- 암호 알고리즘 : AES(Advance Encryption Standard)
- 키 교환 알고리즘 : Diffie-Hellman
- HMAC 알고리즘 : HMAC_SHA1
- 해시 알고리즘 : SHA1

<그림 4>는 네트워크 응용 서비스를 위한 간단한 보안 프로토콜을 보여주고 있다. 우선 키 교환 알고리즘인 Diffie-Hellman을 통해 안전하게 키를 교환할 것이며 교환된 키와 전송할 데이터를 암호 알고리즘인 AES를 이용하여 암호화할 것이다. 또한 전송되는 모든 메시지는 패스워드를 해시하여 나온 인증키와 메시지 인증 알고리즘인 HMAC_SHA1을 통해 인증 코드를 생성하여 붙여줄 것이다.
일단 보안 프로토콜을 구현하기 위해서는 암호 알고리즘이 있어야 한다. 그러나 암호 알고리즘을 구현하는 작업은 전문성이 필요하다. 암호 알고리즘을 이해하고 있어야 하며 암호 모듈의 성능 향상을 위해 계산상에서 몇 가지 연산을 바꿔주어야 하기 때문이다. 따라서 이 문서에서는 공개된 암호 모듈을 이용하도록 하는데 공개 소스는 Crypto++나 OpenSSL 등의 공개 프로젝트에서 추출하여 사용할 수 있다.

◆ Crypto++ Library 4.2 사이트 : http://www.eskimo.com/~weidai/cryptlib. html

이 외에도 여러 사이트에서 보안에 관련된 공개 소스 코드나 라이브러리를 사용할 수 있으나 여기에서는 Crypto++ Library 4.2를 사용한다.

메시지에 인증 도장을 찍어 보자
지금부터 실제 구현을 해보도록 하겠다. 우선 메시지의 인증을 위해 패스워드를 해시 함수에 넣어 해시 값을 계산해야 한다. 다음은 해시 함수의 인터페이스를 보여주고 있다.

◆ 해시 알고리즘
­ SHA1(해시 함수)
BYTE * bpMsg; // 해시할 데이터
int nMsgLen; // 해시할 데이터 길이
BYTE baHashSHA1[20]; // 해시 결과
SHA1().CalculateDigest(baHashSHA1, bpMsg, nMsgLen); // 해시

이렇게 패스워드를 해시한 값과 메시지를 이용하여 인증 코드를 생성하는 HMAC/SHA1 함수의 인터페이스를 살펴보겠다.

­ HMAC/SHA1(HMAC 함수)
BYTE * bpMsg; // 해시할 데이터
int nMsgLen; // 해시할 데이터 길이
BYTE * bpKey; // 해시에 사용될 키
int nKeyLen; // 해시에 사용될 키 길이
BYTE baHMACSHA1[20]; // 해시 결과
typedef HMAC HMAC_SHA1;

HMAC_SHA1 hmacsha1(bpKey, nKeyLen); // 키 설정
hmacsha1.CalculateDigest(baHMACSHA1, bpMsg, nMsgLen); // 해시

이러한 인증을 수행하는 이유는 사용자를 인증하는 목적과 Diffie-Hellman 키 교환 알고리즘을 독립적으로 수행하면 man_in_the_ middle_attack이 가능하기 때문에 이를 막기 위해서다.

키를 교환해 보자
키 교환을 위해서는 다음과 같은 몇 가지 파라미터가 필요하지만 지면 관계상 여기에서는 Diffie-Hellman 파라미터의 수학적 의미를 설명하지는 않겠다.

◆ 키 교환 알고리즘
­ Diffie-Hellman 파라미터 생성(512 비트) ; 개인키, 공개키 생성
? generator는 일반적으로 2, 3, 5를 사용한다.
? 소수는 랜덤하게 생성해서 사용할 수 있으나 큰 소수를 생성하는데 많은 시간이 소요된다.
? 클라이언트와 서버는 미리 고정된 소수, generator를 사용한다. 즉 프로그램에 이미 값을 설정하고 사용한다.
? 클라이언트와 서버는 계산되어 나온 공개키를 상대방에게 전송하고 개인키는 가지고 있어야 한다.

Diffie-Hellman 개인키 및 공개키 생성된 소스는 다음과 같다.

BYTE baPrime[64]; // 고정된 소수
int nGroup; // generator
BYTE baPri[64]; // 개인키
BYTE baPub[64]; // 공개키
Integer IPrime; // 소수
Integer IGroup; // generator

for(int i=0; i<64; i++)
IPrime.SetByte(i, baPrime[i]); // 고정된 소수 사용

IGroup=nGroup; // generator 입력
DH DHVar(IPrime, IGroup); // 소수 및 generator 설정
LC_RNG rng(time(NULL));
DHVar.GenerateKeyPair(rng, baPri, baPub); // 개인키, 공개키 생성

◆ Diffie-Hellman 공개키 생성(512 비트)
이제 교환한 DH 파라미터를 이용하여 공유키를 생성해 보자.
? generator, 소수는 개인키, 공개키 생성시 사용한 값을 사용한다.
? Diffie-Hellman 키 생성 함수에는 상대방이 보내준 공개키와 자신의 개인키를 사용해서 Diffie-Hellman 공유키를 생성한다.

Diffie-Hellman 공유키 생성과 관련된 소스는 다음과 같다.

BYTE baPrime[64]; // 고정된 소수
int nGroup; // generator
BYTE baPri[64]; // 개인키
BYTE baPub[64]; // 상대방 공개키
BYTE baAgree[64]; // Diffie-Hellman 공유키
Integer IPrime, IGroup;

for(int i=0; i<64; i++)
IPrime.SetByte(i, baPrime[i]); // 고정된 소수 사용
IGroup=nGroup; // generator 입력
DH DHVar(IPrime, IGroup); // 소수 및 generator 설정
DHVar.Agree(baAgree, baPri, baPub, TRUE); // Diffie-Hellman 키 생성
내 데이터는 내가 보호하자
이제 키 교환이 끝났으니 그 키를 가지고 데이터를 암호화해 보자. 암호화 함수는 다음과 같이 사용할 수 있다.

◆ 암호 알고리즘
­ AES 암호화(ECB 모드)
? DES, 3DES, RC2, IDEA도 동일한 방법으로 사용할 수 있으며, CBC, CFB, OFB 모드 사용시 별도의 IV 설정이 필요하지만 여기에서는 ECB 모드를 사용하여 필요하지 않다.
? 암/복호화는 블럭 단위(128 비트)로 수행하며, 블럭의 배수가 아니면 블럭의 배수가 되도록 패딩이 필요하다.

다음은 AES 암호화의 경우다.

BYTE * bpPlainMsg; // 평문
int nPlainMsgLen; // 평문 길이
BYTE * bpKey; // 암호화에 사용될 키
int nKeyLen; // 암호화에 사용될 키 길이
BYTE * bpCipherMsg; // 암호문

AESEncryption EncAES(bpKey, nKeyLen); // 키 설정
for(int i=0; i*16 EncAES.ProcessBlock(bpPlainMsg+i*16, bpCipherMsg+i*16); // 암호화

이와 함께 AES 복호화(ECB 모드)의 경우는 다음과 같다.

BYTE * bpCipherMsg; // 암호문
int nPlainMsgLen; // 평문 길이
BYTE * bpKey; // 복호화에 사용될 키
int nKeyLen; // 복호화에 사용될 키 길이
BYTE * bpPlainMsg; // 평문

AESDecryption DecAES(bpKey, nKeyLen); // 키 설정
for(int i=0; i*16 DecAES.ProcessBlock(bpCipherMsg+i*16, bpPlainMsg+i*16); // 복호화

전송 데이터에 보안 모듈 적용
이제 전송할 데이터에 보안을 적용해 보자. 우선 전송할 메시지를 암호 알고리즘을 통해 암호화하는 데 블럭 단위의 암호 알고리즘이기 때문의 암호화 블럭의 배수만큼 크기가 증가할 수 있다. 그 후에 인증 알고리즘을 통해 인증 코드(MAC)를 생성하여 붙인다. 이렇게 인증을 암호화 이후에 수행하는 이유는 중간에 데이터가 변조되었을 경우 빠르게 판단하여 복호화하기 전에 변조된 패킷을 버리기 위함이다.
지금까지 간단한 보안 프로토콜을 알아보았는데 앞에서 언급한 암호화와 인증을 모두 구현해야 하는 것은 아니다. 패스워드 기반의 네트워크 응용 프로그램이 아닌 경우, 인증 부분을 제외하고 키 교환과 암호화만 수행하여도 되지만 사용자 인증은 Diffie-Hellman에서 man_in_the_middle_attack이 가능한 약점을 보완하기 위해서 필요한 작업이다. 이것은 어디까지나 데이터의 중요도와 보안 정책에 관련된 사항으로 개발자나 관리자들이 고민하여 사용하기 바란다.

보안 개념을 실무에 적용하자
지금까지 인터넷에서의 보안 위험성과 여러 가지 문제점을 살펴보고 이러한 위협에 대한 대책을 알아보았다. 그러나 이러한 방법은 결코 완전한 해결책이 아니다. 보안 프로토콜 중에서 핵심 내용만을 사용하였기 때문에 사소한 보안 허점이 있을 수 있으며 호환성을 가지지 못한다. 결국 한정된 자원만을 보호하는 것이기 때문에 금융이나 증권 업계에서는 적용이 불가능하겠지만 일반 네트워크 응용 서비스에서는 그 역할을 충분히 다할 것으로 생각한다.

정리 | 조규형 | jokyu@korea.cnet.com


OpenSSL 프로젝트에서 제공되는
암호 알고리즘 및 보안 프로토콜

◆ 버전 : 0.9.7(2002.12)
◆ 지원 암호 알고리즘
- Block ciphers : AES, 3DES, DES, DESX, CAST, RC2, RC5, IDEA, Blowfish
- Stream ciphers : RC4
- Hash : MD2, MD4, MD5, SHA-1, RIPEMD 160, MDC2
- Asymmetric cryptosystems : RSA, DSA, DH, Elliptic curves
- MAC : HMAC
◆ Standards Implemented
- PKCS1, PKCS7, PKCS8, PKCS10, PKCS12
- X509v3
- SSLv2, SSLv3 and TLSv1

참+고+자+료

? http://www.openssl.org
? http://www.shininglightpro.com
? Network Security with OpenSSL, 오라일리
? 컴퓨터 통신보안, 그린
? 컴퓨터 네트워크 프로그래밍, 김화종
? H.235 v2, “Security and encryption for H-Series(H.323 and other H.245-based) multimedia terminals”, ITU-T, 2000


좋은정보가 되셨다면 아래 한번 클릭해주세요^^



댓글을 달아 주세요