인디노트

OpenSSL API를 이용한 보안 프로그래밍, Part 3: 보안 서비스 제공하기 (한글) 본문

인증기술/PKI 기술

OpenSSL API를 이용한 보안 프로그래밍, Part 3: 보안 서비스 제공하기 (한글)

인디개발자 2018. 10. 8. 08:31

OpenSSL API를 이용한 보안 프로그래밍, Part 3: 보안 서비스 제공하기 (한글)

OpenSSL에 필요한 기능 추가하기

developerWorks
문서 옵션

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.

샘플 코드

영어원문

영어원문


제안 및 의견
피드백

난이도 : 중급

Kenneth Ballard, Software Engineer, MediNotes Corp.

2007 년 10 월 02 일

보안 서버 애플리케이션이 없이는, 보안 클라이언트 애플리케이션도 존재하지 않습니다. OpenSSL을 사용하여 보안 서버 애플리케이션들을 생성할 수 있고, 문서화가 완전하지 않아도 어렵지는 않습니다. 본 3개 시리즈의 Part 1에서 설명한 개념을 기반으로 보안 서버 애플리케이션을 구현하는 방법을 배워봅시다.

본 시리즈의 두 Part에서는 OpenSSL을 이용한 클라이언트 측 애플리케이션 구현에 대해 설명했다. Part 1에서는 OpenSSL을 사용한 기본 보안 클라이언트를 구현하는 방법을 설명했고, Part 2에서는 디지털 인증서에 대해 자세히 설명했다. 본 기술자료에 대해 이메일과 긍정적인 피드백을 받고 나서, 다음에는 어떤 논의를 이어가야 할지 명확해 졌다.

소셜 북마크

mar.gar.inmar.gar.in
diggDigg
del.icio.usdel.icio.us
SlashdotSlashdot

서버는 네트워크와 인터넷에 파일과 장치와 같은 리소스로의 액세스를 제공한다. 가끔씩, 이러한 서비스들을 보안 채널을 통해서 제공해야 할 때도 있다. OpenSSL을 사용하여 보안 및 오픈 채널을 사용하여 서비스를 만들 수 있다.

OpenSSL을 이용하여 기본적인 서버 애플리케이션을 생성하는 것은 기본적인 클라이언트 애플리케이션을 구현하는 것과 본질적으로 거의 같다. 차이점은 비교적 적다. 분명한 것은 서버는 아웃고잉 연결을 생성하는 대신 인커밍 연결을 수락하도록 설정되어야 한다는 점이다. 본 시리즈 Part 2의 디지털 인증서에 대한 글을 기억해 보면 알겠지만, 서버는 핸드쉐이크 동안 사용되는 보안 인증서도 제공해야 한다.

기다리기

서버들은 인커밍 연결을 그저 기다리는 것이 전부이다. 결국, 이것은 서버가 존재하는 이유이다. 웹 서버들은 브라우저가 페이지를 요청하기를 기다리고, FTP 서버들은 클라이언트가 파일을 요청하기를 기다리며, 채팅 서버는 인커밍 채팅 클라이언트 연결을 기다린다. 서버는 그저 기다릴 뿐이다.

핸드쉐이크의 관점에서 서버가 동전의 다른 쪽에 있다는 것을 제외하고는 보안 클라이언트와 서버 통신간에는 거의 차이가 없다. 모든 것이 같다.

따라서, 여러분이 OpenSSL을 이용하여 클라이언트 애플리케이션을 작성하는 방법만 알고 있다면 OpenSSL을 사용하여 보안 서버 애플리케이션을 구현하는 것은 식은죽 먹기다. (그렇지 않다면, 본 시리즈 Part 1, "API의 개요"에서 OpenSSL 라이브러리 설정 방법을 배우기 바란다.)




위로


두 가지 형태의 식별

SSL 콘텍스트

