EzDoum

찾기
처음으로 | 찾기 | 아카이브 | 글 올리기 | 링크 | 자료실 | 통계 | 연락처 | 자유게시판
이지도움 특집
전체보기
네트워크
TI OMAP35x
TI DaVinci
Analog Blackfin
RobotWar2005
임베디드!
캐쉬의 모든것
메모리 할당 알고리즘
CPU 파이프라이닝
자료구조(Tree)
금융

Login
이름

암호

기억하기


사용자 등록

현재 접속중인 등록 사용자는 0명, 익명 사용자는 2명 입니다.
전체 등록 사용자: 751명

마지막 답장
·libcurl + fuse 조합으로 되는게 많네. (1)
·Linux Ftrace에 관해 (3)
·Android MTP ( Media Transfer Protocol ) (1)
·Lighttpd에 인증을 digest 사용시 IE 오동작 문제? (1)
·Dtrace에 관해 (1)

최근글
·OpenSSL and multi-threads (0)
·ARM 환경에서 OpenCL 사용 (0)
·IoT용 WIFI 모듈 비교 ( MCU ) 클래스 (0)
·Glances - 리눅스 여러 가지 항목을 한 화면에서 모니터링 (0)
·plugin 방식의 로그 분석기 (0)

뜨거운 감자
·나는 인터렉티브한 환경에서 역어셈블 한다. (12)
·GNU REGEX (정규표현식) 프로그래밍 강좌 (7)
·SoCRobotWar 2005 - 신입생 기초 교육자료 (7)
·ASP.NET의 데이터 그리드와 사용자 컨트롤 (7)
·DHTML Editing Control (7)

가장 많이 읽은 글
·[Cache] 2-way Set-Associative 방식이란 무엇일까? (2)
·멀티쓰레드(Pthread) 프로그래밍
·GNU REGEX (정규표현식) 프로그래밍 강좌 (7)
·Sorting Algorithm Animation (2)
·SoCRobotWar 2005 - 신입생 기초 교육자료 (7)

CryptoAPI를 ASP에서 사용하기
글쓴이: EzDoum 글쓴날: 2003년 09월 28일 오후 11:00
보안



CryptoAPI를 이용한 대칭 암호화 구현
중요 데이터를 네트워크를 통해 주고받거나 데이터베이스, 파일 등에 저장할 때 보안을 위해 암호화 과정을 거치게 된다. 암호화에는 여러 방식이 있는데 데이터에 일정한 값을 더하거나 XOR을 수행하는 것과 같이 간단한 방식들도 있고 대칭 암호화 방식이나 공개키 암호화 방식과 같이 진보된 방법들도 존재한다. 이번 호에서는 CryptoAPI를 써서 대칭 암호화 방식을 구현하는 예를 보이도록 하겠다.
사실 암호학은 굉장히 난해한 분야라서 전공자가 아니면 어려울 수밖에 없다. 이처럼 어려운 암호학을 함수화 한 CryptoAPI 역시 암호학에 대해 조예가 깊지 못하면 이해하기 어려운 것은 마찬가지다. 이런 문제 때문에 Crypto API의 사용법도 필자가 사용했던 예제 중심으로 다루었으며 자세하게 다루지는 못했다는 점을 이해해주기 바란다. 이번 호에서는 윈도우 운영체제의 암호화 API인 CryptoAPI에 대해 알아본다. 윈도우 이외의 운영체제에서의 암호화에 대해 관심이 있다면 윤우성님의 '자바를 이용한 암호 시스템 구현'을 참고하기 바란다.

암호화(Cryptography) 개론
암호화는 중요한 데이터를 안전하게 저장, 전송하기 위해 암호화하는 여러 가지 방법을 말한다. 암호화는 파일 시스템에 중요한 데이터를 저장할 때 많이 쓰이지만 인터넷과 같이 전송 매체가 신뢰할 수 없는 것일 때 더욱 중요성이 부각된다. 즉, 암호화의 가장 중요한 용도는 보안이 필요한 데이터를 남들이 봐도 내용을 모르게 하는 것이다. 암호화가 속이 보이지 않는 봉투에 데이터를 집어넣는 것이라면 해독은 봉투에서 데이터를 빼내는 것이라고 할 수 있는데 암호화 방식은 암호화에 쓰인 키와 해독에 쓰인 키가 같은지 여부에 따라 크게 대칭 암호화 방식이나 공개키 암호화 방식으로 나뉜다. 암호화의 두 번째 용도는 데이터의 무결성을 보장하기 위한 것이다. 암호화를 이용하면 이 데이터를 누가 만들었으며 만든 이후에 다른 누군가가 조작을 가한 사실이 없다는 것을 보장할 수 있다. 이런 용도로 사용되는 암호화의 영역이 바로 디지털 증명서(Digital certificate)인데 이를 디지털 서명(Digital signature)이라고 하기도 한다.
디지털 서명은 문서나 영수증에 서명하는 것과 비슷한 개념인데 이것의 역할은 누가 사인했는지와 사인한 후에 변경된 사항이 없음을 증명하는데 쓰인다. 연초에 팀버튼 감독의 슬리피 할로우라는 영화가 개봉되었는데 이 영화의 첫 장면에는 크고 빨간 도장으로 유언장에 사인하는 장면이 나온다. 서명의 역할은 바로 이런 것으로 누가 사인했는지, 그리고 사인을 문서의 개봉부에 하여 누군가 뜯어본다면 표시가 나도록 하는 것이다. 사인의 검증은 알고 있는 기존의 사인과 비교해보고 문서를 누군가 뜯어본 적이 없음을 살펴보는 것이라고 생각하면 된다.
암호화와 관련된 사항을 잠깐 정리해 보기로 하자. 사실 굉장히 복잡한 내용이고 필자의 전문 분야도 아니기 때문에 CryptoAPI를 쓰는데 필요한 최소한의 내용만 알아보도록 하겠다. 자세한 내용을 알고 싶다면 참고 문헌 8 Network & Internet Security라는 책의 4장 Distributed Security Services를 보기 바란다.

