C++

[C++] OpenSSL을 이용한 간단한 echo 프로그래밍

xaida 2017. 11. 27. 17:38

우선 구현하기에 앞서 SSL 통신에 필요한 certificate를 하나 생성하자

openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem


위의 명령어를 입력하면 "mycert.pem" 이라는 Certificate File 이 하나 만들어진다


Client 부분 먼저 코딩을 해 보았다


1.소켓 생성하고 서버에게 연결요청하기

socket() 함수를 이용하여 소켓을 생성한다 → sockaddr_in 함수를 이용하여 서버의 IP주소와 PORT를 지정한다 → connect() 함수를 이용하여 서버로 연결을 요청한다.

 헤더

 #include <sys/socket.h>

 #include <arpa/inet.h>

 코드

 int connectServer(const char *hostname, int port)
{
    int sd;
    struct hostent *host;
    struct sockaddr_in addr;

    sd = socket(PF_INET, SOCK_STREAM, 0);
    bzero(&addr, sizeof(addr)); //구조체를 0으로 초기화 시킨다.
    addr.sin_family = PF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr (hostname);

    if ( connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
    {
        printf("filed to connection");
        close(sd);
        abort();
    }
    return sd;
}

- int socket(int protocolFamily, int type, int protocol)

 인자1 ) protocolFamaily : 프로토콜 체계를 말한다 PF_INET 은 인터넷 프로토콜을 의미한다.

 인자2 ) type : 서비스 타입으로 프로토콜마다 3가지 방식(STREAM, DGRAM, RAW)이있다.  SOCK_STREAM 은 tcp 방식을 의미한다.

 인자3 ) protocol : 소켓에서 사용할 프로토콜이다 type에서 미리 정해진 경우 인자로 0을 넣는다.

 반환값 : 소켓생성 실패 -1, 성공 fd>0 


- int connect ( int sockfd, const struct sockaddr* serv_addr, socklen_t addrlen)

 인자1) sockfd  : 소켓 스크립터

 인자2) serv_addr :  서버 주소 정보 구조체를 가르키는 포인터

 인자3) addrlen : 서버 주소 정보 구조체의 크기

 반환값 : 연결 성공 0 , 연결 실패 -1


- 주소구조체 sockaddr_in , sockaddr

 sockaddr_in

 sockaddr

 short sin_family 

 u_short safamily

 u_short sin_port 

 char sa_data[14]

 struct in_addr sin_addr

 char sin_zero[8]



2. 암호화 통신을 위한 초기화 작업을 한다 →  SSL_CTX_new 함수를 이용하여 ssl_ctx 를 생성한다.

 헤더

 #include <openssl/ssl.h>

 #include <openssl/err.h>

 코드

 SSL_CTX* InitCTX(void)
{
    const SSL_METHOD *method;
    SSL_CTX *ctx;
    SSL_library_init();
    SSL_load_error_strings();
    method = TLSv1_2_client_method();
    ctx = SSL_CTX_new(method);
    if ( ctx == NULL )
    {
        printf("SSL_CTX 생성에러\n");
        abort();  //비정상적 강제 종료
    }
    return ctx;
}

 * 초기화에 있어 반드시 필요한 함수는 아래와 같다. 

  (1) void SSL_load_error_strings(void);   - 에러메시지 문자열들을 로드한다.

  (2) void SSL_library_init();  - OpenSSL에서 사용될 알고리즘들을 초기화한다.

  (3) TLSv1_2_client_method(void);  -메소드함수를 이용해 클라이언트의 통신 방식을 tls1.2 프로토콜로 지정한다.

     (SSL_METHOD : SSL 버전을 나타낸다. 각버전에 따라 지원되는 알고리즘이나 프로토콜 형식은 다르다)  

  


 3. SSL_new 함수를 이용하여 SSL(구조체)을 생성한다 → SSL_set_fd 함수를 이용하여 현재 소켓과 ssl 연결

 SSL *ssl;

 ssl = SSL_new(ctx);

 SSL_set_fd(ssl, server);

  * SSL : SSL_CTX를 이용하여 생성된다.  SSL 구조체는 ssl 통신을 하는 상대서버와 관련된 구조체로 상대방과 연결하고 초기 협상과정을 진행하며 데이터를 주고받는 일을 한다.  



4. SSL_connect 함수를 이용하여 handshake 과정을 맺는다.

 SSL_connect(ssl);

 * SSL_connect 실패시 반환값 : -1



5. SSL_write 함수를 이용하여 서버에게 메시지를 암호화하여 전송하고 서버로부터 암호화된 메시지를 받는다.

 const char *msg = "Hello";
 SSL_write(ssl, msg, strlen(msg));



6. SSL_read 함수를 이용하여 서버로 부터 받은 메시지를 복호화하여 확인한다.

 char buf[1024];

 int bytes;

 bytes = SSL_read(ssl, buf, sizeof(buf));

 buf[bytes] = 0;

 printf("Received: \"%s\"\n", buf);



7. 연결을 끊고 객체들을 제거한다.

 헤더

 #include <unistd.h>

 SSL_free(ssl);

 close(server);
 SSL_CTX_free(ctx);


 

전체코드를 보면 ~

client.cpp

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>


#define FAIL -1
int connectServer(const char *hostname, int port)
{
    int sd;
    struct hostent *host;
    struct sockaddr_in addr;

    sd = socket(PF_INET, SOCK_STREAM, 0);
    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr (hostname);

    if ( connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
    {
        printf("Filed to connection");
        close(sd);
        abort();
    }
    return sd;
}


SSL_CTX* InitCTX(void)
{
    const SSL_METHOD *method;
    SSL_CTX *ctx;
    SSL_library_init();
    SSL_load_error_strings();
    method = TLSv1_2_client_method();
    ctx = SSL_CTX_new(method);
    if ( ctx == NULL )
    {
        printf("Faild to create SSL_CTX\n");
        abort();
    }
    return ctx;
}


void ShowCerts(SSL* ssl)
{
    X509 *cert;
    char *line;
    cert = SSL_get_peer_certificate(ssl);
    if ( cert != NULL )
    {
        printf("Server certificates:\n");
        line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
        printf("Subject: %s\n", line); free(line);
        line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
        printf("Issuer: %s\n", line); free(line);
        X509_free(cert);
    }
    else
        printf("No certificates.\n");
}


int main()
{
    SSL_CTX *ctx;
    int server;
    SSL *ssl;
    char buf[1024];
    int bytes;

    server = connectServer("127.0.0.1", 1111);
    ctx = InitCTX();
    ssl = SSL_new(ctx);

    SSL_set_fd(ssl, server);

    if ( SSL_connect(ssl) == FAIL )
        printf("Faild to HandShake\n");
    else
    {
        const char *msg = "Hello";
        printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
        ShowCerts(ssl);
        SSL_write(ssl, msg, strlen(msg));
        bytes = SSL_read(ssl, buf, sizeof(buf));
        buf[bytes] = 0;
        printf("Received: \"%s\"\n", buf);
        SSL_free(ssl);
    }
    close(server);
    SSL_CTX_free(ctx);
}



출처 : https://m.blog.naver.com/PostView.nhn?blogId=jihye2340&logNo=220679910676&proxyReferer=https%3A%2F%2Fwww.google.co.kr%2F