-
Openssl을 이용한 암호화 통신검색하기 귀찮아서 블로그에 박제 2021. 10. 13. 11:18728x90
서버는 암호화 통신을 위하여 가장 먼저 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'검색하기 귀찮아서 블로그에 박제' 카테고리의 다른 글
[C++] ISO C++11 does not allow conversion from string literal to 'char *' (0) 2021.11.25 ROCKCHIP: rknn-toolkit install (0) 2021.11.04 [Linux error] C/C++ openssl 빌드 에러 (0) 2021.10.13 ^M 제거방법 (0) 2021.10.08 AssertionError: CUDA unavailable, invalid device 0 requested (0) 2021.07.19