대칭 암호화(Symmetric Cryptography)
세션키, 비밀키, 혹은 대칭키라고 불리는 하나의 키를 써서 데이터를 암호화하고 해독하는 방식이다. 암호화하고 해독하는데 쓰는 키가 같은 것이라는 뜻에서 대칭 암호화라 부른다. 가장 이해하기 쉽고 쓰기 쉬운 암호화 방식이라고 할 수 있다. 단, 이 비밀키는 최소한의 사람들만 알고 있어야 하기 때문에 비밀키는 자주 변경해야 한다. 반면, 뒤에서 살펴볼 공용키 암호화 방식에서 사용되는 공용, 개인키는 한번 발급되면 원칙적으로 변경하지 않는다. 대칭 암호화 방식의 장점은 공용키 암호화 방식에 비해 속도가 훨씬 빠르다는 것으로 대용량의 데이터를 암호화하고 해독하는데 적합하다. 자주 쓰이는 암호화 알고리즘으로 RC2, RC4, DES(Data Encryption Standard)등이 있다. 대칭 암호화 방식은 주로 데이터 자체를 암호화하는데 쓰이며 키는 매번 만들어 쓰는 것이 일반적이다.
공용키 암호화(Public-key Cryptography)
흔히 비대칭 암호화 방식이라 부르기도 한다. 이 방식은 앞서 설명한 대칭 암호화 방식에서처럼 하나의 비밀키로 암호화하고 해독하는 것이 아니라 암호화하는 키와 해독하는 키를 별도로 두는 방식이다. 즉 두 개의 키를 쓴다는 점에서 앞서 살펴본 대칭 암호화 방식과 구별된다. 이 키는 양방향성을 가지고 있어서 한 키로 암호화한 것은 다른 키로 해독할 수 있다. 이 두 개의 키는 각각 공용키(public key)와 개인키(private key)라는 것으로 구성된다.
개인키는 말 그대로 개인적으로 안전하게 보관되어야 하는 키이고 공용키는 누구에게 제공되어도 관계없는 키이다. 공용키는 디지털 인증서의 일부로 배포되곤 한다. 예를 들어 H라는 사람이 공용키와 개인키를 갖고 있으며 자신의 공용키로 어떤 중요한 데이터를 암호화했다고 하자. 그러면 이 데이터는 H라는 사람의 개인키로만 해독할 수 있다. 반대로 H의 개인키로 암호화된 데이터는 H의 공용키로만 해독할 수 있는 것이다. 후자의 경우 이를 이용하면 데이터를 H가 만들었다는 것을 보장하는 용도로 쓸 수 있다. 단, 이것은 H의 개인키를 H만 알고 있다는 전제에서만 가능하다는 점을 명심하기 바란다.
이와 같이 공용키 방식은 대칭키 방식에 비해 강력한 기능을 제공해 주지만 대칭키 방식에 비해 거의 1/1000정도로 속도가 느리다는 단점을 가지고 있다. 그렇기 때문에 공용키 방식으로 대용량의 데이터를 암호화한다는 것은 거의 불가능한 일이다. 그래서 대부분의 경우 대칭키 방식과 공용키 방식은 함께 쓰인다. 데이터 자체는 비밀키를 하나 만들어 암호화하고 비밀키와 디지털 서명은 공용키 방식을 이용해 암호화한 다음 전송하는 것이 일반적이며 SSL (Secure Socket Layer)은 이런 방식을 쓰고 있다.
그러면 비밀키는 어떤 방식으로 만들어 낼까? 비밀키는 데이터 자체를 바탕으로 해싱을 사용해 어떤 특별한 값을 만들고 이를 비밀키로 사용하는 방식을 많이 쓴다. 이에 대해서는 '디지털 서명'을 참고하기 바란다. 사실 공개키 암호화 방식을 제대로 쓰려면 각 사용자는 두 쌍의 공용, 개인키를 갖는 것이 일반적이다. 한 쌍은 비밀키를 암호화하고 해독하는데, 다른 한 쌍은 디지털 서명을 암호화하고 해독하는데 쓴다. 전자의 쌍을 키 교환키(key exchange key)라고 하고 후자의 쌍은 서명키(signature key)라고 한다.
어떤 데이터를 암호화하려고 한다면 먼저 비밀 암호화 키의 알고리즘과 키의 크기, 파일 형식을 미리 결정해야 한다. 이 중에서 비밀 암호화 키 자체를 제외하면 외부에 공개되어도 보안에 큰 문제는 없다.

