본문 바로가기
-----ETC-----/C++ 네트워크 프로그래밍 시리즈

[C++ 네트크 프로그래밍] Day 17: 네트워크 보안 기초 (SSL/TLS)

by cogito21_cpp 2024. 8. 1.
반응형

네트워크 보안 기초 (SSL/TLS)

SSL(Secure Sockets Layer)과 TLS(Transport Layer Security)는 네트워크 통신을 보호하기 위한 프로토콜입니다. SSL은 초기 버전이고, TLS는 SSL의 업그레이드 버전입니다. 이들은 데이터를 암호화하여 중간에서 도청하거나 변조하지 못하도록 보호합니다.

SSL/TLS의 주요 기능

  1. 암호화: 전송되는 데이터를 암호화하여 보호합니다.
  2. 무결성: 데이터가 전송 중에 변조되지 않도록 합니다.
  3. 인증: 서버와 클라이언트의 신원을 확인하여 신뢰할 수 있는 통신을 보장합니다.

SSL/TLS의 주요 구성 요소

  1. 공개 키 암호화: 데이터 암호화에 사용되는 두 개의 키(공개 키와 비밀 키)를 이용한 암호화 방식입니다.
  2. 대칭 키 암호화: 동일한 키로 데이터를 암호화하고 복호화하는 방식입니다. TLS 세션이 설정되면, 대칭 키 암호화를 사용하여 데이터를 전송합니다.
  3. 디지털 인증서: 서버와 클라이언트의 신원을 확인하는 데 사용됩니다.

OpenSSL을 사용한 SSL/TLS 통신

OpenSSL은 SSL과 TLS 프로토콜을 구현한 오픈 소스 라이브러리입니다. 이를 사용하여 SSL/TLS 통신을 구현할 수 있습니다. Boost.Asio는 OpenSSL을 사용하여 SSL/TLS를 지원합니다.

SSL/TLS 서버 구현 예제

AsyncSslServer.h

#ifndef ASYNCSSLSERVER_H
#define ASYNCSSLSERVER_H

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <memory>
#include <iostream>

namespace beast = boost::beast; // Boost.Beast 네임스페이스
namespace net = boost::asio; // Boost.Asio 네임스페이스
namespace ssl = net::ssl; // SSL 네임스페이스
using tcp = net::ip::tcp; // TCP 네임스페이스

// 세션 클래스 정의
class Session : public std::enable_shared_from_this<Session> {
public:
    // 생성자
    explicit Session(tcp::socket socket, ssl::context& ctx)
        : ssl_stream_(std::move(socket), ctx) {}

    // 세션 실행
    void run() {
        auto self = shared_from_this();
        ssl_stream_.async_handshake(ssl::stream_base::server,
            [this, self](beast::error_code ec) {
                if (!ec) {
                    do_read();
                }
            });
    }

private:
    // 데이터 읽기
    void do_read() {
        auto self = shared_from_this();
        ssl_stream_.async_read_some(net::buffer(data_),
            [this, self](beast::error_code ec, std::size_t length) {
                if (!ec) {
                    do_write(length);
                }
            });
    }

    // 데이터 쓰기
    void do_write(std::size_t length) {
        auto self = shared_from_this();
        net::async_write(ssl_stream_, net::buffer(data_, length),
            [this, self](beast::error_code ec, std::size_t /*length*/) {
                if (!ec) {
                    do_read();
                }
            });
    }

    ssl::stream<tcp::socket> ssl_stream_;
    char data_[1024];
};

// 리스너 클래스 정의
class Listener : public std::enable_shared_from_this<Listener> {
public:
    // 생성자
    Listener(net::io_context& ioc, ssl::context& ctx, tcp::endpoint endpoint)
        : acceptor_(ioc), socket_(ioc), ctx_(ctx) {
        beast::error_code ec;

        // 소켓 열기
        acceptor_.open(endpoint.protocol(), ec);
        if (ec) {
            std::cerr << "오류: " << ec.message() << std::endl;
            return;
        }

        // 소켓 옵션 설정
        acceptor_.set_option(net::socket_base::reuse_address(true), ec);
        if (ec) {
            std::cerr << "오류: " << ec.message() << std::endl;
            return;
        }

        // 바인딩
        acceptor_.bind(endpoint, ec);
        if (ec) {
            std::cerr << "오류: " << ec.message() << std::endl;
            return;
        }

        // 수신 대기
        acceptor_.listen(net::socket_base::max_listen_connections, ec);
        if (ec) {
            std::cerr << "오류: " << ec.message() << std::endl;
            return;
        }

        do_accept();
    }

private:
    // 연결 수락
    void do_accept() {
        acceptor_.async_accept(socket_,
            [this](beast::error_code ec) {
                if (!ec) {
                    std::make_shared<Session>(std::move(socket_), ctx_)->run();
                }
                do_accept();
            });
    }

    tcp::acceptor acceptor_;
    tcp::socket socket_;
    ssl::context& ctx_;
};

#endif // ASYNCSSLSERVER_H

 

main.cpp

#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <memory>
#include "AsyncSslServer.h"

namespace net = boost::asio;
using tcp = net::ip::tcp;

int main() {
    try {
        auto const address = net::ip::make_address("0.0.0.0");
        auto const port = static_cast<unsigned short>(std::atoi("12345"));

        net::io_context ioc{1};

        ssl::context ctx{ssl::context::tlsv12};
        ctx.set_options(ssl::context::default_workarounds
                      | ssl::context::no_sslv2
                      | ssl::context::single_dh_use);

        // SSL 인증서와 키 설정
        ctx.use_certificate_chain_file("server.crt");
        ctx.use_private_key_file("server.key", ssl::context::pem);

        std::make_shared<Listener>(ioc, ctx, tcp::endpoint{address, port})->run();
        ioc.run();
    } catch (const std::exception& e) {
        std::cerr << "예외 발생: " << e.what() << std::endl;
    }

    return 0;
}

 

설명

위의 코드는 Boost.Asio와 OpenSSL을 사용하여 SSL/TLS 통신을 지원하는 서버를 구현한 예제입니다. 이 서버는 클라이언트의 연결을 수락하고, 데이터를 주고받는 기능을 제공합니다.

  • Session 클래스:
    • Session 클래스는 클라이언트와의 세션을 관리합니다. SSL 스트림을 사용하여 클라이언트와 데이터를 주고받습니다.
    • do_read() 함수는 클라이언트로부터 데이터를 비동기적으로 읽습니다.
    • do_write() 함수는 클라이언트에게 데이터를 비동기적으로 씁니다.
  • Listener 클래스:
    • Listener 클래스는 클라이언트의 연결을 수락합니다. TCP 수신자를 사용하여 연결을 수락하고, 새로운 Session 객체를 생성합니다.
    • do_accept() 함수는 비동기적으로 클라이언트의 연결을 수락합니다.
  • main 함수:
    • main 함수는 io_context와 SSL 컨텍스트를 생성하고, SSL 인증서와 키를 설정합니다.
    • 리스너를 생성하여 실행하고, io_context의 작업을 실행합니다.

이제 열일곱 번째 날의 학습을 마쳤습니다. SSL/TLS의 기본 개념과 Boost.Asio와 OpenSSL을 사용하여 SSL/TLS 통신을 지원하는 서버를 구현하는 방법을 학습했습니다.

질문이나 피드백이 있으면 언제든지 댓글로 남겨 주세요. 내일은 "OpenSSL을 사용한 SSL/TLS 통신"에 대해 학습하겠습니다.

반응형