mirror of
https://github.com/boostorg/beast.git
synced 2025-07-30 12:57:31 +02:00
committed by
Mohammad Nejati
parent
0451018f25
commit
41c1abb402
217
doc/qbk/03_core/9_ssl_tls_certificate.qbk
Normal file
217
doc/qbk/03_core/9_ssl_tls_certificate.qbk
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
[/
|
||||||
|
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
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
[section SSL/TLS Certificate]
|
||||||
|
|
||||||
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
|
[heading Certificate Authority]
|
||||||
|
|
||||||
|
A Certificate Authority (CA) is a trusted entity that signs digital
|
||||||
|
certificates, enabling users to verify their authenticity. Rather than storing
|
||||||
|
every individual certificate for each server (which would be impractical due to
|
||||||
|
the sheer volume and frequent renewals), users can store a limited set of root
|
||||||
|
certificates to authenticate server certificates as needed.
|
||||||
|
|
||||||
|
Boost.Asio provides various methods for loading certificate authority
|
||||||
|
certificates:
|
||||||
|
|
||||||
|
* [@boost:/doc/html/boost_asio/reference/ssl__context/add_certificate_authority.html `net::ssl::context::add_certificate_authority`]
|
||||||
|
* [@boost:/doc/html/boost_asio/reference/ssl__context/add_verify_path.html `net::ssl::context::add_verify_path`]
|
||||||
|
* [@boost:/doc/html/boost_asio/reference/ssl__context/load_verify_file.html `net::ssl::context::load_verify_file`]
|
||||||
|
* [@boost:/doc/html/boost_asio/reference/ssl__context/set_default_verify_paths.html `net::ssl::context::set_default_verify_paths`]
|
||||||
|
|
||||||
|
It is important to set up peer verification so that the TLS/SSL handshake fails
|
||||||
|
if certificate verification is unsuccessful:
|
||||||
|
|
||||||
|
[snippet_core_6]
|
||||||
|
|
||||||
|
A client must also verify that the hostname or IP address in the certificate
|
||||||
|
matches the expected one. The
|
||||||
|
[@boost:/doc/html/boost_asio/reference/ssl__host_name_verification.html
|
||||||
|
`net::ssl::host_name_verification`] helper function object can perform this
|
||||||
|
verification according to the rules described in RFC 6125:
|
||||||
|
|
||||||
|
[snippet_core_7]
|
||||||
|
|
||||||
|
A server can also request and verify a client certificate to authenticate the
|
||||||
|
client:
|
||||||
|
|
||||||
|
[snippet_core_8]
|
||||||
|
|
||||||
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
|
[heading Server Certificate]
|
||||||
|
|
||||||
|
A Server Certificate is a digital certificate that confirms a server's identity
|
||||||
|
as the legitimate destination for a client. It contains a verifiable signature
|
||||||
|
that ensures it was issued by a trusted certificate authority (CA).
|
||||||
|
|
||||||
|
When a server certificate is issued by an intermediate certificate authority,
|
||||||
|
and the client lacks those intermediate certificates, the server should provide
|
||||||
|
all the relevant certificates to the client. This allows the client to verify
|
||||||
|
the final certificate in the chain against the root certificate.
|
||||||
|
|
||||||
|
The following Boost.Asio methods can be used for loading a certificate or a
|
||||||
|
certificate chain:
|
||||||
|
|
||||||
|
* [@boost:/doc/html/boost_asio/reference/ssl__context/use_certificate.html `net::ssl::context::use_certificate`]
|
||||||
|
* [@boost:/doc/html/boost_asio/reference/ssl__context/use_certificate_file.html `net::ssl::context::use_certificate_file`]
|
||||||
|
* [@boost:/doc/html/boost_asio/reference/ssl__context/use_certificate_chain.html `net::ssl::context::use_certificate_chain`]
|
||||||
|
* [@boost:/doc/html/boost_asio/reference/ssl__context/use_certificate_chain_file.html `net::ssl::context::use_certificate_chain_file`]
|
||||||
|
|
||||||
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
|
[heading Client Certificate]
|
||||||
|
|
||||||
|
A server can authenticate clients by requiring and verifying their certificates,
|
||||||
|
preventing access for those without a valid certificate and private key. The
|
||||||
|
server enforces this by modifying peer verification settings:
|
||||||
|
|
||||||
|
[snippet_core_8]
|
||||||
|
|
||||||
|
If used, the necessary CA certificates must be loaded into the server's SSL
|
||||||
|
context to enable verification of the client's certificate.
|
||||||
|
|
||||||
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
|
[heading Common Name and Subject Alternative Name]
|
||||||
|
|
||||||
|
The Subject Alternative Name (SAN) is an extension in X.509 certificates that
|
||||||
|
allows multiple domain names, subdomains, or IP addresses to be associated with
|
||||||
|
a single SSL/TLS certificate. Before that it was the Common Name field in the
|
||||||
|
certificate subject which could contain a single hostname.
|
||||||
|
|
||||||
|
[@https://datatracker.ietf.org/doc/html/rfc6125#appendix-B.2 RFC 6125]
|
||||||
|
recommends that if a certificate includes a SAN dNSName field, the client must
|
||||||
|
ignore the subject CN field. Some modern browsers, such as Google Chrome, check
|
||||||
|
only the SAN section in an SSL/TLS certificate and reject certificates that
|
||||||
|
contain only the CN field.
|
||||||
|
|
||||||
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
|
[heading Private Key]
|
||||||
|
|
||||||
|
The private key of a certificate is required during the SSL/TLS handshake to
|
||||||
|
prove that the certificate's provider is its rightful owner
|
||||||
|
|
||||||
|
The following Boost.Asio methods can be used for loading a private key:
|
||||||
|
|
||||||
|
* [@boost:/doc/html/boost_asio/reference/ssl__context/use_private_key.html `net::ssl::context::use_private_key`]
|
||||||
|
* [@boost:/doc/html/boost_asio/reference/ssl__context/use_private_key_file.html `net::ssl::context::use_private_key_file`]
|
||||||
|
* [@boost:/doc/html/boost_asio/reference/ssl__context/use_rsa_private_key.html `net::ssl::context::use_rsa_private_key`]
|
||||||
|
* [@boost:/doc/html/boost_asio/reference/ssl__context/use_rsa_private_key_file.html `net::ssl::context::use_rsa_private_key_file`]
|
||||||
|
|
||||||
|
If the private key is secured with a password, the
|
||||||
|
[@boost:/doc/html/boost_asio/reference/ssl__context/set_password_callback.html
|
||||||
|
net::ssl::context::set_password_callback] allows specifying a callable object to
|
||||||
|
retrieve the password.
|
||||||
|
|
||||||
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
|
[heading Self-Signed and Self-Issued Certificates]
|
||||||
|
|
||||||
|
A self-issued certificate is a certificate where the issuer and subject are the
|
||||||
|
same entity.
|
||||||
|
|
||||||
|
A self-signed certificate is a self-issued certificate in which the digital
|
||||||
|
signature can be verified using the public key within the certificate.
|
||||||
|
|
||||||
|
[warning
|
||||||
|
Installing an untrusted, self-issued, or self-signed CA certificate poses a
|
||||||
|
significant security risk, as there are no restrictions on the domains for
|
||||||
|
which it can issue certificates. This allows attackers to generate
|
||||||
|
fraudulent certificates for any public domain, enabling man-in-the-middle
|
||||||
|
attacks if they gain access to your network.
|
||||||
|
]
|
||||||
|
|
||||||
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
|
[heading Diffie-Hellman (DH) Parameters]
|
||||||
|
|
||||||
|
Diffie-Hellman (DH) key exchange is a cryptographic protocol that allows two
|
||||||
|
parties to securely establish a shared secret over an insecure communication
|
||||||
|
channel. The key exchange process involves both parties agreeing on a set of
|
||||||
|
parameters, known as Diffie-Hellman parameters, which include a large prime
|
||||||
|
number `p` and a generator `g`. Since generating these parameters is a
|
||||||
|
computationally expensive task, a user might prefer to provide a precomputed
|
||||||
|
value at startup.
|
||||||
|
|
||||||
|
The following Boost.Asio methods can be used for loading DH parameters:
|
||||||
|
|
||||||
|
* [@boost:/doc/html/boost_asio/reference/ssl__context/use_tmp_dh.html `net::ssl::context::use_tmp_dh`]
|
||||||
|
* [@boost:/doc/html/boost_asio/reference/ssl__context/use_tmp_dh_file.html `net::ssl::context::use_tmp_dh_file`]
|
||||||
|
|
||||||
|
If no DH parameter is provided, OpenSSL will refuse to perform any handshake
|
||||||
|
that uses DHE-based cipher suites but will still work with other cipher suites,
|
||||||
|
such as those based on ECDHE.
|
||||||
|
|
||||||
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
|
[heading A Self-Issued Certificate Example]
|
||||||
|
|
||||||
|
In the following example, we will generate a self-signed CA certificate and use
|
||||||
|
it to issue both server and client certificates.
|
||||||
|
|
||||||
|
* Generate a CA certificate:
|
||||||
|
|
||||||
|
```
|
||||||
|
openssl req -new -newkey rsa:4096 -keyout ca.key -x509 -out ca.crt -subj "/CN=localhost" -days 365
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
* Generate a Server CSR:
|
||||||
|
|
||||||
|
```
|
||||||
|
openssl req -new -newkey rsa:4096 -keyout server.key -out server.csr -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
* Sign the Server CSR using our CA:
|
||||||
|
|
||||||
|
```
|
||||||
|
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -copy_extensions copy -days 365 -out server.crt
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
* Generate a Client CSR:
|
||||||
|
|
||||||
|
```
|
||||||
|
openssl req -new -newkey rsa:4096 -keyout client.key -out client.csr -subj "/CN=client.1"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
* Sign the Client CSR using our CA:
|
||||||
|
|
||||||
|
```
|
||||||
|
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -days 365 -out client.crt
|
||||||
|
```
|
||||||
|
|
||||||
|
* Generate a DH parameters file:
|
||||||
|
|
||||||
|
```
|
||||||
|
openssl dhparam -out dh4096.pem 4096
|
||||||
|
```
|
||||||
|
|
||||||
|
Server example: [path_link example/doc/ssl/server.cpp server.cpp]
|
||||||
|
|
||||||
|
Note that the server is configured in such a way that it requests and verifies
|
||||||
|
the client certificate. You can disable this by commenting out the related line
|
||||||
|
in the example.
|
||||||
|
|
||||||
|
You can test the server using this cURL command:
|
||||||
|
|
||||||
|
```
|
||||||
|
curl https://localhost:8080 --cacert ca.crt --cert client.crt --key client.key
|
||||||
|
```
|
||||||
|
|
||||||
|
Also, you can use the client example: [path_link example/doc/ssl/client.cpp client.cpp]
|
||||||
|
|
||||||
|
[endsect]
|
@ -96,7 +96,8 @@ effect:
|
|||||||
[include 5_buffers.qbk]
|
[include 5_buffers.qbk]
|
||||||
[include 6_files.qbk]
|
[include 6_files.qbk]
|
||||||
[include 7_composed.qbk]
|
[include 7_composed.qbk]
|
||||||
[include 9_ssl-tls-shutdown.qbk]
|
[include 9_ssl_tls_certificate.qbk]
|
||||||
|
[include 10_ssl_tls_shutdown.qbk]
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
[section:config Configuration]
|
[section:config Configuration]
|
||||||
|
@ -13,4 +13,6 @@ add_subdirectory(advanced)
|
|||||||
add_subdirectory(http)
|
add_subdirectory(http)
|
||||||
add_subdirectory(websocket)
|
add_subdirectory(websocket)
|
||||||
|
|
||||||
|
add_subdirectory(doc)
|
||||||
|
|
||||||
add_subdirectory(echo-op)
|
add_subdirectory(echo-op)
|
||||||
|
@ -30,5 +30,7 @@ build-project advanced ;
|
|||||||
build-project http ;
|
build-project http ;
|
||||||
build-project websocket ;
|
build-project websocket ;
|
||||||
|
|
||||||
|
build-project doc ;
|
||||||
|
|
||||||
# legacy
|
# legacy
|
||||||
build-project echo-op ;
|
build-project echo-op ;
|
||||||
|
12
example/doc/CMakeLists.txt
Normal file
12
example/doc/CMakeLists.txt
Normal 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
10
example/doc/Jamfile
Normal 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 ;
|
44
example/doc/ssl/CMakeLists.txt
Normal file
44
example/doc/ssl/CMakeLists.txt
Normal 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
20
example/doc/ssl/Jamfile
Normal 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
137
example/doc/ssl/client.cpp
Normal 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
133
example/doc/ssl/server.cpp
Normal 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
|
@ -136,4 +136,29 @@ if(ec != net::ssl::error::stream_truncated)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ssl_tls()
|
||||||
|
{
|
||||||
|
net::io_context ioc;
|
||||||
|
net::ssl::context ctx(net::ssl::context::tlsv12);
|
||||||
|
net::ssl::stream<tcp_stream> stream(ioc, ctx);
|
||||||
|
|
||||||
|
//[snippet_core_6
|
||||||
|
// Verify the remote server's certificate
|
||||||
|
ctx.set_verify_mode(net::ssl::verify_peer);
|
||||||
|
//]
|
||||||
|
|
||||||
|
//[snippet_core_7
|
||||||
|
// Verify the remote server's Hostname or IP address
|
||||||
|
stream.set_verify_callback(
|
||||||
|
net::ssl::host_name_verification("host.name"));
|
||||||
|
//]
|
||||||
|
|
||||||
|
//[snippet_core_8
|
||||||
|
// Verify the remote client's certificate
|
||||||
|
ctx.set_verify_mode(
|
||||||
|
net::ssl::verify_peer |
|
||||||
|
net::ssl::verify_fail_if_no_peer_cert);
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
} // doc_core_snippets
|
} // doc_core_snippets
|
||||||
|
Reference in New Issue
Block a user