Md5sum 사용하기

 * Md5sum 사용하기

 때때로, 개발자들은 md5sum 명령을 사용해서 파일의 해시값을 계산한다. 이 해시값과, md5sum 명령을 사용해서 파일이 수정되지 않았다는 것을 확인할 수 있다. 확인하는 가장 쉬운 방법은, 먼저 개발자가 계산한 해시값을 다운로드 받고, 실제 실행 파일을 다운로드 받은 다음, 실행 파일의 해시값을 md5sum으로 계산해서 개발자가 계산한 값과 비교해 보는 것이다.

 예를 들어, 당신의 시스템에 설치된 vsftpd에 보안 문제점이 있다고 하자. 당신은 이런 문제점을 해결한 최신 버전을 다운로드 하고 싶다. 웹 사이트에서 이 최신 버전을 다운로드 한 다음, 이 파일의 해시 값을 md5sum을 사용해서 계산한다.

pchero@MyNote:~$ md5sum vsftpd-1.1.3-8.i386.rpm
d2e807f808c45407f08528f50d29933b  vsftpd-1.1.3-8.i386.rpm

 그런데 만약 계산한 결과 값과 개발자가 올려놓은 md5sum 해시 값이 서로 다르다고 한다면 당신은 방금 다운로드 받은 파일이 개발자가 만 것이 아님을 알 수 있다.

GNU Privacy Guard

 * GNU Privacy Guard

 GNU Privacy Guard(GPG)는 가장 많이 사용되는 오픈 소스 툴의 하나이다. 이 툴은 www.gnupg.org에서 다운 받을 수 있다. 여기서는 모든 Unix 버전용 실행파일과 소스 코드를 다운받을 수 있다. GPG를 사용하면 파일과 e-mail 메시지를 암호화할 수 있다. 또한 공개키들을 import 하거나 export 할 수 있다. 이렇게 하면, PGP 및 GPG에서 생성한 키들을 검사해서, 다운로드 받은 tarball 파일과 RPM 파일이 올바른 파일인지 확인할 수 있다.

 * GNU Privacy Guard 사용하기.
 먼저 GPG가 설치되어 있는지를 검사한다.

pchero@MyNote:~$ whereis gpg
gpg: /usr/bin/gpg /usr/share/man/man1/gpg.1.gz

 이 때 GPG가 설치돼 있지 않다면, GPG를 www.rpmfind.net이나 www.gnupg.org/download.html에서 다운로드 후, 설치해야 한다.

 만약 데비안 혹은 우분투 시스템일 경우

$ sudo apt-get install gpg

 라고 입력한다.

 프로그램이 설치됐다면, 처음 할 일은 루트가 아닌 사용자의 메모리를 보호하는 것이다. 대부분의 리눅스 시스템에서 GPG는 SUID root 로 실행시켜야 한다. 모든 애플리케이션은 시스템에 메모리를 할당하는데, GPG는 이 메모리를 안전하게 보호해야 한다. 메모리가 보호받지 못한다면, 악의적인 사용자가 메모리 정보를 캡쳐한 다음 당신이 암호화하려는 내용의 정보를 빼낼 수 있을 것이다. 이렇게 메모리를 보호하기 위해, GPG는 메모리를 사용하기 전에 먼저 잠그는(lock) 일을 한다. 메모리를 잠그려면 root 권한이 필요하다. 이 메모리를 잠근 뒤에, GPG는 프로그램을 실행시킨 사람의 권한으로 돌아간다.

애플리케이션을 SUID root로 실행시킨다는 말은, 애플리케이션을 실행시킨 사용자가 root가 아니라도 root권한으로 실행시킨다는 뜻이다.

 그러나 기본적으로 GPG는 SUID root로 설치되지 않는다.
 이것을 setuid root로 만들려면 다음과 같이 한다.

1. 애플리케이션을 찾는다.(우분투의 경우 /usr/bin/gpg에 있다.)
2. su – 명령으로 root 계정으로 로그인한다.
3. chmod u+s /usr/bin/gpg 명령을 실행시킨다.

 그리고 다음의 명령을 실행해서 GPG동작에 필요한 디렉토리와 파일들을 생성해야 한다.

