Add SSL/TLS Certificate section to documentation

Closes #2910
This commit is contained in:
Mohammad Nejati
2025-02-26 09:53:15 +00:00
committed by Mohammad Nejati
parent 0451018f25
commit 41c1abb402
12 changed files with 604 additions and 1 deletions

View File

@ -13,4 +13,6 @@ add_subdirectory(advanced)
add_subdirectory(http)
add_subdirectory(websocket)
add_subdirectory(doc)
add_subdirectory(echo-op)

View File

@ -30,5 +30,7 @@ build-project advanced ;
build-project http ;
build-project websocket ;
build-project doc ;
# legacy
build-project echo-op ;

View File

@ -0,0 +1,12 @@
#
# Copyright (c) 2025 Mohammad Nejati
#
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#
# Official repository: https://github.com/boostorg/beast
#
if (OPENSSL_FOUND)
add_subdirectory(ssl)
endif ()

10
example/doc/Jamfile Normal file
View File

@ -0,0 +1,10 @@
#
# Copyright (c) 2025 Mohammad Nejati
#
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#
# Official repository: https://github.com/boostorg/beast
#
build-project ssl ;

View File

@ -0,0 +1,44 @@
#
# Copyright (c) 2025 Mohammad Nejati
#
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#
# Official repository: https://github.com/boostorg/beast
#
# Client
add_executable(doc-ssl-client
Jamfile
client.cpp)
source_group("" FILES
Jamfile
client.cpp)
target_include_directories(doc-ssl-client
PRIVATE ${PROJECT_SOURCE_DIR})
target_link_libraries(doc-ssl-client
PRIVATE Boost::beast OpenSSL::SSL OpenSSL::Crypto)
set_target_properties(doc-ssl-client
PROPERTIES FOLDER "example-doc-ssl")
# Server
add_executable(doc-ssl-server
Jamfile
server.cpp)
source_group("" FILES
Jamfile
server.cpp)
target_include_directories(doc-ssl-server
PRIVATE ${PROJECT_SOURCE_DIR})
target_link_libraries(doc-ssl-server
PRIVATE Boost::beast OpenSSL::SSL OpenSSL::Crypto)
set_target_properties(doc-ssl-server
PROPERTIES FOLDER "example-doc-ssl")

20
example/doc/ssl/Jamfile Normal file
View File

