https서버와 클라이언트를 OpenSSL로 직접 구현하려면..

sisap의 이미지

안녕하세요?

네트웍과 보안쪽을 공부하고 있는 보안 초짜 입니다.

https에 접속할수 있는 클라이언트 프로세스를 만들어 보려고 합니다. 웹브라우져를 사용하면 그냥 https에 접속할 수 있겠지만, 웹브라우져가 아닌 그냥 클라이언트 프로세스에 OpenSSL을 사용하여 https에 직접 접속해 보려고 하는거죠.

구글 gmail도 https서버던데 이러한 일반적인 https 접속을 웹브라우져로 하지 않고 클라이언트로 직접 붙어 보려고 하는데..

OpenSSL라이브러리에서 제공하는 기본적인 TCP/IP 소켓을 사용하여 서버-클라이언트간에 서버 인증을 하여 메시지를 주고 받는 정도의 예제 소스도 KLDP에서 어떤분이 올려주신 소스를 기반으로 테스트를 해 봤습니다. 이 소스의 어떤 부분만 살짝 고쳐주면 https 클라이언트도 구현할수 있을것 같은데, 제가 보안 초짜라서 감이 잘 오질 않습니다.

https 클라이언트가 잘 구현이 되어 일반적인 https 서버로 접속에 성공하면 Apache 없이 OpenSSL로만 간단한 https서버도 만들어 보려고 하고 있고요...

웹서버라는것도 부수적인 기능구현 부분들 다 빼버리고 얘기하자면, 사실 TCP/IP 메시지를 주고 받는 서버니까.. 아파치에 OpenSSL로 https 서버를 돌리는것도 결국 TCP/IP 서버 프로세스에 OpenSSL을 사용하면 https를 구현해 볼수 있는게 아닌가 해서 시작된 스터디 입니다.

머 제가 아파치 같은 웹서버를 직접 만들어 보겠다는게 아니라, 보안쪽을 공부하기 위해서는 이미 다 되어 있는 그런것들을 갖다 쓰는것 보다는 한번 간단하게 라도 구현을 직접 해 볼 수 있다면 여러가지로 도움이 되지 않을까 해서요..

아파치 소스를 분석하자니 너무 막연하고 해서...
일단은 쉽게.. 아래 예제를 가지고 https 클라이언트부터 시작을 해보려고 하고 있습니다.

혹시라도 조금이라도.. 도움이 될 만한 글을 올려주신다면 감사드리겠습니다..

지금 올리려드리는 소스의 어느부분만 살짝 고치면 https서버나 https 클라이언트도 쉽게 할수 있을것같은데.. 감이 잘 오지 않네요...

아래 올린 소스도 역시 KLDP 게시판에서 줏은것 임을 미리 밝힙니다.
(공부하는데 큰 도움이 되고 있습니다. 감사합니다 ^^)

그럼.. 조그만 도움의 글이라도 큰힘이 될수도 있으니.. 부탁드립니다.. 꾸벅~^^

서버쪽 소스

/* serv.cpp  -  Minimal ssleay server for Unix
   30.9.1996, Sampo Kellomaki <sampo@iki.fi> */
                                                                                                                                                
                                                                                                                                                
/* mangled to work with SSLeay-0.9.0b and OpenSSL 0.9.2b
   Simplified to be even more minimal
   12/98 - 4/99 Wade Scholine <wades@mail.cybg.com> */
                                                                                                                                                
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
                                                                                                                                                
#include <openssl/rsa.h>       /* SSLeay stuff */
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
                                                                                                                                                
                                                                                                                                                
/* define HOME to be dir for key and cert files... */
#define HOME "./"
                                                                                                                                                
/* Make these what you want for cert & key files */
// 서버 인증서와 개인키 파일 정의
#define CERTF  HOME "server-req.pem"
#define KEYF  HOME  "server-key.pem"
                                                                                                                                                
//#define CERTF  HOME "rootcert.pem"
//#define KEYF  HOME  "rootkey.pem"
                                                                                                                                                                                                                                                                                                
#define CHK_NULL(x) if ((x)==NULL) exit (1)
#define CHK_ERR(err,s) if ((err)==-1) { perror(s); exit(1); }
#define CHK_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); exit(2); }
                                                                                                                                                
// SSL 핸드쉐이크 메시지교환 과정을 알려주는 콜벡함수
void  ssl_info_callback(const SSL *s, int where, int ret);
                                                                                                                                                
// 화면에 표시 하기 위한 파일 BIO생성
BIO * errBIO;
                                                                                                                                                