$ gpg –gen-key

 그 후, 다음의 명령어로 키 쌍(key pair)을 생성해야 한다.

$ gpg –genkey

 그러면 GPG는 키 유형을 선택하라고 물어볼 것이다. 여기서 선택할 수 있는 것은

 Digital Signature Algorithm(DSA)
 ElGamal(기본 알고리즘)
 DSA(서명만)
 ElGamal(서명과 암호화)

이다. 이들 옵션은 서로 다른 서명 및 암호화 알고리즘을 사용한다. 첫번째 옵션에서는 표준 Elgamal 키 분배 방법과 서로 다른 서명 및 암호화 알고리즘을 사용한다. 첫번째 옵션에서는 표준 Elgamal 키 분배 방법과 DSA를 사용하는데, 이를 이용해 서명을 하거나 데이터를 암호화한다.
 DSA는 기존에 사용하던 RSA와는 달리, 사용에 제한이 없다. 여기서 만약 문서를 서명하고 암호화하고 싶다면 DSA를 사용하면 된다. 대부분의 사람을 정보를 서명하고 암호화하는데 첫번째 옵션을 사용한다. 일반적으로, 첫번째(기본값)를 선택하는 게 좋다.

 그 다음, 키의 크기를 선택해야 한다. 키 크기의 기본값은 2048 bit인데, 보통은 이정도로 충분하다.

 다음은 키 만료 시간을 정하는 부분이다. 키가 1년까지만 유효하도록 설정하려면 1y를 입력하면 된다.

 마지막으로 유저 정보를 입력하는 부분이다.

 Real name : 이름을 입력하는 부분
 Email address : E-Mail 주소를 입력하는 부분
 Comment : 원하는 정보를 넣는다. (ex. GPG Signature)…

 그리고 입력한 정보가 맞는지 확인을 한 다음 비밀키를 생성하기 위한 passphrase를 입력한다. 이 passphrase는 최소한 6글자 이상이 되어야 하며, 물론 당연히 기억할 수 있어야 한다. 이 작업이 끝나면 GPG는 새 개인키를 생성한다. GPG가 끝나면 키가 생성됐고 서명됐다는 메시지를 받을 것이다.

 이제 GPG에서 계정키를 제대로 만들고 서명했는지 확인하는 데는 다음 명령을 실행한다.

 $ gpg –list-secert-key
 $ gpg –list-public-key
 $ gpg –list-sig

 위 명령들은 각각 비밀키와 공개키를 나열하고, 서명을 나열한다.

 다음으로 할 일은 취소 인증서(revocation certificate)를 만들어야 한다. 취소 인증서는, 비밀키를 삭제하고 새로 만들고 싶을 때 사용하는 인증서이다. 취소 인증서를 만드는 과정은 다음과 같다.

pchero@MyNote:~$ gpg –output revoke.asc –gen-revoke pchero21@gmail.com

sec  1024D/68B8F353 2007-11-15 Kim Sung-tae <pchero21@gmail.com>

Create a revocation certificate for this key? (y/N) y
Please select the reason for the revocation:
  0 = No reason specified
  1 = Key has been compromised
  2 = Key is superseded
  3 = Key is no longer used
  Q = Cancel
(Probably you want to select 1 here)
Your decision? 1
Enter an optional description; end it with an empty line:
>
Reason for revocation: Key has been compromised
(No description given)
Is this okay? (y/N) y

You need a passphrase to unlock the secret key for
user: “Kim Sung-tae <pchero21@gmail.com>”
1024-bit DSA key, ID 68B8F353, created 2007-11-15

ASCII armored output forced.
Revocation certificate created.

Please move it to a medium which you can hide away; if Mallory gets
access to this certificate he can use it to make your key unusable.
It is smart to print this certificate and store it away, just in case
your media become unreadable.  But have some caution:  The print system of
your machine might store the data and make it available to others!

 키 쌍과 취소 인증서를 만들었다면, 이제 키를 import 혹은 export할 수 있다. 키를 export 하려면 다음 명령을 실행한다.

 $ gpg –export –armor > yourname.asc

 위 명령은 사용자의 공개키를 담은 파일을 생성하는 명령어이다. 이제 이 공개키를 신뢰관계를 맺고자 하는 사람에게 보내주면 된다.

 그럼, 이제는 RPM 명령을 사용해서 다른 사람이 만든 서명과 공개키를 확인하는 방법을 알아보자.

 먼저 다운로드받은 RPM 패키지가 Red Hat에서 만든 것인지를 확인하기 위해, Red Hat의 서명을 다운 받아야 한다. www.redhat.com으로 간 다음 Red Hat의 공개키를 받고, 원하는 RPM 파일도 다운로드 받는다.

  redhat의 공개키는 다음의 주소에 있다.