이 글에서 사용할 SSL 콘텍스트를 설정하려면 다음 코드를 사용하라. 이 함수는 에러 시 NULL을 리턴할 것이다.

SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method());

또는 두 개의 식별 방법이라고도 할 수 있다.

서버는 핸드쉐이크 동안 사용될 보안 인증서를 제공할 책임이 있다. 완전한 서버 인증서는 두 부분, 공개 키(public key)와 개인 키(private key)로 구성된다. 공개 키는 클라이언트로 보내지는 것이고, 개인 키는 비밀로 유지된다.

트러스트 인증서가 클라이언트 애플리케이션용 라이브러리에 제공되어야 하는 것처럼, 서버 키들도 서버 애플리케이션용 라이브러리에 제공되어야 한다. 이를 제공하는 여러 함수들이 있다.


Listing 1. 서버 인증서를 로딩하는 함수들 
SSL_CTX_use_certificate(SSL_CTX *, X509 *)
SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, unsigned char *d);
SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type);

ASN1 함수는 ASN1-encoded 디지털 인증서를 지정된 메모리 위치에서 SSL 콘텍스트로 로딩한다. 첫 번째 함수는 주어진 메모리 구조에 제공된 X.509 인증서를 로딩하지만, 마지막 함수, _file은 PEM-encoded 디지털 인증서를 파일에서 로딩한다. 이 함수의type 매개변수는 DER-encoded 인증서가 로딩되도록 한다.

개인 키를 로딩하려면, 다음 함수들 중 하나를 사용한다.


Listing 2. 개인 키 로딩을 위한 함수들 
SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey);
SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, unsigned char *d, long len);
SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type);
SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa);
SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, unsigned char *d, long len);
SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx, const char *file, int type);




위로


몇 가지 필수 인터랙션

어떤 개인 키라도 암호화 되어 저장된다. 문제는, 인증서를 로딩하는 함수들이 암호화 된 인증서에 패스워드를 요청하지 않는다는 점이다. 대신, OpenSSL은 패스워드를 얻을 때 콜백 메커니즘을 제공한다.

콜백의 포맷은 다음과 같다.


Listing 3. 콜백 포맷
int password_callback(char *buf, int size, int rwflag, void *userdata);

이 글의 목적상, 마지막 매개변수, userdata는 필요하지 않다. 함수가 호출되기 전에 버퍼가 할당되기 때문에 여러분에게는 버퍼의 크기에 대한 제어권이 없다.

서버 개인 키

서버 인증서의 경우, 개인 키는 암호화 되어 저장되어서는 안된다. 그렇지 않으면, 관리자가 패스워드를 너무 자주 입력해야 한다.

매개변수 rwflag는 읽기/쓰기 플래그이다. 패스워드가 정보를 암호화 하는데 사용되는지(rwflag = 1) 아니면 정보를 해독하는데 사용되는지(rwflag = 0) 여부를 프로그래밍 방식으로 결정하기 위해 사용된다. 데이터를 암호화 하기 위해 패스워드를 요청하기 위해 콜백이 사용된다면, 패스워드를 두 번 요청하여 오타를 방지할 수 있다.

패스워드는 인증서가 로딩될 때 단 한번 요청되기 때문에, 해독되고 메모리에 저장될 수 있다. 사용자에게서 패스워드를 획득하는 방법은 구현에 전적으로 달려있다.

패스워드 콜백 함수를 생성했다면, 이것을 SSL_CTX_set_default_passwd_cb를 사용하여 SSL 콘텍스트에 설치한다.


Listing 4. 콜백 함수 설치하기 
/* ctx is a pointer to a previously created SSL context, and cb is the pointer
 * to the callback function you created.
 */

SSL_CTX_set_default_passwd_cb(ctx, cb);




위로


키 실행하기

콜백 함수가 생성되어 사용자에게 패스워드를 묻는 프롬프트가 생겼으니, 인증서를 실제로 가져오는 함수가 사용될 수 있다. 인증서는 기존 메모리 구조나 파일에서 가져올 수 있다.

