I am currently setting a RESTful API with boost asio.
Connecting from a client works fine via HTTP. But if I try to connect via HTTPS I get an error on the server side: "no shared cipher".
The error seems to come from the openssl implementation, but I have no idea what to make of it. My first guess would be that that no cypher algorithm is set, but I cannot see how this can be done in asio.
This is what I put in the code and where the error occurs:
auto acceptHandler = boost::bind(&self::onAccept, this, connection,
boost::asio::placeholders::error);
connection->async_accept(m_acceptor, acceptHandler);
m_sslContext.set_options(
context::default_workarounds | context::no_sslv2 | context::single_dh_use);
m_sslContext.use_certificate_file(filename, context::pem);
m_sslContext.use_private_key_file(filename, context::pem);
Anyone ever had this before or got it working?
I had the same problem and solved it this way.You have to generate a private key and a certificate file for your server. Here is the procedure to do so :
// Generate a new ssl private key :
$openssl genrsa -out privkey.pem 1024
// Create a certificate signing request using your new key
$openssl req -new -key privkey.pem -out certreq.csr
// Self-sign your CSR with your own private key:
$openssl x509 -req -days 3650 -in certreq.csr -signkey privkey.pem -out newcert.pem
// Install the signed certificate and private key for use by an ssl server
// This allows you to use a single file for certificate and private key
$( openssl x509 -in newcert.pem; cat privkey.pem ) > server.pem
// If you use a dh temp file :
$openssl dhparam -outform PEM -out dh512.pem 512
Then, copy the server.pem
and dh512.pem
files in your server execution directory.
If you use the tmp dh file you also have to add this line of code m_sslContext.use_tmp_dh_file("dh512.pem");
I got same error when feed unconfigured boost::asio::ssl::context
object to boost::asio::ssl::stream
object constructor, which then used as resulted socket of accepting:
server()
: m_acceptor(/*args*/)
, m_context(boost::asio::ssl::context::tlsv1_server)
, m_stream(m_io_service, m_context)
{
// `m_context` configuring, BUT `m_stream` is unaffected
m_acceptor.async_accept(m_stream.lowest_layer(), accept_result_handler);
}
// run somewhere `m_io_service.run()`, or other processor of `async` operations.
And after actual accept connection and process handshake on it the handshake handler receive boost::system::error_code
with value 336109761 and message no shared cipher
.
So first create and configure boost::asio::ssl::context
and then construct boost::asio::ssl::stream
with it:
typedef boost::asio::ip::tcp::socket socket_type;
typedef boost::asio::ssl::stream<socket_type> stream_type;
std::shared_ptr<stream_type> m_stream;
server()
: m_acceptor(/*args*/)
, m_context(boost::asio::ssl::context::tlsv1_server)
{
// `m_context` configuring
m_stream = std::make_shared<stream_type>(m_io_service, m_context);
m_acceptor.async_accept(m_stream->lowest_layer(), accept_result_handler);
}
And don't forget create new stream for each new connection, context can be the same.
As @asiocity points out, the essential part is to fully configure the ssl::context before initialising the ssl::stream.
It is hard to initialise them both in an initialiser list while still deferring the ssl::stream until the ssl::context is fully configured.
One way I use is to subclass the ssl::context and override the constructor so that it can be fully configured in the initializer list, before the ssl::stream is initialised.
e.g.
class server_context : public boost::asio::ssl::context {
public: server_context(boost::asio::ssl::context::method m, std::string certfile);
};
...
server_context::server_context(boost::asio::ssl::context::method m, std::string certfile)
: boost::asio::ssl::context(m)
{
use_private_key_file(certfile);
use_certificate_chain_file(certfile);
}
...
class myclass {
private:
server_context : ssl_context_;
boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> stream_;
public:
myclass(boost::asio::ip::tcp::socket&, std::string certfile);
...
myclass::myclass(std::string certfile)
: ssl_context_(boost::asio::ssl::context::sslv23, certfile),
stream_(socket, ssl_context_)
{
...
}
However that seems a lot of work to avoid a shared_ptr
I'm wishing/hoping for some constructor-mixin/template trick that would allow me to do this with just a custom initializer list rather than a subclass.