@ -0,0 +1,20 @@
import ac ;
project
: requirements
[ ac.check-library /boost/beast/test//lib-asio-ssl : <library>/boost/beast/test//lib-asio-ssl/<link>static : <build>no ]
;
exe doc-ssl-client :
client.cpp
:
<variant>coverage:<build>no
<variant>ubasan:<build>no
;
exe doc-ssl-server :
server.cpp
:
<variant>coverage:<build>no
<variant>ubasan:<build>no
;

137
example/doc/ssl/client.cpp Normal file
View File

@ -0,0 +1,137 @@
//
// Copyright (c) 2025 Mohammad Nejati
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <iostream>
#if defined(BOOST_ASIO_HAS_CO_AWAIT)
namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
namespace ssl = net::ssl;
void
print_exception(std::exception_ptr eptr)
{
if(eptr)
{
try
{
std::rethrow_exception(eptr);
}
catch(std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
}
net::awaitable<void>
request(ssl::context& ctx)
{
auto executor = co_await net::this_coro::executor;
net::ip::tcp::endpoint endpoint{ {}, 8080 };
ssl::stream<net::ip::tcp::socket> stream{ executor, ctx };
// Connect TCP socket
co_await stream.lowest_layer().async_connect(endpoint);
// Set Server Name Indication (SNI)
if(!SSL_set_tlsext_host_name(stream.native_handle(), "localhost"))
{
throw beast::system_error(
static_cast<int>(::ERR_get_error()),
net::error::get_ssl_category());
}
// Set a callback to verify that the hostname in the server
// certificate matches the expected value
stream.set_verify_callback(ssl::host_name_verification("localhost"));
// Perform the SSL handshake
co_await stream.async_handshake(ssl::stream_base::client);
// Write an HTTP GET request
http::request<http::empty_body> req{ http::verb::get, "/", 11 };
req.set(http::field::host, "localhost");
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
co_await http::async_write(stream, req);
// Read the response
beast::flat_buffer buf;
http::response<http::string_body> res;
co_await http::async_read(stream, buf, res);
// Print the response body
std::cout << res.body();
// Gracefully shutdown the SSL stream
auto [ec] = co_await stream.async_shutdown(net::as_tuple);
if(ec && ec != ssl::error::stream_truncated)
throw boost::system::system_error(ec);
}
int
main()
{
try
{
// The io_context is required for all I/O
net::io_context ioc;
// The SSL context is required, and holds certificates,
// configurations and session related data
ssl::context ctx{ ssl::context::sslv23 };
// https://docs.openssl.org/3.4/man3/SSL_CTX_set_options/
ctx.set_options(
ssl::context::no_sslv2 | ssl::context::default_workarounds |
ssl::context::single_dh_use);
// set up the peer verification mode so that the TLS/SSL handshake fails
// if the certificate verification is unsuccessful
ctx.set_verify_mode(ssl::verify_peer);
// The servers's certificate will be verified against this
// certificate authority.
ctx.load_verify_file("ca.crt");
// In a real application, the passphrase would be read from
// a secure place, such as a key vault.
ctx.set_password_callback([](auto, auto) { return "123456"; });
// Client certificate and private key (if server request for).
ctx.use_certificate_chain_file("client.crt");
ctx.use_private_key_file("client.key", ssl::context::pem);
net::co_spawn(ioc, request(ctx), print_exception);
ioc.run();
}
catch(std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
#else
int
main(int, char*[])
{
std::printf("awaitables require C++20\n");
return EXIT_FAILURE;
}
#endif

133
example/doc/ssl/server.cpp Normal file
View File

@ -0,0 +1,133 @@
//
// Copyright (c) 2025 Mohammad Nejati
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <iostream>
#if defined(BOOST_ASIO_HAS_CO_AWAIT)
namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
namespace ssl = net::ssl;
void
print_exception(std::exception_ptr eptr)
{
if(eptr)
{
try
{
std::rethrow_exception(eptr);
}
catch(std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
}
net::awaitable<void>
handle_session(ssl::stream<net::ip::tcp::socket> stream)
{
// Perform the SSL handshake
co_await stream.async_handshake(ssl::stream_base::server);
// Read and discard a request
beast::flat_buffer buf;
http::request<http::empty_body> req;
co_await http::async_read(stream, buf, req);
// Write the response
http::response<http::string_body> res;
res.body() = "Hello!";
co_await http::async_write(stream, res);
// Gracefully shutdown the SSL stream
auto [ec] = co_await stream.async_shutdown(net::as_tuple);
if(ec && ec != ssl::error::stream_truncated)
throw boost::system::system_error(ec);
}
net::awaitable<void>
acceptor(ssl::context& ctx)
{
auto executor = co_await net::this_coro::executor;
net::ip::tcp::endpoint endpoint{ {}, 8080 };
net::ip::tcp::acceptor acceptor{ executor, endpoint };
for(;;)
{
net::co_spawn(
executor,
handle_session({ co_await acceptor.async_accept(), ctx }),
print_exception);
}
}
int
main()
{
try
{
// The io_context is required for all I/O
net::io_context ioc;
// The SSL context is required, and holds certificates,
// configurations and session related data
ssl::context ctx{ ssl::context::sslv23 };
// https://docs.openssl.org/3.4/man3/SSL_CTX_set_options/
ctx.set_options(
ssl::context::no_sslv2 | ssl::context::default_workarounds |
ssl::context::single_dh_use);
// Comment this line to disable client certificate request.
ctx.set_verify_mode(
ssl::verify_peer | ssl::verify_fail_if_no_peer_cert);
// The client's certificate will be verified against this
// certificate authority.
ctx.load_verify_file("ca.crt");
// In a real application, the passphrase would be read from
// a secure place, such as a key vault.
ctx.set_password_callback([](auto, auto) { return "123456"; });
// Server certificate and private key.
ctx.use_certificate_chain_file("server.crt");
ctx.use_private_key_file("server.key", ssl::context::pem);
// DH parameters for DHE-based cipher suites
ctx.use_tmp_dh_file("dh4096.pem");
net::co_spawn(ioc, acceptor(ctx), print_exception);
ioc.run();
}
catch(std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
#else
int
main(int, char*[])
{
std::printf("awaitables require C++20\n");
return EXIT_FAILURE;
}
#endif