Apache HTTP Server Project의 디지털 인증서처럼, 디지털 인증서를 다루는 일반적인 방식에 더하여 인증서를 로딩하는 방법을 설명하겠다. 본 시리즈의 Part 1을 읽었다면, 인증서를 로딩하는 것은 트러스트 스토어가 이전 기술 자료의 데모에서 로딩된 방식과 비슷하다는 것을 알 수 있을 것이다.

클라이언트로 보내지는 것 중 하나인 공개 인증서로 시작하겠다.


Listing 5. 공개 인증서 로딩하기 
/**
 * ctx is the SSL context created earlier
 */

if(SSL_CTX_use_certificate_file(ctx, "/path/to/certificate.pem", SSL_FILETYPE_PEM) < 1)
{
    /* Handle failed load here */
}

공개 인증서가 로딩된 후에, 개인 인증서도 로딩되어야 한다. 이 부분은 핸드쉐이크 동안 필요하다. 클라이언트는 공개 인증서로 암호화 된 정보를 서버로 보낼 것이기 때문이다. 이 데이터는 개인 키를 사용해서 해독될 수 있다. 일관성을 유지하기 위해, 파일에서 키를 로딩한다.


Listing 6. 개인 키 로딩하기
if(SSL_CTX_use_PrivateKey_file(ctx, "/path/to/private.key", SSL_FILETYPE_PEM) < 1)
{
    /* Handle failed load here */
}




위로


설정 끝내기

콘텍스트(SSL 콘텍스트 사이드바 참조)를 설정하고 키를 로딩했다면, 이제는 BIO 객체를 생성하여 설정을 끝낼 차례이다. Part 1에서는 OpenSSL의 BIO 라이브러리를 사용하여 SSL과 비 SSL 통신 모두를 구축하는 방법을 설명했다. 일관성을 유지하기 위해, 같은 것이 이 곳에서도 수행된다.


Listing 7. BIO 포인터
BIO *bio, *abio, *out;

세 개의 BIO 객체들? 왜 세 개씩이나 필요한가? 다 이유가 있다. 나를 믿어라. (기억하는가? 신뢰와 보안은 함께 한다는 것을..)

우선, bio는 SSL 콘텍스트에서 생성될 메인 BIO 객체이다. 두 번째 객체인 abio는 accept BIO로서 인커밍 연결을 수락한다. 세 번째 BIO인 out을 통해 서버가 클라이언트와 통신한다.


Listing 8. 메인 BIO 객체 설정하기 
bio = BIO_new_ssl(ctx, 0);
if(bio == NULL)
{
    /* Handle failure here */
}

/* Here, ssl is an SSL* (see Part 1) */

