Openssl을 이용한 암호화 통신

 Openssl을 이용한 암호화 통신

 서버는 암호화 통신을 위하여 가장 먼저 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() 등의 함수를 이용할 수 있으며, 이 과정은 기종의 소켓 프로그래밍과 유사한 방법으로 수행된다.

serv.c (Language : c)
  1. /*
  2. *      serv.c
  3. *     
  4. *      Copyright 2008 Kim Sung-tae <pchero21@gmail.com>
  5. *     
  6. *      This program is free software; you can redistribute it and/or modify
  7. *      it under the terms of the GNU General Public License as published by
  8. *      the Free Software Foundation; either version 2 of the License, or
  9. *      (at your option) any later version.
  10. *     
  11. *      This program is distributed in the hope that it will be useful,
  12. *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. *      GNU General Public License for more details.
  15. *     
  16. *      You should have received a copy of the GNU General Public License
  17. *      along with this program; if not, write to the Free Software
  18. *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  19. *      MA 02110-1301, USA.
  20. */
  21.  
  22.  
  23. #include <stdio.h>
  24. #include <unistd.h>
  25. #include <stdlib.h>
  26. #include <memory.h>
  27. #include <errno.h>
  28. #include <sys/types.h>
  29. #include <sys/socket.h>
  30. #include <netinet/in.h>
  31. #include <arpa/inet.h>
  32. #include <netdb.h>
  33.  
  34. /*
  35. * openssl 관련 헤더 파일을 include 한다.
  36. */
  37. #include <openssl/rsa.h>        /* SSLeay stuff */
  38. #include <openssl/crypto.h>
  39. #include <openssl/x509.h>
  40. #include <openssl/pem.h>
  41. #include <openssl/ssl.h>
  42. #include <openssl/err.h>
  43.  
  44. /* define HOME to be dir for key and cert files…. */
  45. #define HOME “./”
  46.  
  47. /* Make these what you want for cert & key files */
  48. #define CERTF HOME “server.crt”
  49. #define KEYF HOME “server.key”
  50.  
  51. #define CHK_NULL(x) if((x) == NULL) exit(1);
  52. #define CHK_ERR(err, s) if((err) == -1) { perror(s); exit(1); }
  53. #define CHK_SSL(err) if((err) == -1) { ERR_print_errors_fp(stderr); exit(2); }
  54.  
  55.  
  56. int main(void)
  57. {
  58.     int err;
  59.     int listen_sd;
  60.     int sd;
  61.     struct sockaddr_in sa_serv;
  62.     struct sockaddr_in sa_cli;
  63.     size_t client_len;
  64.    
  65.     /* SSL Context 및 관련 구조체를 선언한다. */
  66.     SSL_CTX  *ctx;
  67.     SSL    *ssl;
  68.     X509                *client_cert;
  69.     char                *str;
  70.     char                buf[4096];
  71.     SSL_METHOD  *meth;
  72.    
  73.     /* SSL 관련 초기화 작업을 수행한다. */
  74.     SSL_load_error_strings();
  75.     SSLeay_add_ssl_algorithms();
  76.     meth = SSLv23_server_method();    // 서버 메소드.
  77.     ctx = SSL_CTX_new(meth);                // 지정된 초기 값을 이용하여 SSL Context를 생성한다.
  78.    
  79.     if(!ctx) {
  80.         ERR_print_errors_fp(stderr);
  81.         exit(2);
  82.     }
  83.    
  84.     /* 사용하게 되는 인증서 파일을 설정한다. */
  85.     if(SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {      // 인증서를 파일로 부터 로딩할때 사용함.
  86.         ERR_print_errors_fp(stderr);
  87.         exit(3);
  88.     }
  89.    
  90.     /* 암호화 통신을 위해서 이용하는 개인 키를 설정한다. */
  91.     if(SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {
  92.         ERR_print_errors_fp(stderr);
  93.         exit(4);
  94.     }
  95.    
  96.     /* 개인 키가 사용 가능한 것인지 확인한다. */
  97.     if(!SSL_CTX_check_private_key(ctx)) {
  98.         fprintf(stderr, “Private key does not match the certificate public keyn);
  99.         exit(5);
  100.     }
  101.    
  102.     /* Prepare TCP socket for receiving connections */
  103.     listen_sd = socket(AF_INET, SOCK_STREAM, 0);
  104.     CHK_ERR(listen_sd, “socket”);
  105.    
  106.     memset(&sa_serv, , sizeof(sa_serv));
  107.     sa_serv.sin_family = AF_INET;
  108.     sa_serv.sin_addr.s_addr = INADDR_ANY;
  109.     sa_serv.sin_port = htons(1111); /* Server Port number */
  110.  
  111.     err = bind(listen_sd, (struct sockaddr*)&sa_serv, sizeof(sa_serv));
  112.     CHK_ERR(err, “bimd”);
  113.    
  114.     /* Receive a TCP connection. */
  115.      err = listen(listen_sd, 5);
  116.      CHK_ERR(err, “listen”);
  117.     
  118.      client_len = sizeof(sa_cli);
  119.      sd = accept(listen_sd, (struct sockaddr*)&sa_cli, &client_len);
  120.      CHK_ERR(sd, “accept”);
  121.      close(listen_sd);
  122.     
  123.      printf(“Connection from %1x, port %xn, sa_cli.sin_addr.s_addr, sa_cli.sin_port);
  124.     
  125.      /* TCP connection is ready. Do server side SSL. */
  126.     ssl = SSL_new(ctx); // 설정된 Context를 이용하여 SSL 세션의 초기화 작업을 수행한다.
  127.     CHK_NULL(ssl);
  128.     SSL_set_fd(ssl, sd);
  129.     err = SSL_accept(ssl);    // SSL 세션을 통해 클라이언트의 접속을 대기한다.
  130.     CHK_SSL(err);
  131.    
  132.     /* Get the cipher – opt */
  133.     printf(“SSL connection using %sn, SSL_get_cipher(ssl));
  134.    
  135.     /* 클라이언트의 인증서를 받음 – opt */
  136.     client_cert = SSL_get_peer_certificate(ssl);
  137.     if(client_cert != NULL) {
  138.         printf(“Client certificate:n);
  139.        
  140.         str = X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0);
  141.         CHK_NULL(str);
  142.         printf(t subject: %sn, str);
  143.         OPENSSL_free(str);
  144.        
  145.         str = X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0);
  146.         CHK_NULL(str);
  147.         printf(t issuer: %sn, str);
  148.         OPENSSL_free(str);
  149.        
  150.         /* We could do all sorts of certificate verification stuff here before deallocating the certificate. */
  151.         X509_free(client_cert);
  152.     } else {
  153.         printf(“Client does not have certificate.n);
  154.     }
  155.    
  156.     /* SSL 세션을 통해서 클라이언트와 데이터를 송수신한다. */
  157.     err = SSL_read(ssl, buf, sizeof(buf)1);
  158.     CHK_SSL(err);
  159.     buf[err] = ;
  160.     printf(“Got %d chars: ‘%s’n, err, buf);
  161.    
  162.     err = SSL_write(ssl, “I hear you/”, strlen(“I hear you.”));
  163.     CHK_SSL(err);
  164.    
  165.     /* 설정한 자원을 반환하고 종료한다. */
  166.     close(sd);
  167.     SSL_free(ssl);
  168.     SSL_CTX_free(ctx);
  169.    
  170.     return(0);
  171. }
  172.  