http://www.redhat.com/security/team/key/

 버전에 맞는 공개키를 다운받고 다음을 실행한다. 마지막에 쓴 redhat.asc는 파일명이다. 다른 파일명으로 redhat의 공개키를 저장했다면 마지막 부분을 파일명에 맞게 고쳐 써야 한다.

$ gpg –import redhat.asc

 키를 import 했다면 이제 키를 서명해야 한다. 만약 이 키를 서명하지 못하면, 나중에 이 키를 사용하려 할 때 에러가 날 것이다. 키를 서명하려면 gpg –sign 명령을 이용하거나 gpg –edit-key security@redhat.com 명령을 이용하여 서명한다.

pchero@MyNote:~$ gpg –edit-key security@redhat.com
gpg (GnuPG) 1.4.6; Copyright (C) 2006 Free Software Foundation, Inc.
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions. See the file COPYING for details.

pub  1024D/DB42A60E  created: 1999-09-23  expires: never       usage: SCA
                     trust: unknown       validity: unknown
sub  2048g/961630A2  created: 1999-09-23  expires: never       usage: E  
[ unknown] (1). Red Hat, Inc <security@redhat.com>

명령> sign

pub  1024D/DB42A60E  created: 1999-09-23  expires: never       usage: SCA
                     trust: unknown       validity: unknown
 Primary key fingerprint: CA20 8686 2BD6 9DFC 65F6  ECC4 2191 80CD DB42 A60E

     Red Hat, Inc <security@redhat.com>

Are you sure that you want to sign this key with your
key “Kim Sung-tae <pchero21@gmail.com>” (68B8F353)

Really sign? (y/N) y

You need a passphrase to unlock the secret key for
user: “Kim Sung-tae <pchero21@gmail.com>”
1024-bit DSA key, ID 68B8F353, created 2007-11-15

명령> q
Save changes? (y/N) y

 그리고 다운받은 RPM을 확인하는데는 다음의 명령어를 사용한다.

 $ rpm -Kv your_rpm.9386.rpm

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 서버를 구축하고 운영할 수도 있다.

SSL의 구조

 * SSL의 구조
 SSL은 가장 다양한 분야에서 적용하고 있는 암호 프로토콜 중 하나다. TCP/IP는 기본적으로 보안을 고려하지 않으므로 전자상거래를 비롯한 다양한 온라인 서비스를 활성화하려면 서비스 상호간의 인증, 암호와 통신을 비롯한 문제점을 해결해야 한다.

 * SSL의 개념
 SSL은 ‘Secure Socket Layer’의 약자로 넷스케이프(Netscape)에서 처음 개발하여 넷스케이프 네이게이터(Netscape Navigator)에 처음 적용한 보안 프로토콜이다. 기본적인 아이디어는 기본의 TCP/IP에서 고려하지 않은 보안 세션을 추가하여 통신 상호 간의 전송 메시지에 대한 암호화와 상호 인증을 수행하는 프로토콜이다.

사용자 삽입 이미지
 그림과 같이 SSL은 기존의 TCP/IP에서 응용 계층과 TCP 계층 사이에 추가된 보안 계층으로 동작한다. TCP 계층은 응용 계층의 프로톨 간의 인터페이스를 위하여 소켓을 이용하며, SSL은 각 소켓으로 연결된 상호 간의 통신 채널을 안전하게 관리하는 기능을 한다. 클라이언트와 서버 간의 안전한 통신을 위하여 SSL은 다음과 같은 기능을 제공한다.

==================================================================================================
기능            | 설명
==================================================================================================
상호인증       | 클라이언트/서버 간의 인증을 위한 세션 키를 생성하고 “Hello”라는 메시지를 교환함으로써
                | 상호 인증을 한다.