void main ()
{
    int err;
    int listen_sd;
    int sd;
    struct sockaddr_in sa_serv;
    struct sockaddr_in sa_cli;
    size_t client_len;
                                                                                                                                                
    SSL_METHOD *meth;
    SSL_CTX* ctx;
    SSL*     ssl;
    X509*    client_cert;
                                                                                                                                                
    char*    str;
    char     buf [4096];
                                                                                                                                                
    /* SSL preliminaries. We keep the certificate and key with the context. */
    // 모든 에러 스트링 로드
    SSL_load_error_strings();
    // 모든 알고리즘 로드
    SSLeay_add_ssl_algorithms();
    // SSL 버전3 프로토콜 사용
    meth = SSLv3_method();
    //meth = TLSv1_server    // create a new SSL_CTX object as framework for TLS/SSL enabled functions
    // SSL 컨텍스트 생성
    ctx = SSL_CTX_new (meth);
    if (!ctx) {
        ERR_print_errors_fp(stderr);
        exit(2);
    }
                                                                                                                                                
    // SSL 핸드쉐이크 메시지교환 과정을 알려주는 콜벡함수
    SSL_CTX_set_info_callback(ctx,ssl_info_callback);
                                                                                                                                                
    // 자신의 인증서를 파일에서 로딩한다.
    if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(3);
    }
                                                                                                                                                
    fprintf(stderr, "======== PEM pass phrease confirm here\n");
    // 자신의 개인키를 파일에서 로딩한다.
    if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {
        fprintf(stderr,"======== PEM pass phrase does not match the password\n");
        ERR_print_errors_fp(stderr);
        exit(4);
    }
                                                                                                                                                
    // 읽은 인증서와 개인키가 맞는지 확인 한다.
    if (!SSL_CTX_check_private_key(ctx)) {
        fprintf(stderr,"Private key does not match the certificate public key\n");
        exit(5);
    }
                                                                                                                                                
    listen_sd = socket (AF_INET, SOCK_STREAM, 0);
    CHK_ERR(listen_sd, "socket");
                                                                                                                                                
    memset (&sa_serv, '\0', sizeof(sa_serv));
    sa_serv.sin_family      = AF_INET;
    sa_serv.sin_addr.s_addr = INADDR_ANY;
    sa_serv.sin_port        = htons (1111);          /* Server Port number */

    err = bind(listen_sd, (struct sockaddr*) &sa_serv, sizeof (sa_serv));
    CHK_ERR(err, "bind");
                                                                                                                                                
    err = listen (listen_sd, 5);
    CHK_ERR(err, "listen");
                                                                                                                                                
    client_len = sizeof(sa_cli);
    sd = accept (listen_sd, (struct sockaddr*) &sa_cli, &client_len);
    CHK_ERR(sd, "accept");
                                                                                                                                                
    close (listen_sd);
                                                                                                                                                
    printf ("Connection from %lx, port %d\n",
    sa_cli.sin_addr.s_addr, sa_cli.sin_port);
                                                                                                                                                
    // TCP connection is ready. Do server side SSL.
    // create a new SSL structure for a connection
    // SSL 구조체 생성
    ssl = SSL_new (ctx);
    CHK_NULL(ssl);
                                                                                                                                                
    // connect the SSL object with a file descriptor
    // 연결된 소켓과 SSL과의 연결
    SSL_set_fd (ssl, sd);
    // 가장 중요한 함수, 클라이언트와의 초기 협상과정, 즉 핸드쉐이크 과정을 수행
    printf ("SSL_accept start =========================\n");
    err = SSL_accept (ssl);
    CHK_SSL(err);
    printf ("SSL_accept end =========================\n");
                                                                                                                                                
    // Get the cipher - opt
    // 현재 클라이언트와 정의된 암호화 파라메터정보를 얻음
    printf ("SSL connection using %s\n", SSL_get_cipher (ssl));
    printf ("SSL connection using %s\n", SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)));
                                                                                                                                                
    // Get client's certificate (note: beware of dynamic allocation) - opt
    client_cert = SSL_get_peer_certificate (ssl);


     if (client_cert != NULL)
    {
        printf ("Client certificate:\n");
                                                                                                                                                
        str = X509_NAME_oneline (X509_get_subject_name (client_cert), 0, 0);
        CHK_NULL(str);
        printf ("\t subject: %s\n", str);
        free (str);
                                                                                                                                                
        str = X509_NAME_oneline (X509_get_issuer_name  (client_cert), 0, 0);
        CHK_NULL(str);
        printf ("\t issuer: %s\n", str);
        free (str);
                                                                                                                                                
        /* We could do all sorts of certificate verification stuff here before
           deallocating the certificate. */
                                                                                                                                                
        X509_free (client_cert);
    }
    else
    {
        printf ("Client does not have certificate.\n");
    }
                                                                                                                                                
    // DATA EXCHANGE - Receive message and send reply.
    // 클라이언트로 부터 SSL 통신을 통해 메시지 받음
    err = SSL_read (ssl, buf, sizeof(buf) - 1);
    CHK_SSL(err);
                                                                                                                                                
    buf[err] = '\0';
    // 받은 데이터를 화면에 표시
    printf ("Got %d chars:'%s'\n", err, buf);
                                                                                                                                                
    // 클라이언트에게 SSL 통신을 통해 메시지 보냄
    err = SSL_write (ssl, "I hear you.", strlen("I hear you."));
    CHK_SSL(err);
    /* Clean up. */
    close (sd);
    SSL_free (ssl);
    SSL_CTX_free (ctx);
}
                                                                                                                                                