cli.c (Language : c)
  1. /*
  2. *      cli.c
  3. *     
  4. *      Copyright 2008 Kim Sung-tae <pchero21@gmail.com>
  5. *     
  6. *      This program is free software; you can redistribute it and/or modify
  7. *      it under the terms of the GNU General Public License as published by
  8. *      the Free Software Foundation; either version 2 of the License, or
  9. *      (at your option) any later version.
  10. *     
  11. *      This program is distributed in the hope that it will be useful,
  12. *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. *      GNU General Public License for more details.
  15. *     
  16. *      You should have received a copy of the GNU General Public License
  17. *      along with this program; if not, write to the Free Software
  18. *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  19. *      MA 02110-1301, USA.
  20. */
  21.  
  22.  
  23. #include <stdio.h>
  24. #include <memory.h>
  25. #include <errno.h>
  26. #include <sys/types.h>
  27. #include <sys/socket.h>
  28. #include <netinet/in.h>
  29. #include <arpa/inet.h>
  30. #include <netdb.h>
  31.  
  32. #include <openssl/crypto.h>
  33. #include <openssl/x509.h>
  34. #include <openssl/pem.h>
  35. #include <openssl/ssl.h>
  36. #include <openssl/err.h>
  37.  
  38. #define CHK_NULL(x) if((x) == NULL) exit(1);
  39. #define CHK_ERR(err, s) if((err) == -1) { perror(s); exit(1); }
  40. #define CHK_SSL(err) if((err) == -1) { ERR_print_errors_fp(stderr); exit(2); }
  41.  
  42.  
  43. int main(void)
  44. {
  45.     int err;
  46.     int sd;
  47.     struct sockaddr_in sa;
  48.    
  49.     /* SSL 관련 정보를 관리할 구조체를 선언한다. */
  50.     SSL_CTX   *ctx;
  51.     SSL     *ssl;
  52.     X509                    *server_cert;
  53.     char                    *str;
  54.     char                    buf[4096];
  55.     SSL_METHOD    *meth;
  56.    
  57.     /* 암호화 통신을 위한 초기화 작업을 수행한다. */
  58.     SSL_load_error_strings();
  59.     SSLeay_add_ssl_algorithms();
  60.     meth = SSLv3_client_method();
  61.     ctx = SSL_CTX_new(meth);
  62.     CHK_NULL(ctx);
  63.    
  64.     /* 사용하게 되는 인증서 파일을 설정한다. – opt*/
  65.     if(SSL_CTX_use_certificate_file(ctx, “./client.crt”, SSL_FILETYPE_PEM) <= 0) {    // 인증서를 파일로 부터 로딩할때 사용함.
  66.         ERR_print_errors_fp(stderr);
  67.         exit(3);
  68.     }
  69.    
  70.     /* 암호화 통신을 위해서 이용하는 개인 키를 설정한다. – opt */
  71.     if(SSL_CTX_use_PrivateKey_file(ctx, “./client.key”, SSL_FILETYPE_PEM) <= 0) {
  72.         ERR_print_errors_fp(stderr);
  73.         exit(4);
  74.     }
  75.    
  76.     /* 개인 키가 사용 가능한 것인지 확인한다. – opt */
  77.     if(!SSL_CTX_check_private_key(ctx)) {
  78.         fprintf(stderr, “Private key does not match the certificate public keyn);
  79.         exit(5);
  80.     }
  81.    
  82.     //CHK_SSL(err);
  83.    
  84.     /* Create a socket and connect to server using normal socket calls. */
  85.     sd = socket(AF_INET, SOCK_STREAM, 0);
  86.     CHK_ERR(sd, “socket”);
  87.    
  88.     memset(&sa, , sizeof(sa));
  89.     sa.sin_family = AF_INET;
  90.     sa.sin_addr.s_addr = inet_addr(“127.0.0.1”);        // Server IP Address
  91.     sa.sin_port = htons(1111);                // Server Port Number
  92.    
  93.     err = connect(sd, (struct sockaddr*)&sa, sizeof(sa));
  94.     CHK_ERR(err, “connect”);
  95.    
  96.     /* Now we have TCP connection. Start SSL negotiation. */
  97.     ssl = SSL_new(ctx);  // 세션을 위한 자원을 할당받는다.
  98.     CHK_NULL(ssl);
  99.    
  100.     SSL_set_fd(ssl, sd);
  101.     err = SSL_connect(ssl); // 기존의 connect() 함수 대신 사용하여 서버로 접속한다.
  102.     CHK_NULL(err);
  103.    
  104.     /* Following two steps are optional and not required for data exchange to be successful. */
  105.    
  106.     /* Get the Cipher – opt */
  107.     printf(“SSL connection using %sn, SSL_get_cipher(ssl));
  108.    
  109.     /* Get server’s certificate (note: beware of dynamic allocation) – opt */
  110.     /* 서버의 인증서를 받는다. */
  111.     server_cert = SSL_get_peer_certificate(ssl);
  112.     CHK_NULL(server_cert);
  113.     printf(“Server certificate:n);
  114.    
  115.     /* 인증서의 이름을 출력한다. */
  116.     str = X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0);
  117.     CHK_NULL(str);
  118.     printf(t subject: %sn, str);
  119.     OPENSSL_free(str);
  120.    
  121.     /* 인증서의 issuer를 출력한다. */
  122.     str = X509_NAME_oneline(X509_get_issuer_name(server_cert), 0, 0);
  123.     CHK_NULL(str);
  124.     printf(t issuer: %sn, str);
  125.     OPENSSL_free(str);
  126.    
  127.     /* We could do all sorts of certificate verification stuff here before deallocating the certificate */
  128.     X509_free(server_cert);
  129.    
  130.     /* 서버와 데이터를 송수신 한다. */
  131.     err = SSL_write(ssl, “Hello World!”, strlen(“Hello World!”));
  132.     CHK_SSL(err);
  133.    
  134.     err = SSL_read(ssl, buf, sizeof(buf)1);
  135.     CHK_SSL(err);
  136.     buf[err] = ;
  137.     printf(“Got %d chars: ‘%s’n, err, buf);
  138.     SSL_shutdown(ssl);    // SSL로 연결된 접속을 해지한다.
  139.    
  140.     /* 할당된 자원을 반환하고 종료한다. */
  141.     close(sd);
  142.     SSL_free(ssl);
  143.     SSL_CTX_free(ctx);
  144.    
  145.     return 0;
  146. }
  147.  
  148.  