————————————————————————————————–
기밀성         | 전송되는 메시지에 대한 다양한 형식의 암호화를 통해서 타인에게 메시지의 내용을 노출하
                | 지 않도록 한다.
————————————————————————————————–
무결성         | 전송 메시지의 변조를 방지할 수 있도록 MD5를 비롯한 알고리즘을 이용하여 전자 서명을 한다.
==================================================================================================

 기밀성이나 무결성 등의 암호학적 기능을 제공하기 위해서는 국제적으로 표준화되고 있는 암호 알고리즘의 지원이 따라야 한다. SSL은 표준화되어 있는 주요 암호 알고리즘을 비롯하여 현재 주로 이용하는 다양한 암호 알고리즘을 지원하고 있다.

==================================================================================================
기능          | 적용 알고리즘
==================================================================================================
기밀성        | DES, 3DES, IDEA, RC2, RC4
————————————————————————————————–
무결성        | MD5
————————————————————————————————–
서버 인증     | RSA, X509
————————————————————————————————–
클라이언트 인증 | 현재 대부분의 웹 브라우저에서 클라이언트 인증을 제공함
==================================================================================================

 * SSL의 구조 및 통신 절차
 SSL은 다수의 프로토콜로 구성되어 있는 프로토콜 집합이라고 할 수 있으며, 패킷 분할, 세션 관리 등의 기본적인 통신과 관련된 처리를 수행하는 부분과 데이터의 암호화, 복호화, 메시지 인증 같은 보안 서비스를 담당하고 있는 부분으로 구분할 수 있다.

사용자 삽입 이미지
 그림에서 SSL 핸드쉐이크 프로토콜, SSL 변겸 암호 스펙, SSL 경고 프로토콜이 SSL의 세션 관리 및 동작을 처리하는 부분이며, 메시지의 암호화 및 무결성 등의 보안 서비스는 SSL 레코드 프로토콜에서 담당한다.

 * SSL 핸드쉐이크 프로토콜
 클라이언트와 서버 간의 통신을 수행하기 위한 첫번째 단계는 상호 간의 통신을 위한 세션(Session)을 설정하는 과정이다. 세션은 데이터 전송을 위한 논리적인 경로로, 실제로는 여러 개의 커넥션(Connection)이 포함이 되어 클라이언트와 서버 간의 논리적인 관계를 표시하는 정보를 포함하고 있다. SSL에서 세션 상태 연결을 수행하는 것이 SSL 핸드쉐이크 프로토콜(SSL Handshake Protocol)이며 한 세션 동안 이용되는 암호 매개 변수를 생성하여 클라이언트와 서버 간에 공유하기 위해서 사용한다.

 SSL 핸드쉐이크 프로토콜은 SSL 레코드 프로토콜(Record Protocol)에 의해서 메시지의 송수신을 위한 작업보다 먼저 수행이 되어 다음과 같은 세션 정보들을 생성하게 된다.

 세션 식별자(Session Identifier) : 활동 또는 보류 중인 세션을 식별하기 위해서 서버가 임의로 설정한 식별자
 인증서(peer certification) : 서버와 클라이언트 주체의 X.509 인증서
 Cipher Spec : 다량의 데이터에 대해 수행되는 압축 알고리즘과 MAC 알고리즘에 대한 규격
 압축 방법(Compression method) : 암호화 이전에 데이터 압축을 위해 사용하는 알고리즘
 Master secret : 클라이언트와 서버 간에 공유하는 48비트 길이의 비밀 값
 재사용 여부 : 세션이 새로운 연결에 사용 가능한지 여부를 나타내는 플래그

 세션이 설정된 후 해당 세션 내에 수행되는 각 커넥션에서 메시지는 세션 정보의 Master secret과 클라이언트의 난수를 통해서 각 커넥션마다 다르게 생성된 비밀 키에 의해서 암호화된다. 따라서 공격자는 스니핑을 통해서 전송 내용을 도청하더라도 전송되는 메시지를 복호화하기 위한 정보를 얻어내기 어렵게 된다.