BIO_get_ssl(bio, &ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

BIO 객체 설정은 클라이언트 연결을 설정하는 것과는 다르다. 클라이언트 연결이 BIO_new_ssl_connect를 사용하여 설정되는 것이라는 것을 Part 1에서 배웠다.

여기에서, 설정은 두 개의 매개변수, SSL_CTX 객체에 대한 포인터와 플래그와 함께 BIO_new_ssl을 사용하여 수행된다. 이 플래그는 OpenSSL에게 어떤 종류의 BIO 객체를 구현해야 하는지를 말해준다. 서버의 경우, 0, 클라이언트의 경우 1이다. 이 코드는 클라이언트 연결을 설정하는 것이므로, 플래그는 0으로 설정된다.


Listing 9. accept BIO 설정하기 
abio = BIO_new_accept("4422");
BIO_set_accept_bios(abio, bio);

BIO_do_connect는 클라이언트 연결에 BIO를 생성하는데, BIO_new_accept는 서버 연결용 BIO를 생성한다. 여기에는 단 하나의 인자만 취하는데, 리스닝용 포트이다.

이것은 보안 연결을 리스닝 할 것이므로, 보안 BIO를 이 accept BIO로 연결시켜야 한다. 여기에서 두 번째 함수 호출, BIO_set_accept_bios가 작동한다. 이것은 이전에 생성된 SSL BIO를 accept BIO로 연결한다.

이 함수는 SSL BIO를 해제할 필요도 없앤다. accept BIO가 제거되면 자동으로 해제된다.




위로


앉아서 기다리기

서버는 어부와 같다. 클라이언트가 물릴 때까지 앉아서 기다린다. 서버는 그저 인커밍 연결을 기다릴 뿐이다.

여러분이 Winsock이나 BSD Sockets를 사용해본 경험이 있다면, accept 함수에 대해서도 알 것이다. OpenSSL 상대자는 BIO_do_accept이다. 여기에서는 accept를 호출하면 되었지만, 이 경우에는 앉아서 기다리기 전에 BIO_do_accept를 두 번 호출해야 한다.


Listing 10. 서버에게 앉을 것을(sit) 명령하기 
/* First call to set up for accepting incoming connections... */

if(BIO_do_accept(abio) <= 0)
{
    /* Handle fail here */
}

/* Second call to actually wait */

if(BIO_do_accept(abio) <= 0)
{
    /* Handle fail here */
}

/* Any other call will cause it to wait automatically */

BIO_do_accept에 대한 첫 번째 호출은 BIO가 인커밍 연결을 수락하도록 설정한다. 두 번째 호출은 실제로 이것이 앉아서 기다리도록 하는데 필요하다. 이후에는 그저 기다리면 된다.




위로


인커밍 호출에 응답하기

BIO_do_accept는 인커밍 연결을 받으면 1을 리턴한다. 하지만, accept BIO를 통해서 통신할 수 없다. 대신, OpenSSL은 BIO_pop을 사용하여 accept BIO에 필적하는 또 다른 BIO를 생성한다.


Listing 11. 연결하기
out = BIO_pop(abio);

if(BIO_do_handshake(out) <= 0)
{
    /* Handle fail here */
}

accept BIO에서 인커밍 연결을 해제한 후에, 핸드쉐이크는 BIO_do_handshake로의 호출을 사용하여 처리되어야 한다. 이전 섹션의 설정이 성공했다면, 핸드쉐이크도 성공할 것이다.

서버는 BIO 라이브러리에 사용할 수 있는 다양한 읽기 및 쓰기 함수들을 통해 클라이언트와 통신한다. 이 부분은 Part 1에서 설명했으므로 참조하기 바란다.




위로


최상의 서비스 제공하기

결국, OpenSSL로 보안 서버 애플리케이션들을 구현하는 것은 실행 방법을 이해하기만 한다면 어렵지 않다. 이 코드 샘플을 확장하여 완벽한 SSL 서버 애플리케이션들을 여러분의 필요에 맞게 구현할 수 있다. 다시 한번 말하지만, 다운로드 섹션에서 제공하는 코드 샘플들은 최대로 간단한 것이고 실험을 목적으로 한 것이다. SSL 서버 애플리케이션들을 실제로 구현하기 전에, 최신 보안 권고 사항을 읽어보기 바란다.





위로


다운로드 하십시오

설명이름크기다운로드 방식
이 기술자료의 샘플 코드openssl3.tar.gz4KBHTTP
다운로드 방식에 대한 정보


참고자료

교육

제품 및 기술 얻기

토론


필자소개

Kenneth는 MediNotes Corp.(West Des Moines, Iowa)의 소프트웨어 엔지니어이다. 페루, 나브라스카에 있는 Peru State College에서 경영학 학사 학위를 받았다. Southwestern Community College에서 컴퓨터 프로그래밍의 Associate of Science도 보유하고 있다. 여러 애플리케이션 및 프로그래밍 라이브러리를 작성했다.


반응형
Comments