ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Openssl을 이용한 암호화 통신
    검색하기 귀찮아서 블로그에 박제 2021. 10. 13. 11:18
    728x90

    서버는 암호화 통신을 위하여 가장 먼저 SSL_CTX와 SSL 구조체를 선언하여 암호화 통신을 위한 정보를 관리할 수 있도록 한다. 그리고, SSL 세션을 시작하기 위한 초기값을 설정하고, SSL_CTX_new() 함수를 이용하여 SSL 컨텍스트를 생성한다.

     다음 단계는 인증서를 이용하여 서버와 클라이언트 간의 인증을 수행할 경우 SS_CTX_use_certificate_file()를 이요하여 인증서 파일을 생성하며, SSL_CTX_use_PrivateKey_file()를 이용하여 개인 키를 생성한다.

     이런 준비 단계 후에 SSL_new() 함수를 이용하여 SSL 세션을 생성하고, 이후의 단계는 기존의 소켓 서버와 유사한 방법으로 처리를 수행할 수 있다. SSL을 이용한 송수신은 SSL_accept(), SSL_read(), SSL_write() 등의 함수를 이용할 수 있으며, 이 과정은 기종의 소켓 프로그래밍과 유사한 방법으로 수행된다.

          Client                                                 Server                        
    ===============================                  =====================================
    const SSL_METHOD *meth;                          const SSL_METHOD *meth;
    SSL_CTX *ssl_ctx;                                SSL_CTX *ssl_ctx;
    meth = TLS_client_method();                      meth = TLS_server_method();
    ssl_ctx = SSL_CTX_new( meth );                   ssl_ctx = SSL_CTX_new( meth );
    ...                                              ...
    ...                                              SSL_CTX_set_verify( ssl_ctx, SSL_VERIFY_PEER |
    				                              SSL_VERIFY_FAIL_IF_NO_PEER_CERT | 
                                                                   SSL_VERIFY_CLIENT_ONCE , NULL );
    SSL_CTX_set_verify_depth( ssl_ctx, 2 );          SSL_CTX_set_verify_depth( ssl_ctx, 2 );
    ...                                              SSL_CTX_set_num_tickets(ssl_ctx, 0 );
    ...                                              ...
    SSL *ssl = SSL_new(ssl_ctx );                    SSL *ssl = SSL_new( ssl_ctx );
    nSSLIORetVal = SSL_connect( ssl )                nSSLIORetVal = SSL_accept( ssl );
    ==========================================================================================
    When using TLSv1.2, SSL_connect returns -1       For both TLSv1.2 and TLSv1.3, SSL_accept
    When using TLSv1.3, SSL_connect returns 1        returns -1 with Err_get_error=337100999 
                                                     (Peer did not return a certificate)

    -server.c

    /*
    *      serv.c
    *     
    *      Copyright 2008 Kim Sung-tae <pchero21@gmail.com>
    *     
    *      This program is free software; you can redistribute it and/or modify
    *      it under the terms of the GNU General Public License as published by
    *      the Free Software Foundation; either version 2 of the License, or
    *      (at your option) any later version.
    *     
    *      This program is distributed in the hope that it will be useful,
    *      but WITHOUT ANY WARRANTY; without even the implied warranty of
    *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    *      GNU General Public License for more details.
    *     
    *      You should have received a copy of the GNU General Public License
    *      along with this program; if not, write to the Free Software
    *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
    *      MA 02110-1301, USA.
    */
     
     
    #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>
     
    /*
    * openssl 관련 헤더 파일을 include 한다.
    */
    #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.crt”
    #define KEYF HOME “server.key”
     
    #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); }
     
     
    int main(void)
    {
        int err;
        int listen_sd;
        int sd;
        struct sockaddr_in sa_serv;
        struct sockaddr_in sa_cli;
        size_t client_len;
       
        /* SSL Context 및 관련 구조체를 선언한다. */
        SSL_CTX  *ctx;
        SSL    *ssl;
        X509                *client_cert;
        char                *str;
        char                buf[4096];
        SSL_METHOD  *meth;
       
        /* SSL 관련 초기화 작업을 수행한다. */
        SSL_load_error_strings();
        SSLeay_add_ssl_algorithms();
        meth = SSLv23_server_method();    // 서버 메소드.
        ctx = SSL_CTX_new(meth);                // 지정된 초기 값을 이용하여 SSL Context를 생성한다.
       
        if(!ctx) {
            ERR_print_errors_fp(stderr);
            exit(2);
        }
       
        /* 사용하게 되는 인증서 파일을 설정한다. */
        if(SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {      // 인증서를 파일로 부터 로딩할때 사용함.
            ERR_print_errors_fp(stderr);
            exit(3);
        }
       
        /* 암호화 통신을 위해서 이용하는 개인 키를 설정한다. */
        if(SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {
            ERR_print_errors_fp(stderr);
            exit(4);
        }
       
        /* 개인 키가 사용 가능한 것인지 확인한다. */
        if(!SSL_CTX_check_private_key(ctx)) {
            fprintf(stderr, “Private key does not match the certificate public keyn“);
            exit(5);
        }
       
        /* Prepare TCP socket for receiving connections */
        listen_sd = socket(AF_INET, SOCK_STREAM, 0);
        CHK_ERR(listen_sd, “socket”);
       
        memset(&sa_serv, ‘‘, 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, “bimd”);
       
        /* Receive a TCP connection. */
         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 %1x, port %xn“, sa_cli.sin_addr.s_addr, sa_cli.sin_port);
        
         /* TCP connection is ready. Do server side SSL. */
        ssl = SSL_new(ctx); // 설정된 Context를 이용하여 SSL 세션의 초기화 작업을 수행한다.
        CHK_NULL(ssl);
        SSL_set_fd(ssl, sd);
        err = SSL_accept(ssl);    // SSL 세션을 통해 클라이언트의 접속을 대기한다.
        CHK_SSL(err);
       
        /* Get the cipher – opt */
        printf(“SSL connection using %sn“, SSL_get_cipher(ssl));
       
        /* 클라이언트의 인증서를 받음 – 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: %sn“, str);
            OPENSSL_free(str);
           
            str = X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0);
            CHK_NULL(str);
            printf(“t issuer: %sn“, str);
            OPENSSL_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“);
        }
       
        /* SSL 세션을 통해서 클라이언트와 데이터를 송수신한다. */
        err = SSL_read(ssl, buf, sizeof(buf) – 1);
        CHK_SSL(err);
        buf[err] = ‘‘;
        printf(“Got %d chars: ‘%s’n“, err, buf);
       
        err = SSL_write(ssl, “I hear you/”, strlen(“I hear you.”));
        CHK_SSL(err);
       
        /* 설정한 자원을 반환하고 종료한다. */
        close(sd);
        SSL_free(ssl);
        SSL_CTX_free(ctx);
       
        return(0);
    }

    -client.c

     

    /*
    *      cli.c
    *     
    *      Copyright 2008 Kim Sung-tae <pchero21@gmail.com>
    *     
    *      This program is free software; you can redistribute it and/or modify
    *      it under the terms of the GNU General Public License as published by
    *      the Free Software Foundation; either version 2 of the License, or
    *      (at your option) any later version.
    *     
    *      This program is distributed in the hope that it will be useful,
    *      but WITHOUT ANY WARRANTY; without even the implied warranty of
    *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    *      GNU General Public License for more details.
    *     
    *      You should have received a copy of the GNU General Public License
    *      along with this program; if not, write to the Free Software
    *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
    *      MA 02110-1301, USA.
    */
     
     
    #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 <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); }
     
     
    int main(void)
    {
        int err;
        int sd;
        struct sockaddr_in sa;
       
        /* SSL 관련 정보를 관리할 구조체를 선언한다. */
        SSL_CTX   *ctx;
        SSL     *ssl;
        X509                    *server_cert;
        char                    *str;
        char                    buf[4096];
        SSL_METHOD    *meth;
       
        /* 암호화 통신을 위한 초기화 작업을 수행한다. */
        SSL_load_error_strings();
        SSLeay_add_ssl_algorithms();
        meth = SSLv3_client_method();
        ctx = SSL_CTX_new(meth);
        CHK_NULL(ctx);
       
        /* 사용하게 되는 인증서 파일을 설정한다. – opt*/
        if(SSL_CTX_use_certificate_file(ctx, “./client.crt”, SSL_FILETYPE_PEM) <= 0) {    // 인증서를 파일로 부터 로딩할때 사용함.
            ERR_print_errors_fp(stderr);
            exit(3);
        }
       
        /* 암호화 통신을 위해서 이용하는 개인 키를 설정한다. – opt */
        if(SSL_CTX_use_PrivateKey_file(ctx, “./client.key”, SSL_FILETYPE_PEM) <= 0) {
            ERR_print_errors_fp(stderr);
            exit(4);
        }
       
        /* 개인 키가 사용 가능한 것인지 확인한다. – opt */
        if(!SSL_CTX_check_private_key(ctx)) {
            fprintf(stderr, “Private key does not match the certificate public keyn“);
            exit(5);
        }
       
        //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, ‘‘, sizeof(sa));
        sa.sin_family = AF_INET;
        sa.sin_addr.s_addr = inet_addr(“127.0.0.1”);        // Server IP Address
        sa.sin_port = htons(1111);                // Server Port Number
       
        err = connect(sd, (struct sockaddr*)&sa, sizeof(sa));
        CHK_ERR(err, “connect”);
       
        /* Now we have TCP connection. Start SSL negotiation. */
        ssl = SSL_new(ctx);  // 세션을 위한 자원을 할당받는다.
        CHK_NULL(ssl);
       
        SSL_set_fd(ssl, sd);
        err = SSL_connect(ssl); // 기존의 connect() 함수 대신 사용하여 서버로 접속한다.
        CHK_NULL(err);
       
        /* Following two steps are optional and not required for data exchange to be successful. */
       
        /* Get the Cipher – opt */
        printf(“SSL connection using %sn“, 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);
        printf(“Server certificate:n“);
       
        /* 인증서의 이름을 출력한다. */
        str = X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0);
        CHK_NULL(str);
        printf(“t subject: %sn“, str);
        OPENSSL_free(str);
       
        /* 인증서의 issuer를 출력한다. */
        str = X509_NAME_oneline(X509_get_issuer_name(server_cert), 0, 0);
        CHK_NULL(str);
        printf(“t issuer: %sn“, str);
        OPENSSL_free(str);
       
        /* We could do all sorts of certificate verification stuff here before deallocating the certificate */
        X509_free(server_cert);
       
        /* 서버와 데이터를 송수신 한다. */
        err = SSL_write(ssl, “Hello World!”, strlen(“Hello World!”));
        CHK_SSL(err);
       
        err = SSL_read(ssl, buf, sizeof(buf) – 1);
        CHK_SSL(err);
        buf[err] = ‘‘;
        printf(“Got %d chars: ‘%s’n“, err, buf);
        SSL_shutdown(ssl);    // SSL로 연결된 접속을 해지한다.
       
        /* 할당된 자원을 반환하고 종료한다. */
        close(sd);
        SSL_free(ssl);
        SSL_CTX_free(ctx);
       
        return 0;
    }
    728x90

    댓글

Designed by Tistory.