// SSL 핸드쉐이크 메시지교환 과정을 알려주는 콜벡함수
void  ssl_info_callback(const SSL *s, int where, int ret)
{
  char * writeString;
  int w;
  // 현재 어떤 메시지 교환 과정인지를 나타냄
  w = where & ~SSL_ST_MASK;
                                                                                                                                                
  // 클라이언트가 연결 했을 때
  if (w & SSL_ST_CONNECT)
    writeString="SSL_connect";
  // 서버가 연결을 받았을 때
  else if (w & SSL_ST_ACCEPT)
    writeString="SSL_accept";
  // 알 수 없는 경우
  else
    writeString="undefined";
                                                                                                                                                
    fprintf(stderr, "======== writeString = [%s]\n", writeString);
                                                                                                                                                
  // 일반적인 핸드쉐이크 프로토콜 메시지일 경우
  if (where & SSL_CB_LOOP)
  {
    // SSL_state_string_long(s) 함수로 부터 현재 진행되는 메시지가 무엇인지 표시
    BIO_printf(errBIO,"%s:%s\n",writeString,SSL_state_string_long(s));
    fprintf(stderr, "======== writeString = [%s], SSL_state_string_long(s) = [%s]\n", writeString, SSL_state_string_long(s));
  }
  else if (where & SSL_CB_ALERT)
  { // alert 프로토콜일 경우
    writeString=(where & SSL_CB_READ)?"read":"write";
    BIO_printf(errBIO,"SSL3 alert %s:%s:%s\n",writeString,SSL_alert_type_string_long(ret),SSL_alert_desc_string_long(ret));
    fprintf(stderr, "======== writeString = [%s], SSL_alert_type_string_long(ret) = [%s], SSL_alert_desc_string_long(ret) = [%s]\n", writeString, SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
  }
  else if (where & SSL_CB_EXIT)
  { // 종료 과정일 경우
    if (ret == 0) {
      BIO_printf(errBIO,"%s:failed in %s\n",writeString,SSL_state_string_long(s));
      fprintf(stderr,"======== writeString = [%s], SSL_state_string_long(s) = [%s]\n", writeString, SSL_state_string_long(s));
    }
    else if (ret < 0)
    {
      BIO_printf(errBIO,"%s:error in %s\n",writeString,SSL_state_string_long(s));
      fprintf(stderr,"======== writeString = [%s], SSL_state_string_long(s) = [%s]\n", writeString, SSL_state_string_long(s));
    }
  }
}
[/b]

클라이언트 소스

/* cli.cpp  -  Minimal ssleay client for Unix
   30.9.1996, Sampo Kellomaki <sampo@iki.fi> */
                                                                                                                                                
/* mangled to work with SSLeay-0.9.0b and OpenSSL 0.9.2b
   Simplified to be even more minimal
   12/98 - 4/99 Wade Scholine <wades@mail.cybg.com> */
                                                                                                                                                
#include <stdio.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
                                                                                                                                                
#include <unistd.h>
                                                                                                                                                
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
                                                                                                                                                
                                                                                                                                                
#define CHK_NULL(x) if ((x)==NULL) exit (1)
#define CHK_ERR(err,s) if ((err)==-1) { perror(s); exit(1); }
#define CHK_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); exit(2); }
                                                                                                                                                
void print_x509(SSL *ssl)
{
    char *ascii_cert;
    X509 *cert = SSL_get_peer_certificate(ssl);
    BIO *b;
    BUF_MEM *bptr;
                                                                                                                                                
    b = BIO_new(BIO_s_mem());
    if(X509_print(b, cert) > 0)
    {
        BIO_get_mem_ptr(b, &bptr);
        ascii_cert = (char *)malloc(1 + bptr->length);
        memcpy(ascii_cert, bptr->data, bptr->length);
    }
    else
    {
        ascii_cert = (char *)malloc(1024);
        sprintf(ascii_cert, "This certificate has never been seen before and can't be shown\n");
    }
    BIO_free(b);
                                                                                                                                                
    /* X.509 인증서 출력 */
    printf("X.509:\n%s\n", ascii_cert);
}
                                                                                                                                                
void main (int argc, char **argv)
{
    int err;
    int sd;
    struct sockaddr_in sa;
                                                                                                                                                
    SSL_METHOD *meth;
    SSL_CTX* ctx;
    SSL*     ssl;
    X509*    server_cert;
                                                                                                                                                
    char*    str;
    char     buf [4096];
    char hello[80];
                                                                                                                                                
    printf ("Message to send: ");
    fgets (hello, 80, stdin);

    SSLeay_add_ssl_algorithms();
    meth = SSLv3_client_method(); // meth = SSLv3_method();
    //meth = SSLv2_client_method();
    //meth = TLSv1_client_method();
    SSL_load_error_strings();
    ctx = SSL_CTX_new (meth);
    CHK_NULL(ctx);
    CHK_SSL(err);
                                                                                                                                                
    ///////////////////////////////////////////////////////////////////////////////////////
    // Create a socket and connect to server using normal socket calls.
    sd = socket (AF_INET, SOCK_STREAM, 0);
    CHK_ERR(sd, "socket");
                                                                                                                                                
    memset (&sa, '\0', sizeof(sa));
    sa.sin_family      = AF_INET;
    sa.sin_addr.s_addr = inet_addr ("111.111.11.11");   /* Server IP */
    sa.sin_port        = htons     (atoi(argv[1]));          /* Server Port number */
                                                                                                                                                
    // Normal-connect
    err = connect(sd, (struct sockaddr*) &sa, sizeof(sa));
    CHK_ERR(err, "connect");
                                                                                                                                                
    //printf("랜덤 수 생성중....");
    //RAND_screen();
    //printf("랜덤 수 생성 완료.\n");
                                                                                                                                                
    // Now we have TCP conncetion. Start SSL negotiation.
    // create a new SSL structure for a connection
    ssl = SSL_new (ctx);
    CHK_NULL(ssl);
                                                                                                                                                
    // connect the SSL object with a file descriptor
    SSL_set_fd (ssl, sd);
                                                                                                                                                
    // initi    err = SSL_connect (ssl);
    CHK_SSL(err);
                                                                                                                                                   
    /*
     * Following two steps are optional and not required for
     * data exchange to be successful.
     */
                                                                                                                                                   
    printf ("SSL connection using %s\n", SSL_get_cipher (ssl));
                                                                                                                                                   
    // Get server's certificate (note: beware of dynamic allocation) - opt
    server_cert = SSL_get_peer_certificate (ssl);
    CHK_NULL(server_cert);
    print_x509(ssl);
    printf ("===================\n");
                                                                                                                                                   
    printf ("Server certificate:\n");
    str = X509_NAME_oneline (X509_get_subject_name (server_cert),0,0);
    CHK_NULL(str);
    printf ("\t subject: %s\n", str);
    free (str);
                                                                                                                                                   
    str = X509_NAME_oneline (X509_get_issuer_name  (server_cert),0,0);
    CHK_NULL(str);
    printf ("\t issuer: %s\n", str);
    free (str);
                                                                                                                                                   
    /*
     * We could do all sorts of certificate verification stuff here before
     * deallocating the certificate.
     */
    X509_free (server_cert);
                                                                                                                                                   
    ///////////////////////////////////////////////////////////////////////////////////////
    // DATA EXCHANGE - Send a message and receive a reply.
    //err = SSL_write (ssl, "Hello World!", strlen("Hello World!"));
    //CHK_SSL(err);
    err = SSL_write (ssl, hello, strlen(hello));
    CHK_SSL(err);
                                                                                                                                                   
    err = SSL_read (ssl, buf, sizeof(buf) - 1);
    CHK_SSL(err);
    buf[err] = '\0';
    printf ("Got %d chars:'%s'\n", err, buf);
                                                                                                                                                   
    // send SSL/TLS close_notify
    SSL_shutdown (ssl);
                                                                                                                                                   
    /* Clean up. */
                                                                                                                                                   
    close (sd);
    SSL_free (ssl);
    SSL_CTX_free (ctx);
}
cjh의 이미지

HTTPS 클라이언트라면 libcurl을 쓰시면 https:로 접속할 수 있습니다.

서버라면 노력이 더 필요하겠지만, 소규모라면 stone같은 SSL tunnel 프로그램을 이용해서 기존의 웹 서버를 SSL지원으로 만들 수 있습니다.

libcurl: http://curl.haxx.se/
stone: http://www.gcd.org/sengoku/stone/

--
익스펙토 페트로눔

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.