SSL 라이브러리의 활용

  SSL 라이브러리의 활용

 SSL 라이브러리의 가장 중요한 기능은 암호화 채널을 통한 통신이다. 이를 위해서 각각의 통신은 인증서를 필요한 경우가 있으며, 실제 전자상거래나 증권 거래 등에서 이용하는 인증서인 경우 공인 인증기관에서 발급받는다.

 하지만 제한적인 범위 내에서는 자체적으로 인증서를 생성하여 이용할 수 있으며, 이러한 기능은 openssl 라이브러리에서 기본적인 명령으로 제공한다.

 1) 인증서 만들기
 인 증서(Certificate)는 서버와 클라이언트 간에 통신을 수행하는 과정에서 서로의 신원을 보증하는 일종의 보증서 역할을 한다. 은증서는 공인된 인증기관에서 개인의 신원 확인을 거쳐서 발급하며 주식거래, 전자상거래 같은 금융 거래에서 주로 이용한다. 인증서를 발급하는 기관을 CA(Certificate Authority)라고 하며, 이러한 기관에서 발급한 인증서는 기본적으로 다음과 같은 정보를 포함한다.

 – 소유자 이름 : 주민등록증의 이름과 동일한 기능
 – 일련 번호 : 주민등록증에 비교할 때 주민등록번호의 역할
 – 소유자의 공개 키 : 해당 인증서를 소유한 사람의 공개 키
 – 인증 기관의 전자 서명 : 인증서를 발급한 기관의 전자 서명

 하 지만 개발자 입장에서 제한된 시스템과 응용 프로그램을 대상으로 하는 경우에는 자체적인 인증서를 발급하고 이를 이용하여 응용 프로그램에서 이용할 수도 있는데, 이러한 기능을 수행하는 시스템을 ‘self-signed’ 서버라고 한다. 이를 위해 openssl은 기본적인 명령어로 인증서를 발급하는 기능을 갖추고 있다.

 * 비밀 키 생성
 인증서를 만들 때 가장 먼저 하는 작업으로 비밀 키를 생성한다. 비밀 키는 자신만이 알 수 있게 주의해서 관리해야 하는 정보다. 따라서 가능하면 누구도 알 수 없게 비밀스럽게 생성하고 관리해야 한다. 이 과정에서 가장 먼저 하는 작업은 DSA Parameter를 만드는 과정으로 다음과 같은 명령으로 수행한다.

     # openssl dsaparam 1024 -out dsaparam1024.pem

 이 명령은 1024 비트 크기로 DSA Parameter를 만드는 기능을 수행하며, DSA 키를 만드는 데 사용한다.

 다음 명령은 각각 암호화된 DSA 또는 암호화 되지 않은 DSA 키를 생성하는 기능을 한다. 처음의 것이 암호화되지 않은 키를 생성하는 명령이고, 두번째 명령이 암호화 된 DSA 키를 생성하는 명령이다.

     # openssl gendsa -out dsa1024.pem -des dsaparam1024.pem
     # openssl gendsa -out dsa1024.pem dsaparam1024.pem

 RSA 키를 생성하려면 다음과 같은 형식으로 명령을 이용하면 된다. DSA 키의 생성과 마찬가지로 암호 알고리즘 지정 여부에 따라 암호화 된 키 또는 암호화되지 않은 키를 생성할 수 있다.

     # openssl genrsa -out rsa1024.key -des3 1024
     # openssl genrsa -out rsa1024.key 1024

 DH 파라미터는 다음과 같은 명령으로 생성한다.

     # openssl gendh -out dh1024.pem 1024

 * CSR 생성
 CSR(Certificate Signing Request)은 인증서를 만들기 위해서 CA(Certificate Autority)로 보내는 문서를 말한다. 실제로 이러한 서비스는 사설 인증 업체에서 수행하는 경우가 많기 때문에 개인적으로 CSR을 생성하고 이를 이용해서 인증서를 신청하는 작업이 조금 어려울 수도 있다. openssl에서 제공하는 다음 명령을 이용해서 CSR을 생성할 수 있다.

     # openssl req -new -config /etc/ssl/openssl.cnf -days 365 -key rsa1024.key > server.scr

 이 명령은 RSA 1024비트 키로 CSR을 만들며, 명령 수행 과정에서 openssl.cnf 파일을 필요로 한다. 실제로 이 명령을 수행할 때는 라이브러리와 함께 설치된 openssl.cnf 파일을 적절히 수정해서 사용하자. 이 명령은 앞 단계에서 만든 RSA 키를 이용하는 명령이며, DSA를 이용해서 생성할 수도 있다. 이 경우 위의 명령에서 rsa1024.key를 DSA 키 파일인 dsa1024.key로 대치하여 수행하면 된다.

 * CRT 생성
 이 과정은 앞에서 생성한 CSR을 이용해서 인증서를 만드는 과정이다. 실제로 이 과정은 CSR을 받은 공인 인증 기관을 통해서 수행하지만, 한정된 범위에서 암호화 통신 기능을 위해서 사용하는 정도라면 자신이 가지고 있는 키로 서명해서 이용할 수도 있다. 이러한 경우를 self-signing이라고 한다. 이 과정은 다음 명령을 통해서 수행한다.

     # openssl x509 -req -days 365 -in server.csr -signkey rsa1024.key -out server.crt

 만일 가상적으로 CA 서버를 만들고 싶다면, 이 과정을 응용해서 CA 서버를 구축하고 운영할 수도 있다.