디지털 서명(Digital Signature)
흔히 디지털 인증서라고도 하는 것으로 대개 256바이트 이하의 바이너리 데이터로 구성되어 데이터의 저작자를 확인하고 데이터의 무결성을 검증하는데 쓰인다. 이러한 디지털 서명은 VeriSign(http://www.verisign.com/)과 같은 인증서 발급 기관(Certificate Authority)에서 발급해 준다. 국내에는 VeriSign의 공식 대행 기관으로 한국전자인증(http://www.crosscert. com/)이 있다. 디지털 서명은 이 서명이 누구의 것인지를 나타내는 정보와 그 사람의 공용키와 인증 유효 날짜 등의 정보로 구성된다.
특히 그 사람의 공용키는 아주 중요한 역할을 하는데, 마이크로소프트의 CryptoAPI에서 제공하는 디지털 서명은 RSA의 공용키 암호화 표준(PKCS : Public-Key Cryptography Standard) #6를 따르고 있다. 디지털 서명이 얼마나 널리 쓰이는지 모르는 경우가 많은데, 인터넷 익스플로러로 인터넷을 많이 뒤지는 독자라면 자신도 모르게 이미 많이 경험한 상태라고 할 수 있다. ActiveX 컨트롤이나 자바 애플릿을 쓰는 사이트에 들어가면 갑자기 다이얼로그 박스가 떠서 다운로드 받을 것이냐고 물어보는 경우가 있는데 이것이 디지털 서명이 쓰이는 경우다.
실제 예를 하나 들어보겠다. 필자는 농구를 참 좋아한다. 그 중에서 NBA 게임 관전을 특히 좋아하는데 유타 재즈 팀과 유타 재즈의 존 스탁턴 선수를 좋아한다. NBA 웹 사이트(http://www.nba.com)에 가면 Courtside Live라는 애플릿을 통해 게임을 실시간 중계해 준다. 동영상이 지원되는 것은 아니지만 관심 있는 사람들이 보기엔 아주 쓸만하다. 이 애플릿이 들어 있는 페이지를 처음 로드하면 그림 1과 같은 다이얼로그가 뜬다.
이 다이얼로그에 뜨는 정보는 애플릿 파일에 포함되어 있는 디지털 서명을 분석하여 띄우는 것이다. 이 디지털 서명은 VeriSign에서 자신의 개인키로 암호화했기 때문에 VeriSign의 공용키가 있으면 해독할 수 있다. 그런데 인터넷 익스플로러가 설치될 때 디폴트로 VeriSign의 공용키가 같이 설치되기 때문에 디지털 서명을 해독할 수 있다. 일단 이 과정을 통해 디지털 서명이 VeriSign과 같은 공인 기관에서 만들어졌다는 것을 알 수 있다. 서명을 분석하면 누구의 서명인지도 알 수 있다. 위의 예에서는 애플릿을 만든 곳은 InfoSeek이라는 회사임을 알 수 있다. 서명을 해독하면 그 사람(혹은 회사)의 공용키도 알아낼 수 있다. 앞서 이야기한 것처럼 애플릿 자체는 덩치가 커서 공용키 방식으로 암호화하면 시간이 오래 걸리기 때문에 애플릿 자체는 비밀키로 압축하며 이 비밀키는 애플릿 데이터를 바탕으로 해싱을 적용해 만들어낸다. 그런 다음 이 비밀키는 제작자의 개인키로 압축되어 애플릿과 같이 전송되기 때문에 서명의 해독을 통해 알아낸 제작자의 공용키로 해독해 낼 수 있다. 이 비밀키로 암호화된 애플릿을 해독하면 원래의 애플릿 내용을 알아내어 실행할 수 있게 된다. 절차를 정리하면 다음과 같다.

① 애플릿의 내용을 바탕으로 해싱을 통해 애플릿을 암호화하는데 사용할 비밀키를 만들어 낸다.
② 이 비밀키는 제작자의 개인키로 암호화된다.
③ 애플릿은 ①에서 만든 비밀키로 암호화되고 디지털 서명은 이미 인증기관의 개인키로 암호화되어 있다. 암호화된 애플릿과 암호화된 비밀키와 디지털 서명을 전송한다.
④ 브라우저에서는 받은 애플릿을 대상으로 해싱을 적용해 키를 ①에서와 동일한 방법으로 만든다.
⑤ 전송된 암호화된 비밀키를 알아내기 위해 디지털 서명에서 제작자의 공용키를 빼내서 해독한다. 디지털 서명의 해독은 인증기관의 공용키를 통해 수행한다. 그 다음 ④에서 나온 키와 비교한다. 같으면 애플릿의 무결성이 보장되는 것이다. 즉, 다른 누군가가 애플릿의 내용을 조작하지 않았음이 보장되는 것이다.
⑥ 애플릿을 해독하고 실행한다.

그림 2는 NBA Courtside Live의 실행 화면이다.


웹 상의 보안 프로토콜 SSL(Secure Socket Layer)
현재 웹에서 쓸 수 있는 보안 프로토콜로는 SSL과 SHTTP가 있다. SSL은 원래 넷스케이프에 의해 만들어져서 무료로 공개되었으며 소스와 관련 문서도 구할 수 있다. SSL에 대한 보다 자세한 자료는 http://home. netscape.com/security/techbriefs/ssl.html에서 찾을 수 있다. SHTTP(Secure HyperText Transfer Protocol)는 현재 IETF(Internet Engineering Task Force)에 의해 드래프트 작업 중이며 SSL보다 융통성이 좋다고 한다. 이에 관한 문서는 http://search.ietf.org/intern et-drafts/draft-ietf-wts-shttp-06.txt에서 찾을 수 있다. SSL로 연결하고자 할 때는 http://가 아닌 https://로 연결해야 한다. 또한 HTTP는 포트 80을 쓰는데 반해 HTTPS는 포트 443을 사용한다. HTTPS로 연결되었을 때 인터넷 익스플로러의 경우에는 상태 바의 하단에 자물쇠 모양이 나타난다.

CryptoAPI
마이크로소프트의 CryptoAPI 응용프로그램 개발자들이 암호화 기능과 디지털 서명 기능을 사용하고자 할 때 쓰는 API(Application Programming Interface)이다. CryptoAPI는 데이터의 암호화뿐 아니라 데이터의 디지털 사인 작업까지 수행하는데 쓸 수 있다. CryptoAPI는 GDI(Graphic Device Interface)와 비슷한 구조로 구성되어 있다. GDI가 실제 비디오 카드의 종류에 관계없이 동일한 인터페이스를 제공하는 것처럼 CryptoAPI도 다양한 암호화 알고리즘을 골라 쓸 수 있지만 그렇다고 API의 사용법이 달라지거나 하지는 않는다.
마이크로소프트는 다양한 암호화 알고리즘들을 Crypto API에서 쓸 수 있도록 CSP(Cryptographic Service Pro vider)라는 확장 모듈 형식을 제공한다. 이 형식에 맞춰 제공되는 암호화 알고리즘은 그대로 CryptoAPI에서 쓸 수 있다. 즉, GDI에서의 그래픽 디바이스 드라이버에 해당하는 것이 CSP라고 생각하면 쉬울 것이다. 윈도우 운영체제와 함께 제공되는 기본 CSP로는 마이크로소프트 RSA Base Provider라는 것이 있다. 뒤에서 만들어 볼 예제 프로그램에서도 기본 CSP를 쓰게 된다. CSP에는 스마트 카드와 같은 하드웨어 컴포넌트와 같이 동작하는 것, 사용자의 디지털 사인을 수행하기 위해 사용자에게 비밀 사인키를 물어보는 것 등 여러 가지 종류가 있다.

CryptoAPI로 할 수 있는 일
CryptoAPI로 할 수 있는 일에는 크게 두 가지 종류가 있다. 하나는 말 그대로 암호화(encryption)이고 다른 하나는 사인닝(signing)이다. 전자의 암호화에는 당연히 반대 과정으로 해독(decryption)이 포함되는 것이고 후자의 사인닝에는 반대 과정으로 사인의 인증 과정이 포함된다. 앞서 설명한 암호화 방식으로는 대칭 암호화와 공개키 암호화 방식 모두를 지원한다.

CryptoAPI를 사용하려면
현재 CryptoAPI 자체는 API로만 제공되기 때문에 CryptoAPI를 직접 쓰려면 C, C++ 프로그래밍을 하는 것이 제일 적합하다. 비주얼 베이직이나 델파이, ASP 같은 개발 환경에서는 직접 사용하기에 좀 복잡할 것이고 정말로 사용해야할 일이 있다면 MFC 혹은 ATL에서 Crypto API를 써서 COM 컴포넌트로 만든 후 사용해야 할 것이다. 뒤의 예제 프로그램에서 ATL과 CryptoAPI로 간단한 암호, 해독 컴포넌트를 만들어 보고 이를 비주얼 베이직 예제 프로그램으로 테스트 해보도록 하겠다. 간혹 Cryp toAPI가 NT 이상에만 설치되어 있다고 잘못 알고 있는 경우도 있는데 CryptoAPI는 인터넷 익스플로러 3.0 이상이 설치된 시스템에는 모두 설치되어 있으며 인터넷 익스플로러의 ‘도움말'-‘Internet Explore 정보'를 열면 ‘암호화 수준'에서 확인해 볼 수 있다. 그런데 지난해까지만 해도 CryptoAPI에는 40비트와 128비트, 두 가지 크기의 암호화키가 따로 만들어져 미국과 캐나다 이외의 지역으로 나가는 CryptoAPI는 40비트만을 사용할 수 있었는데, 수출 금지가 미국 경제의 경쟁력을 저하시킨다는 업체의 주장에 밀려 올해 초부터 세계 어디서나 128비트 암호키를 쓸 수 있게 되었다. 만일 여러분의 인터넷 익스플로러가 40비트 암호키만 지원한다면 http://www.microsoft.com/windows/ie/download/128bit/intro.htm에서 업데이트 파일을 내려받을 수 있다.

CryptoAPI의 사용법
그러면 CryptoAPI의 사용법에 대해 간략히 살펴보자. 먼저 어떤 CSP를 사용할 것인지 선택해야 한다. 이 때 쓰는 함수가 CryptAcquireContext이다. CSP의 사용이 끝나면 CryptReleaseContext 함수를 호출해야 한다. 즉, CryptoAPI를 쓰는 코드는 CryptAcquireContext 함수 호출로 시작해서 CryptReleaseContext 함수의 호출로 끝나게 된다. 암호화 방식에 쓰는 키는 CrytoAPI의 CryptGenKey 함수나 CryptDeriveKey 함수를 사용해 만들거나 CryptImportKey 함수를 써서 기존의 키를 지정할 수 있다. 그리고 CryptoAPI와 관련된 모든 키들은 CSP내에 보관되며 CSP들이 키의 생성, 제거 관련 작업 수행을 책임진다. 이렇게 만들어지는 키 값 자체에 대해 알고 싶다면 CryptExportKey 함수를 이용해야 한다. 그러면 CSP 내부에 저장되어 있던 세션키 값을 알아낼 수 있으며 이 데이터는 'key BLOB'라는 형식을 갖는다. 이렇게 만들어낸 키는 CryptDestroyKey로 나중에 지워야 한다. 사실 키를 만들어 내는 방법은 굉장히 다양하다. 키를 만든 뒤 실제로 데이터를 암호화하는데 쓰이는 함수가 바로 CryptEncrypt이고 반대로 암호화된 데이터를 해독하는데 쓰이는 함수가 바로 CryptDecrypt이다. 참고로 Crypto API는 wincrypt.h 파일에 정의되어 있고 임포트 라이브러리는 advapi32.lib이다. CryptoAPI의 사용법을 익히는 가장 좋은 방법은 예제 프로그램을 보는 것이다. 다음과 같은 예제 프로그램이 제공된다.

·INITUSER 예제 : 디폴트 사용자의 사인용 공용키, 개인키와 키 교환용 공용키, 개인키 쌍을 생성하는 간단한 콘솔 프로그램(http:// msdn.microsoft.com/workshop/security/capi/CAP1615D.ZIP)
·ENCRYPT 예제 : 파일을 암호화하는 예제 콘솔 프로그램과 해독하는 콘솔 프로그램(http://msdn.microsoft.com/workshop/security/capi/CAP1615E.ZIP)
·ENUMALGS 예제 : 디폴트 CSP에서 제공하는 알고리즘을 보여주는 콘솔 프로그램(http://msdn.microsoft.com/workshop/security/ capi/CAP1615F.ZIP)
·SIGN 예제 : 파일을 사인하는 예제 콘솔 프로그램과 사인된 파일을 검증하는 콘솔 프로그램(http://msdn.microsoft.com/workshop/ security/capi/CAP1615G.ZIP)
·CFILER 예제 : 파일을 암호화하고 사인하는 윈도우 프로그램(http: //msdn.microsoft.com/workshop/security/capi/CAP1615H.ZIP)

예제 프로그램의 작성
CryptoAPI를 이용해서 대칭키 방식의 간단한 암호화와 해독 기능을 제공해주는 COM 컴포넌트를 만들어 보자. ActiveX 컨트롤이 아닌 COM 컴포넌트로 만들어 볼 것이기 때문에 ASP 프로그래밍에서도 쓸 수 있을 것이다. 개발 도구는 비주얼 C++ 6.0(ATL)과 비주얼 베이직을 사용하도록 한다. 컴포넌트 자체는 비주얼 C++에서 ATL을 써서 만들 것이며 컴포넌트의 테스트 프로그램은 비주얼 베이직으로 만들어 본다. 그리고 본 예제 프로그램은 필자가 애용하는 코드구루 사이트에 있는 예제(http://www. codeguru.com/misc/encrypt_decrypt.shtml)를 바탕으로 이를 컴포넌트화한 것임을 미리 밝혀 둔다. 게재를 허락해준 원저자인 Jorge Lodos(jorge.lodos@cigb. edu.cu)에게도 감사의 뜻을 전하는 바이다. 만들어 볼 컴포넌트는 다음과 같은 두 개의 메소드와 하나의 프로퍼티를 바탕으로 한다.

BYTE *Encrypt(String strData);
String Decrypt(BYTE *lpData);
String SecretKey;
Encrypt 메소드는 인자로 문자열을 받아서 이를 SecretKey로 지정된 키로 암호화한 다음 그 결과 암호화된 바이너리 데이터를 리턴한다. Descrypt 메소드는 반대로 해독하는 역할을 담당하는데 인자로 암호화된 바이너리 데이터를 받아서 이를 원래의 문자열로 해독한다. 프로퍼티인 SecretKey는 암호화, 해독에 쓸 키를 정하는데 사용된다. 키는 적당한 크기의 문자열을 임의로 지정하면 되는데 기본적으로 ‘z6M@#0i*'라는 값을 키로 갖는다. Secret Key로 지정된 값이 암호화, 해독에 사용되는 비밀키가 바로 쓰이는 것은 아니고 이 값을 바탕으로 해싱 객체를 만들어 나온 값을 비밀키로 쓴다. 그럼 이제부터 암호, 해독 컴포넌트를 만들어 보자. ATL을 어느 정도 사용해본 경험이 있어야 이해하기 쉬울 것이다.
프로젝트 뼈대 만들기
비주얼 C++를 띄우고 프로젝트 타입으로 ATL COM Wizard를 만든다. 프로젝트 이름으로는 Secure라고 입력한다. Step 1 of 1 다이얼로그에서 Server Type으로 Dynamic Link Library를 선택한다. 하단의 박스에서는 아무 것도 선택하지 않는다. Finish 버튼을 눌러 프로젝트를 생성한다. 다음으로 프로젝트에 실제 COM 컴포넌트를 추가하는 작업을 한다. Insert 메뉴의 New ATL Object 명령을 선택한다. Category 박스에서 Objects를 선택하고 우측의 Objects 박스에서는 Simple Object를 선택한다. 만일 ASP에서 사용하는데 최적화된 컴포넌트를 만들고 싶다면 ActiveX Server Component 항목을 선택하면 된다. Next 버튼을 누른다. C++ 박스의 Short Name 박스에 컴포넌트 이름으로 Message라고 입력한다. COM 박스 하단에 컴포넌트의 이름으로 Secure.Message가 주어졌음을 주목하자. 이것이 바로 이 컴포넌트의 ProgID가 되며 나중에 비주얼 베이직에서 이 컴포넌트를 생성하고자 할 때 이 이름을 써야 한다. 확인 버튼을 누른다.


이제 이 컴포넌트에 앞서 설명한 두 개의 메소드와 한 개의 프로퍼티를 더해 보자. 프로젝트 웍스페이스의 클래스뷰에서 IMessage를 오른쪽 클릭하고 Add Method 명령을 선택한다.

메소드 추가 다이얼로그에 추가할 메소드를 선언한다. Encrypt와 Decrypt 두 개의 메소드를 더하기 위해 위의 다이얼로그에 입력해야할 내용은 다음과 같다.

Method name 박스 Parameters 박스
Encrypt [in]BSTR bstrIn, [out, retval]VARIANT *pvarOut
Decrypt [in]VARIANT varIn, [out, retval]BSTR *pbstrOut
위에서 중요한 부분은 메소드의 리턴값을 인자로 지정해야 하며 그 인자의 속성으로는 [out, retval]을 주어야 한다는 점이다. 메소드의 리턴값이 되는 인자는 반드시 마지막 인자라야 한다. 이제 프로퍼티 추가를 위해 IMessage를 다시 오른쪽 클릭하고 Add Property 명령을 선택해서 SecretKey 프로퍼티를 추가한다.

SecretKey 프로퍼티의 구현
SecretKey 프로퍼티의 값을 저장하는데 사용할 문자열 배열을 CMessage 클래스 안에 다음과 같이 m_szSe cretKey라는 이름의 64바이트 배열로 정의하고 생성자 안에서 이를 디폴트 값으로 초기화한다.

class ATL_NO_VTABLE CMessage :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CMessage, &CLSID_Message>,
public IDispatchImpl<IMessage, &IID_IMessage,
&LIBID_SECURELib>
{
public:
TCHAR m_szSecretKey[64]; // +

CMessage()
{
_tcscpy(m_szSecretKey, "z6M@#0i*"); // +
}
위의 코드를 보면 TCHAR라는 타입이 있는데 이는 유니코드 프로젝트이냐 아니냐에 따라서 일반 char 문자열로 정의되기도 하고 유니코드 문자열로 정의되기도 한다. 프로젝트가 어떤 유니코드 프로젝트인지 알고 싶다면 Build 메뉴의 Set Active Configuration 명령을 실행해 보면 된다.
프로젝트의 형태는 크게 유니코드 프로젝트(그림 5에서 Unicode라는 말이 들어간 모든 프로젝트)와 비유니코드 프로젝트로 나눠볼 수 있다. 유니코드 프로젝트는 프로젝트에 _UNICODE라는 상수가 정의되며 이 모드에서 컴파일 된 프로그램은 윈도우 NT나 윈도우 2000에서만 동작한다. 유니코드에 대한 보다 자세한 설명은 이번 호의 For a computer guru를 참고하기 바란다. SecretKey 프로퍼티의 get 함수와 put 함수의 내부를 다음과 같이 채운다. ATL 프로그래밍을 많이 한 사람이라면 자주 보았을 유니코드와 일반 문자열 사이의 변환 코드다.

STDMETHODIMP CMessage::get_SecretKey(BSTR *pVal)
{
USES_CONVERSION;
*pVal = SysAllocString(T2OLE(m_szSecretKey));
return S_OK;
}

STDMETHODIMP CMessage::put_SecretKey(BSTR newVal)
{
USES_CONVERSION;
_tcscpy(m_szSecretKey, OLE2T(newVal));
return S_OK;
}
put_SecretKey 함수의 경우 문자열 복사를 위해 사용되는 함수가 _tcscpy라는 함수인데 이 함수는 유니코드 프로젝트에서는 유니코드 문자열 복사를 위한 wcscpy로 치환되고 비유니코드 프로젝트에서는 ANSI 문자열 복사를 위해 우리에게 익숙한 strcpy 함수로 치환된다.

Encrypt 메소드의 구현
다음으로 Encrypt 메소드를 구현해 보자. 먼저 코드를 살펴보자. 일단 CryptoAPI의 사용법을 익히기에 앞서 COM으로 대량의 바이너리 데이터를 넘기는 방법부터 익혀야 한다. 이는 VARIANT라는 타입과 SAFEARRAY라는 타입을 이해해야 하기 때문에 상당히 어려운 부분이다. COM 컴포넌트에서 표준 타입(배열 제외) 이외의 타입을 메소드의 인자로 쓰려면 VARIANT라는 타입을 사용해야 하며 배열을 인자로 쓰고 싶다면 SAFEARRAY와 VARIANT라는 타입을 같이 사용해야 한다. 리스트 1의 코드를 잘 활용하면 COM 컴포넌트에서 대량의 바이너리 데이터를 인자로 받아 처리하는 방법을 알 수 있을 것이다. VARIANT와 SAFEARRAY에 대한 설명도 이번 호 For a computer guru를 참고하기 바란다.
리스트 1의 코드를 보면 실제 암호화 작업은 Encrypt Data 함수에서 일어남을 알 수 있다. 이 함수의 첫 번째 인자로 암호화 대상이 되는 문자열에 대한 포인터를 지정하고 두 번째 인자로 암호화된 결과를 받아올 포인터를 지정한다. 마지막 세 번째 인자로 암호화에 사용될 비밀키를 지정한다.
리스트 2의 코드를 보면 알겠지만(아마 도움말 찾아가며 좀 열심히 보아야 할 것이다) 이 비밀키가 바로 진짜 비밀키가 되는 것은 아니고 이 값을 해싱하여 실제로 암호화에 사용될 비밀키를 생성해낸다.
Decrypt 메소드의 구현
해독 메소드는 위의 암호화 메소드와 반대 과정을 거친다. 첫 번째 인자로 넘어온 암호화되어 있는 바이너리 데이터를 받아 이를 문자열로 변경하여 두 번째로 리턴 한다.
소스 코드는 리스트 3과 같다.
Encrypt 메소드와 마찬가지로 Decrypt 메소드에서 실제로 CryptoAPI를 사용하는 부분은 DecryptData라는 별개의 함수에 존재한다. DecryptData의 소스 코드는 리스트 4와 같다. 해독에 쓸 비밀키를 만들어 내는 부분은 앞서 본 EncryptData에서와 같다. 앞서 얘기한 바와 같이 대칭키 방식이므로 비밀키가 같다.

리스트 1 : CMessage의 Encrypt
STDMETHODIMP CMessage::Encrypt(BSTR bstrIn, VARIANT *pvarOut)
{
USES_CONVERSION;

LPCTSTR lpStrIn = OLE2T(bstrIn);
// 문자열의 총 크기를 바이트 수로 계산한다
int nLimit = _tcslen(lpStrIn)*sizeof(TCHAR);

// 문자열을 암호화한 바이너리 데이터가 저장될 공간을 만든다
SAFEARRAY *safeArray;
SAFEARRAYBOUND rgsabound;
// 바이너리 데이터의 크기를 지정한다
rgsabound.lLbound = 0;
rgsabound.cElements = nLimit;
// 1 바이트 데이터를 원소로 갖는 1차원 배열을 nLimit개만큼 생성한다
safeArray = ::SafeArrayCreate(VT_UI1, 1, &rgsabound);
// 위의 배열의 포인터를 얻는다
if (SafeArrayLock(safeArray) == S_OK)
{
BYTE *pByte = (BYTE *)safeArray->pvData;
// lpStrIn이 가리키는 데이터를 암호화해서 pByte에 넣는다
if (!EncryptData((LPTSTR)lpStrIn, pByte, m_szSecretKey))
{
// Safe Array의 포인터를 제거한다
SafeArrayUnlock(safeArray);
SafeArrayDestroy(safeArray);
safeArray = NULL;
}
else // Safe Array의 포인터를 락만 푼다
SafeArrayUnlock(safeArray);
}
else
return S_FALSE;
// COM으로 위의 배열 데이터를 넘기기 위해 VARAINT 타입의 변수를 하나 만든다
VARIANT varOut;
::VariantClear(&varOut);
if (safeArray == NULL)
varOut.vt = VT_EMPTY;
else
{
varOut.vt = VT_ARRAY | VT_UI1;
varOut.parray = safeArray;
}
*pvarOut = varOut;
return S_OK;
}


리스트 2 : EncryptData
BOOL EncryptData(LPTSTR strIn, LPBYTE byteOut, LPCTSTR lpKey)
{
BOOL bResult = TRUE;
HCRYPTPROV hProv = NULL;
HCRYPTKEY hKey = NULL;
HCRYPTHASH hHash = NULL;
DWORD dwLength;

// 디폴트 프로바이더에 대한 핸들을 얻어 온다. 아래의 함수는 특정 CSP를 선택하는데
사용된다
if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0))
{
// 해싱 객체를 만든다
if (CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash))
{
dwLength = sizeof(TCHAR) * _tcslen(lpKey);
// lpKey 데이터를 위의 해싱 객체에 넘긴다
if (CryptHashData(hHash, (BYTE *)lpKey, dwLength, 0))
{
// 위의 결과를 바탕으로 암호화에 사용될 세션 키를 생성한다
if (CryptDeriveKey(hProv,CALG_RC4,hHash,
CRYPT_EXPORTABLE,&hKey))
{
// 암호화할 대상의 크기를 알아낸다
dwLength = sizeof(TCHAR)*_tcslen(strIn);
// 암호화 대상 데이터를 복사한다
memcpy(byteOut, strIn, dwLength);
// 데이터를 암호화한다
if (!CryptEncrypt(hKey, 0, TRUE, 0, byteOut,
&dwLength, dwLength))
bResult = FALSE;
// 세션 키를 제거한다
CryptDestroyKey(hKey);
}
else
bResult = FALSE;
}
else
bResult = FALSE;
// 해싱 객체를 제거한다
CryptDestroyHash(hHash);
}
else
bResult = FALSE;
CryptReleaseContext(hProv, 0);
}
else
bResult = FALSE;
return bResult;
}
리스트 3 : CMessage의 Decrypt
STDMETHODIMP CMessage::Decrypt(VARIANT varIn, BSTR *pbstrIn)
{
USES_CONVERSION;
// 넘어온 데이터가 바이너리 데이터인지 확인한다
if (varIn.vt == (VT_ARRAY|VT_UI1))
{
SAFEARRAY *parr = varIn.parray;
LPBYTE pDataIn = NULL;
TCHAR *pStrTemp = NULL;
long plLbound, plUbound;
// 넘어온 바이너리 배열 데이터를 접근하기 위한 정보를 얻는다
SafeArrayAccessData(parr , (void **)&pDataIn);
SafeArrayGetLBound(parr, 1, &plLbound);
// 바이너리 배열의 시작 인덱스를 알아낸다
SafeArrayGetUBound(parr, 1, &plUbound);
// 바이너리 배열의 끝 인덱스를 알아낸다
// 해독한 바이너리 데이터를 저장할 공간을 할당한다
pStrTemp = new TCHAR[(plUbound - plLbound)/sizeof(TCHAR)
+ 2];
if (pStrTemp)
{
// 암호화된 바이너리 데이터를 해독한다.
DecryptData(pDataIn, plUbound-plLbound+1, pStrTemp,
m_szSecretKey);
// 바이너리 배열 데이터에 대한 접근을 종료한다
SafeArrayUnaccessData(parr);
// 해독된 데이터를 리턴한다
*pbstrIn = SysAllocString(T2OLE(pStrTemp));
delete [] pStrTemp;
}
else
{
SafeArrayUnaccessData(parr);
return E_OUTOFMEMORY;
}
}
else
return E_INVALIDARG;
return S_OK;
}

테스트 프로그램의 작성
앞서 만든 컴포넌트를 테스트하기 위한 프로그램은 비주얼 베이직으로 작성되었으며 실행했을 때 화면은 그림 6과 같다.
그림 6에서 볼 수 있듯이 좌측 창에 암호화할 텍스트 데이터를 입력하고 암호화 버튼을 누르면 암호화된 바이너리 데이터가 우측 창에 표시된다. 이 때 바이너리 데이터는 한 바이트씩 잘라내서 10진수로 표시된다. 해독 버튼을 누르면 암호화되었던 데이터가 다시 해독되어 메시지 박스로 그 내용이 표시된다. 이 과정에서 디폴트가 아닌 비밀키를 사용하고 싶다면 우측 상단의 비밀키 박스에 다른 문자열을 입력하면 된다. 이제 소스 코드를 살펴보자. 먼저 비주얼 베이직을 실행하고 프로젝트 메뉴의 참조(Reference) 명령을 실행한다. 이제 다이얼로그가 뜨는데 거기서 "Secure 1.0 Type Library"를 선택하고 OK를 누른다. 앞서 만든 컴포넌트에 대한 타입 정보를 비주얼 베이직에게 알리는 것이다. 이제 전역 변수로 다음과 같은 변수를 선언한다.

Dim objSecure As SECURELib.Message
Dim binData
Dim bByte As Byte
다음으로 폼의 모양을 그림 6의 예제 프로그램에서처럼 만든다. 암호화 버튼의 Name으로는 cmdEncrypt를 지정하고 해독 버튼의 Name으로는 cmdDecrypt를 지정한다. 그리고 좌측 텍스트 박스의 Name으로는 txtData를 지정하고 우측 텍스트 박스의 Name으로는 txtEncode를 지정한다. 암호화 버튼의 처리 함수를 다음과 같이 만든다. 아래의 코드에서 바이너리 데이터의 크기를 알아내는데 LenB라는 함수를 사용했다는 점과 CreateObject 함수로 컴포넌트를 생성할 때 컴포넌트의 ProgID를 지정한다는 점도 알아두자.

Private Sub cmdEncrypt_Click()

Set objSecure = CreateObject("Secure.Message")
' 암호 컴포넌트를 만든다
If txtSecretKey.Text <> "" Then ' 비밀키가 혹시 지정되었는지 살핀다
objSecure.SecretKey = txtSecretKey.Text
End If
' 좌측 창에 입력된 데이터를 암호화한다
binData = objSecure.Encrypt(txtData.Text)
Set objSecure = Nothing ' 암호 컴포넌트를 제거한다
' 암호화된 데이터를 바이트별로 십진수로 우측 창에 표시한다
txtEncode.Text = ""
For i = 0 To LenB(binData) - 1
bByte = binData(i)
txtEncode.Text = txtEncode.Text + CStr(bByte)
Next

End Sub

해독 버튼의 처리 함수를 다음과 같이 만든다. 앞서 만든 암호화 버튼의 처리 함수와 흡사하다.

Private Sub cmdDecrypt_Click()

Set objSecure = CreateObject("Secure.Message")
' 암호 컴포넌트를 만든다
If txtSecretKey.Text <> "" Then ' 비밀키가 혹시 지정되었는지 살핀다
objSecure.SecretKey = txtSecretKey.Text
End If
strOut = objSecure.Decrypt(binData) ' 해독한다
Set objSecure = Nothing ' 암호 컴포넌트를 제거한다
MsgBox strOut ' 결과를 메시지 박스로 보여준다

End Sub
다음 호에는 인터넷 익스플로러에서의 보안 영역(Security Zone)과 관련 인터페이스에 대해 설명하도록 하겠다. 아마 웹 프로그래밍을 하면서 ActiveX 컨트롤을 내려 받아 쓰는 일을 하는 사람이라면 많은 도움이 될 것이라 여겨진다.

리스트 4 : DecryptData
BOOL DecryptData(LPBYTE byteIn, long lSize, LPTSTR strOut,
LPCTSTR lpKey)
{
BOOL bResult = TRUE;
HCRYPTPROV hProv = NULL;
HCRYPTKEY hKey = NULL;
HCRYPTHASH hHash = NULL;
DWORD dwLength;

// 디폴트 프로바이더에 대한 핸들을 얻어 온다
if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
// 해싱 객체를 만든다
if (CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash))
{
dwLength = sizeof(TCHAR)*_tcslen(lpKey);
// lpKey 데이터를 위의 해싱 객체에 넘긴다
if (CryptHashData(hHash, (BYTE *)lpKey, dwLength, 0))
{
// 해싱 객체에서 비밀 키를 생성한다
if (CryptDeriveKey(hProv, CALG_RC4, hHash,
CRYPT_EXPORTABLE, &hKey))
{
// 해독할 데이터의 크기를 구한다
dwLength = lSize;
// 비밀키를 이용해 데이터를 해독한다
if (!CryptDecrypt(hKey, 0, TRUE, 0,
(BYTE *)byteIn, &dwLength))
bResult = FALSE;
// 해독된 데이터를 복사한다
_tcsncpy(strOut, (LPTSTR)byteIn, dwLength/
sizeof(TCHAR));
strOut[dwLength/sizeof(TCHAR)] = 0;
CryptDestroyKey(hKey); }
else
bResult = FALSE; }
else
bResult = FALSE;
// 해시 객체를 제거한다
CryptDestroyHash(hHash);
}
else
bResult = FALSE;
CryptReleaseContext(hProv, 0);
}
else
bResult = FALSE;

return bResult;
}


암호 데이터 해킹
암호화된 데이터를 깨는 가장 분명한 방법은 단순하게 모든 가능한 키를 다 사용해 보는 것(brute-force attack)이다. 물론 이 경우 암호화에 사용된 알고리즘을 알아야 한다. 56비트의 키를 사용하는 DES 알고리즘을 생각해 보면 모두 2의 56승 개의 키가 나올 수 있다. 여기에 모든 키 조합을 적용해 볼만한 시간적 여유가 있는 사람이라면 암호화된 데이터를 깰 수도 있을 것이다. 즉 암호화하는 사람의 입장에서는 키의 크기를 늘려서 해독하는데 엄청난 시간이 걸리게 하여 아예 시도조차 못하게 하거나 시도를 포기하게 만드는 수밖에 없다.
참고문헌 8에 의하면 1995년에 백만 달러를 들여 키 해킹 전용 하드웨어를 만들 경우 40비트 키 알고리즘의 경우 깨는데 약 0.2초면 되고 56비트 키의 경우 3.6시간이 걸리고 112비트 키의 경우에는 1013년이 걸리며 128비트 키의 경우에는 1018년이 걸린다고 계산하였다.
또 그간의 기술 발전 속도를 감안해 보면 2000년에 백만 달러를 들여 키 해킹 전용 하드웨어를 만들면 40비트의 경우 0.02초가 걸리고, 56비트의 경우에는 21분이, 112비트 키의 경우에는 1012년이 걸리며 128비트 키의 경우에는 1017년이 걸린다고 하였다. 이 예측 수치를 보면 알 수 있듯이 100비트 이상의 키를 사용할 경우 암호화된 데이터를 해킹할 확률은 거의 제로에 가깝지만 40비트나 56비트 키의 경우에는 사용된 알고리즘과 키의 비트 수를 해커가 알면 어렵지 않게 해독해 낼 수 있음을 알 수 있다.
물론 이 해독 시간은 전용 하드웨어를 사용한 것이므로 소프트웨어만으로 해결할 경우에는 위에서 제시한 것보다 더 많은 시간이 걸릴 것이다. 참고로 DES 같은 알고리즘은 키의 크기가 56비트로 제한되어 있지만, RC2/RC4나 RSA 공용키 알고리즘의 경우에는 키의 크기를 가변적으로 지정할 수 있다.


참고문헌 및 웹 사이트
1. http://msdn.microsoft.com/library/
(MSDN Library의 CryptoAPI 레퍼런스)
2. http://msdn.microsoft.com/workshop/
(MSDN Workshop의 Security and Cryptography)
3. http://www.cs.auckland.ac.nz/~pgut001/links.html
(보안 관련 링크 모음)
4. http://www.ssh.fi/tech/crypto/protocols.html
(보안 프로토콜 설명)
5. http://home.netscape.com/security/techbriefs/ssl.html
(SSL 설명)
6. http://www.verisign.com/cgi-bin/go.cgi?a=w008700750003000
(Guide to securing your site for business)
7. http://www.verisign.com/whitepaper/enterprise/overview/
index.html
(Encryption and Digital Certificates)
8. Network & Internet Security, 1996, AP Professional, Vijay Ahuja


  • 관련 링크
  • [분류: 보안 인쇄용 페이지 본문 email로 보내기 ]

    <  오빠 달려.. asp cache object | The COM Macro-Architecture Topology  >
    CryptoAPI를 ASP에서 사용하기 | 답장: 1개 | 본문에 답장
    정렬 :  
    답장 EzDoum 2003년 09월 29일 오전 12:01 [ 이글에 답장 | 본문에 답장 | 책갈피 ]
    ASP Component Guidelines
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnserv/html/server01242000.asp


    [수정]

    CryptoAPI를 ASP에서 사용하기 | 답장: 1개 | 본문에 답장
    정렬 :  

    답장 쓰기
    글을 올리시려면 로그인 (사용자 등록) 하셔야 합니다.

    검색
    Google

    분류
    ·공지 (6)
    ·인터넷 (87)
    ·하드웨어 (260)
    ·C/C++ (65)
    ·어셈블리 (7)
    ·리눅스 (136)
    ·리눅스 커널 (67)
    ·윈도우즈 (25)
    ·데이터베이스 (20)
    ·보안 (16)
    ·.NET (25)
    ·그래픽 (13)
    ·책소개 (42)
    ·호기심 천국 (80)
    ·잡담 (111)
    ·사랑 (3)

    전체 본문수: 963
    전체 답장수: 525


    분류 : 보안
    최근글
    최근글
    가장 많이 읽은 글
    ·ReverseEngineering - 종합선물세트 (2)
    뜨거운 감자
    ·ReverseEngineering - 종합선물세트 (2)

    EzDoum투표
    이지도움 어때요?
    이게 뭐야. 다시 안올란다. --;
    아이 좋아라~ +_+;
    관심없다.
    먼가는 있는거 같은데 뭐하는 곳이지?
    기타 (자유게시판에 글로 남겨 주세요)
    [ 결과 | 투표 ]

    랜덤 링크
    http://kldp.net


     Home ^ BACK TO TOP ^ EzDoum - 도움이 필요하세요~??
     Powered by KorWeblog 1.5.8 Copyleft © 2001 EzDoum, 관리자: EzDoum