SSL 서버를 구동하기 위해서는 인증서가 필요하다.
자체 서명 인증서
를 생성한다.
$ openssl genrsa -out private.key 2048
$ openssl req -x509 -new -nodes -key private.key -days 365 -out certificate.crt -subj "/C=KR/ST=Seoul/L=Seoul/O=test/OU=test"
OpenSSL
라이브러리를 이용하기 위해 패키지를 설치한다.
$ sudo apt -y install libssl-dev
boost의 asio를 이용하여 비동기적으로 데이터를 읽고 다시 ECHO 해주는 서버를 작성해본다.
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
class session {
public:
session(boost::asio::io_service& io_service, boost::asio::ssl::context& context) : socket_(io_service, context) {}
ssl_socket::lowest_layer_type& socket() {
return socket_.lowest_layer();
}
void start() {
socket_.async_handshake(boost::asio::ssl::stream_base::server, boost::bind(&session::handle_handshake, this, boost::asio::placeholders::error));
}
void handle_handshake(const boost::system::error_code& error) {
if (!error) {
socket_.async_read_some(boost::asio::buffer(data_, max_length), boost::bind(&session::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
else {
delete this;
}
}
void handle_read(const boost::system::error_code& error, size_t bytes_transferred) {
if (!error) {
boost::asio::async_write(socket_, boost::asio::buffer(data_, bytes_transferred), boost::bind(&session::handle_write, this, boost::asio::placeholders::error));
}
else
{
delete this;
}
}
void handle_write(const boost::system::error_code& error) {
if (!error) {
socket_.async_read_some(boost::asio::buffer(data_, max_length), boost::bind(&session::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
else {
delete this;
}
}
private:
ssl_socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
class server {
public:
server(boost::asio::io_service& io_service, unsigned short port) : io_service_(io_service), acceptor_(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)), context_(boost::asio::ssl::context::sslv23) {
context_.set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use);
context_.use_certificate_chain_file("certificate.crt");
context_.use_private_key_file("private.key", boost::asio::ssl::context::pem);
start_accept();
}
void start_accept()
{
session* new_session = new session(io_service_, context_);
acceptor_.async_accept(new_session->socket(), boost::bind(&server::handle_accept, this, new_session, boost::asio::placeholders::error));
}
void handle_accept(session* new_session, const boost::system::error_code& error) {
if (!error) {
new_session->start();
}
else {
delete new_session;
}
start_accept();
}
private:
boost::asio::io_service& io_service_;
boost::asio::ip::tcp::acceptor acceptor_;
boost::asio::ssl::context context_;
};
int main(int argc, char* argv[]) {
try {
boost::asio::io_service io_service;
server s(io_service, 1234);
io_service.run();
}
catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
컴파일 시 libssl.so, libcrypto.so, libpthread.so를 연결한다.
주어진 호스트 정보를 통해 SSL 서버에 접속한다.
접속 시 인증서 정보는 자체 인증서
이기 때문에 검증은 무시
하도록 한다.
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
enum { max_length = 1024 };
class client {
public:
client(boost::asio::io_service& io_service, boost::asio::ssl::context& context, boost::asio::ip::tcp::resolver::iterator endpoint_iterator) : socket_(io_service, context) {
socket_.set_verify_mode(boost::asio::ssl::verify_none);
boost::asio::async_connect(socket_.lowest_layer(), endpoint_iterator, boost::bind(&client::handle_connect, this, boost::asio::placeholders::error));
}
void handle_connect(const boost::system::error_code& error) {
if (!error) {
socket_.async_handshake(boost::asio::ssl::stream_base::client, boost::bind(&client::handle_handshake, this, boost::asio::placeholders::error));
}
else {
std::cout << "Connect failed: " << error.message() << "\n";
}
}
void handle_handshake(const boost::system::error_code& error) {
if (!error) {
std::cout << "Enter message: ";
std::cin.getline(request_, max_length);
size_t request_length = strlen(request_);
boost::asio::async_write(socket_, boost::asio::buffer(request_, request_length), boost::bind(&client::handle_write, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
else {
std::cout << "Handshake failed: " << error.message() << "\n";
}
}
void handle_write(const boost::system::error_code& error, size_t bytes_transferred) {
if (!error) {
boost::asio::async_read(socket_, boost::asio::buffer(reply_, bytes_transferred), boost::bind(&client::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
else {
std::cout << "Write failed: " << error.message() << "\n";
}
}
void handle_read(const boost::system::error_code& error, size_t bytes_transferred) {
if (!error) {
std::cout << "Reply: ";
std::cout.write(reply_, bytes_transferred);
std::cout << "\n";
}
else {
std::cout << "Read failed: " << error.message() << "\n";
}
}
private:
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
char request_[max_length];
char reply_[max_length];
};
int main(int argc, char* argv[]) {
try {
boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query("127.0.0.1", "1234");
boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
client c(io_service, ctx, iterator);
io_service.run();
}
catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
컴파일 시 libssl.so, libcrypto.so, libpthread.so를 연결한다.