사용자 삽입 이미지
  세션을 설정하는 첫번째 단계로 클라이언트와 서버는 Hello 메시지를 교환하며, 이 과정을 통해서 SSL 프로토콜 보존, 세션 ID, 암호 알고리즘, 압축 방법을 설정하고 ClientHello.random과 ServerHello.random을 생성, 교환하게 된다. ClientHello 메시지를 받은 서버는 클라이언트의 인증서를 요구할 수 있으며, 이 경우 서버 자신의 인증서를 보내며 그렇지 않을 경우 ServerKeyExchange를 응답에 포함하여, CertificateRequest를 보내고, Hello 절차가 완료됨을 알리는 ServerHelloDone 메시지를 클라이언트로 보내어 응답한다.

 클라이언트는 Hello에 대한 응답을 받아서 클라이언트의 인증서 또는 ClientKeyExchange와 클라이언트가 사용할 ChangeCipherSpec 메시지를 보내고 뒤이어 CipherSpec에 맞추어 작성된 finished 메시지를 서버로 보낸다.

서버는 ChangeCipherSpec 메시지와 이를 이용해서 만들어진 Finished 메시지를 받은 후 서버가 사용할 CihpherSpec을 ChangeCipherSpec 메시지와 Finished 메시지를 보내고 이후에는 합의된 내용으로 Application Data의 교환이 이루어지게 된다.

 * 세션(Session)과 커넥션(Connection)
 커넥션은 실제로 데이터이 송 수신이 이루어지는 단위를 말하며, Peer-to-Peer 관계가 성립하는 개념이다. 이에 비해서 세션은 논리적인 개념에 가까우며 컴퓨터 사용 시부터 종료 시까지의 개념을 포함한다. 실제로 한 세션은 여러 커넥션을 포함할 수 있다.

 
 * SSL 레코드 프로토콜
 SSL 레코드 프로토콜은 상호 송수신을 위한 암호화 스펙이 SSL 핸드쉐이크 프로토콜에 의해 공유된 후 이러한 스펙에 따라 실제로 전송하는 데이터를 TCP 패킷으로 변환하기 위한 기능을 수행한다. SSL 레코드 프로토콜은 이를 위해 먼저 전송하는 메시지를 일정한 단위의 조각으로 분리하는 Fragmentation, 레코드 압축과 해제, 메시지 인증 및 암호화, 복호화 기능을 수행한다.

사용자 삽입 이미지
 SSL 레코드 프로토콜은 각 메시지에 대해 다음과 같은 절차를 통해서 전송을 수행한다. 먼저 전송할 메시지를 일정한 크기의 레코드 프로토콜 유닛(Record Protocol Unit)으로 분해하고, 각 유닛을 SSL 핸드쉐이크 프로토콜로 사전에 협의한 규칙에 맞게 압축한다. 다음으로 압축된 유닛마다 해시를 이용한 MAC을 생성한 후 이것을 포함하여 암호화한 후 전송한다.

 이 과정에서 사전에 클라이언트와 서버 간에 협의한 스펙에 따라 암호화, MAC 생성, 압축 방식 등을 수행하므로, 메시지를 수신한 측에서도 정상적으로 해독할 수 있다.

 * 변경 암호 스펙 프로토콜
 SSL 변경 암호 스펙(Change Cipher Spec) 메시지는 이후에 전송되는 레코드에 대해서 암호 스켁(Cipher Spec)에 정의된 알고리즘과 키를 이용해서 보호할 수 있도록 수신측에 알려주는 용도로 사용한다. 간단히 요약하면 송수신하는 데 사용하는 암호화 알고리즘, MAC, 압축 방식 등을 요약하고 있는 암호 스펙을 변경하는 데 이용한다.

 클라이언트는 키 교환 메시지와 인증서 확인 메시지를 전송한 후 보내며, 서버는 클라이언트에서 받은 키 교환 메시지를 성공적으로 처리한 후 보내게 된다.

 * SSL 경고 프로토콜
 SSL 경고 프로토콜(SSL Alert Protocol)은 SSL의 동작 과정에서 발생할 수 있는 문제 상황에 대해 경고를 전달하기 위해서 사용한다. 전송하는 경고 정보는 SSL 스펙에 정의되어 있는 치명적은 경고 목록과 메시지의 심각성을 나타내는 2바이트 데이터로 구성된다.