Skip to content

Commit

Permalink
http: add client certificate & fix ca
Browse files Browse the repository at this point in the history
  • Loading branch information
binarytrails committed Sep 30, 2019
1 parent 55c56a5 commit c11aa26
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 53 deletions.
3 changes: 2 additions & 1 deletion include/opendht/dht_proxy_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class OPENDHT_PUBLIC DhtProxyClient final : public DhtInterface {
DhtProxyClient();

explicit DhtProxyClient(
std::shared_ptr<dht::crypto::Certificate> certificate,
std::shared_ptr<dht::crypto::Certificate> ca_cert, std::shared_ptr<dht::crypto::Certificate> cert,
std::function<void()> loopSignal, const std::string& serverHost,
const std::string& pushClientId = "", std::shared_ptr<dht::Logger> logger = {});

Expand Down Expand Up @@ -328,6 +328,7 @@ class OPENDHT_PUBLIC DhtProxyClient final : public DhtInterface {
void cancelAllOperations();

std::shared_ptr<dht::crypto::Certificate> serverCertificate_;
std::shared_ptr<dht::crypto::Certificate> clientCertificate_;
std::pair<std::string, std::string> serverHostService_;
std::string pushClientId_;

Expand Down
1 change: 1 addition & 0 deletions include/opendht/dhtrunner.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class OPENDHT_PUBLIC DhtRunner {
std::string push_token {};
bool peer_discovery {false};
bool peer_publish {false};
std::shared_ptr<dht::crypto::Certificate> client_ca;
std::shared_ptr<dht::crypto::Certificate> client_cert;
};

Expand Down
15 changes: 8 additions & 7 deletions include/opendht/http.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,15 @@ class OPENDHT_PUBLIC Connection
{
public:
Connection(asio::io_context& ctx, const bool ssl = true, std::shared_ptr<dht::Logger> l = {});
Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certificate> certificate,
std::shared_ptr<dht::Logger> l = {});
Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certificate> ca_cert,
std::shared_ptr<dht::crypto::Certificate> cert = {}, std::shared_ptr<dht::Logger> l = {});
~Connection();

unsigned int id();
bool is_open();
bool is_v6();
bool is_ssl();

void set_endpoint(const asio::ip::tcp::endpoint& endpoint,
const asio::ssl::verify_mode verify_mode = asio::ssl::verify_none);
void set_ssl_verification(const asio::ip::tcp::endpoint& endpoint, const asio::ssl::verify_mode verify_mode);

asio::streambuf& input();
asio::streambuf& data();
Expand All @@ -120,7 +118,8 @@ class OPENDHT_PUBLIC Connection
std::unique_ptr<socket_t> socket_;
std::shared_ptr<asio::ssl::context> ssl_ctx_;
std::unique_ptr<ssl_socket_t> ssl_socket_;
std::unique_ptr<asio::const_buffer> certificate_;
std::unique_ptr<asio::const_buffer> cacert_;
std::unique_ptr<asio::const_buffer> cert_;

asio::ip::tcp::endpoint endpoint_;

Expand Down Expand Up @@ -232,6 +231,7 @@ class OPENDHT_PUBLIC Request
return request_;
}

void set_ca_certificate(std::shared_ptr<dht::crypto::Certificate> certificate);
void set_certificate(std::shared_ptr<dht::crypto::Certificate> certificate);
void set_logger(std::shared_ptr<dht::Logger> logger);

Expand Down Expand Up @@ -311,7 +311,8 @@ class OPENDHT_PUBLIC Request
std::unique_ptr<Callbacks> cbs_;
State state_;

std::shared_ptr<dht::crypto::Certificate> certificate_;
std::shared_ptr<dht::crypto::Certificate> ca_cert_;
std::shared_ptr<dht::crypto::Certificate> cert_;
std::string service_;
std::string host_;

Expand Down
37 changes: 25 additions & 12 deletions src/dht_proxy_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,25 @@ struct DhtProxyClient::ProxySearch {
DhtProxyClient::DhtProxyClient() {}

DhtProxyClient::DhtProxyClient(
std::shared_ptr<dht::crypto::Certificate> certificate,
std::shared_ptr<dht::crypto::Certificate> ca_cert, std::shared_ptr<dht::crypto::Certificate> cert,
std::function<void()> signal, const std::string& serverHost,
const std::string& pushClientId, std::shared_ptr<dht::Logger> logger)
:
serverCertificate_(certificate),
serverCertificate_(ca_cert), clientCertificate_(cert),
pushClientId_(pushClientId), loopSignal_(signal), logger_(logger)
{
// build http client
serverHostService_ = splitPort(serverHost);
serverHostService_.second = serverHostService_.second.empty() ? "80" :
serverHostService_.second;
if (serverCertificate_ and logger_)
logger_->d("[proxy:client] using server certificate for ssl:\n%s",
serverCertificate_->toString(false/*chain*/).c_str());
if (logger_){
if (serverCertificate_)
logger_->d("[proxy:client] using ca certificate for ssl:\n%s",
serverCertificate_->toString(false/*chain*/).c_str());
if (clientCertificate_)
logger_->d("[proxy:client] using client certificate for ssl:\n%s",
clientCertificate_->toString(false/*chain*/).c_str());
}
// resolve once
resolver_ = std::make_shared<http::Resolver>(httpContext_, serverHost, logger_);
// run http client
Expand Down Expand Up @@ -346,8 +351,9 @@ DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb, Va
}
});
if (serverCertificate_)
request->set_certificate(serverCertificate_);
request->set_certificate(serverCertificate_);
request->set_ca_certificate(serverCertificate_);
if (clientCertificate_)
request->set_certificate(clientCertificate_);
request->send();
requests_[reqid] = request;
}
Expand Down Expand Up @@ -482,8 +488,9 @@ DhtProxyClient::doPut(const InfoHash& key, Sp<Value> val, DoneCallback cb, time_
}
});
if (serverCertificate_)
request->set_certificate(serverCertificate_);
request->set_certificate(serverCertificate_);
request->set_ca_certificate(serverCertificate_);
if (clientCertificate_)
request->set_certificate(clientCertificate_);
request->send();
requests_[reqid] = request;
}
Expand Down Expand Up @@ -679,7 +686,9 @@ DhtProxyClient::queryProxyInfo(std::shared_ptr<InfoState> infoState, const sa_fa
return;

if (serverCertificate_)
request->set_certificate(serverCertificate_);
request->set_ca_certificate(serverCertificate_);
if (clientCertificate_)
request->set_certificate(clientCertificate_);
request->send();
requests_[reqid] = request;
}
Expand Down Expand Up @@ -958,7 +967,9 @@ DhtProxyClient::handleExpireListener(const asio::error_code &ec, const InfoHash&
}
});
if (serverCertificate_)
request->set_certificate(serverCertificate_);
request->set_ca_certificate(serverCertificate_);
if (clientCertificate_)
request->set_certificate(clientCertificate_);
request->send();
requests_[reqid] = request;
}
Expand Down Expand Up @@ -1065,7 +1076,9 @@ DhtProxyClient::sendListen(const restinio::http_request_header_t header,
}
});
if (serverCertificate_)
request->set_certificate(serverCertificate_);
request->set_ca_certificate(serverCertificate_);
if (clientCertificate_)
request->set_certificate(clientCertificate_);
request->send();
requests_[reqid] = request;
}
Expand Down
1 change: 1 addition & 0 deletions src/dhtrunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,7 @@ DhtRunner::enableProxy(bool proxify)
// Init the proxy client
auto dht_via_proxy = std::unique_ptr<DhtInterface>(
new DhtProxyClient(
config_.client_ca,
config_.client_cert,
[this]{
if (config_.threaded) {
Expand Down
73 changes: 41 additions & 32 deletions src/http.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,23 +99,33 @@ Connection::Connection(asio::io_context& ctx, const bool ssl, std::shared_ptr<dh
}
}

Connection::Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certificate> certificate,
std::shared_ptr<dht::Logger> l)
Connection::Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certificate> cacert,
std::shared_ptr<dht::crypto::Certificate> cert, std::shared_ptr<dht::Logger> l)
: id_(Connection::ids_++), ctx_(ctx), logger_(l)
{
ssl_ctx_ = std::make_shared<asio::ssl::context>(asio::ssl::context::sslv23);
ssl_ctx_->set_default_verify_paths();

asio::error_code ec;
auto cert = certificate->toString(false/*chain*/);
certificate_ = std::make_unique<asio::const_buffer>(static_cast<const void*>(cert.data()),
(std::size_t) cert.size());
ssl_ctx_->use_certificate(*certificate_, asio::ssl::context::file_format::pem, ec);
if (ec)
throw std::runtime_error("Error setting certificate: " + ec.message());
else if (logger_)
logger_->d("[http:client] [connection:%i] start https session with %s", id_, certificate->getUID().c_str());

if (cacert){
auto ca = cacert->toString(false/*chain*/);
cacert_ = std::make_unique<asio::const_buffer>(static_cast<const void*>(ca.data()),
(std::size_t) ca.size());
ssl_ctx_->add_certificate_authority(*cacert_, ec);
if (ec)
throw std::runtime_error("Error adding certificate authority: " + ec.message());
else if (logger_)
logger_->d("[http:client] [connection:%i] certficate authority %s", id_, cacert->getUID().c_str());
}
if (cert){
auto c = cacert->toString(false/*chain*/);
cert_ = std::make_unique<asio::const_buffer>(static_cast<const void*>(c.data()),
(std::size_t) c.size());
ssl_ctx_->use_certificate(*cert_, asio::ssl::context::file_format::pem, ec);
if (ec)
throw std::runtime_error("Error adding client certificate: " + ec.message());
else if (logger_)
logger_->d("[http:client] [connection:%i] client certificate %s", id_, cert->getUID().c_str());
}
ssl_ctx_->set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert);
ssl_socket_ = std::make_unique<ssl_socket_t>(ctx_, ssl_ctx_);
}
Expand Down Expand Up @@ -154,33 +164,28 @@ Connection::is_open()
return socket_->is_open();
}

bool
Connection::is_v6()
{
return endpoint_.address().is_v6();
}

bool
Connection::is_ssl()
{
return ssl_ctx_ ? true : false;
}

void
Connection::set_endpoint(const asio::ip::tcp::endpoint& endpoint, const asio::ssl::verify_mode verify_mode)
Connection::set_ssl_verification(const asio::ip::tcp::endpoint& endpoint, const asio::ssl::verify_mode verify_mode)
{
endpoint_ = endpoint;
if (ssl_ctx_ and verify_mode != asio::ssl::verify_none){
auto hostname = endpoint_.address().to_string();
auto hostname = endpoint.address().to_string();
ssl_socket_->asio_ssl_stream().set_verify_mode(verify_mode);
ssl_socket_->asio_ssl_stream().set_verify_callback(
[this, hostname](bool preverified, asio::ssl::verify_context& ctx) -> bool {
if (preverified)
return preverified;
// starts from CA and goes down the presented chain
auto verifier = asio::ssl::rfc2818_verification(hostname);
bool verified = verifier(preverified, ctx);
auto verify_ec = X509_STORE_CTX_get_error(ctx.native_handle());
if (verify_ec == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT /*18*/
|| verify_ec == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN /*19*/)
verified = true;
if (verified != 0 /*X509_V_OK*/ and logger_)
logger_->e("[http::connection:%i] ssl verification error=%i", id_, verify_ec);
return verified;
}
);
Expand Down Expand Up @@ -493,10 +498,16 @@ Request::get_connection() const
return conn_;
}

void
Request::set_ca_certificate(std::shared_ptr<dht::crypto::Certificate> certificate)
{
ca_cert_ = certificate;
}

void
Request::set_certificate(std::shared_ptr<dht::crypto::Certificate> certificate)
{
certificate_ = certificate;
cert_ = certificate;
}

void
Expand Down Expand Up @@ -728,8 +739,8 @@ Request::connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, HandlerCb cb)
logger_->d("[http:client] [request:%i] connect begin: %s", id_, eps.c_str());
}
if (get_url().protocol == "https"){
if (certificate_)
conn_ = std::make_shared<Connection>(ctx_, certificate_, logger_);
if (ca_cert_)
conn_ = std::make_shared<Connection>(ctx_, ca_cert_, cert_, logger_);
else
conn_ = std::make_shared<Connection>(ctx_, true/*ssl*/, logger_);
}
Expand All @@ -750,9 +761,9 @@ Request::connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, HandlerCb cb)
logger_->d("[http:client] [request:%i] connect success", id_);

if (get_url().protocol == "https"){
if (certificate_)
conn_->set_endpoint(endpoint, asio::ssl::verify_peer
| asio::ssl::verify_fail_if_no_peer_cert);
if (ca_cert_)
conn_->set_ssl_verification(endpoint, asio::ssl::verify_peer
| asio::ssl::verify_fail_if_no_peer_cert);

if (conn_ and conn_->is_open() and conn_->is_ssl()){
conn_->async_handshake([this, cb](const asio::error_code& ec){
Expand All @@ -770,8 +781,6 @@ Request::connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, HandlerCb cb)
cb(asio::error::operation_aborted);
return;
}
else
conn_->set_endpoint(endpoint, asio::ssl::verify_none);
}
if (cb)
cb(ec);
Expand Down
3 changes: 2 additions & 1 deletion tests/dhtproxytester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ DhtProxyTester::setUp() {
/*https*/serverIdentity,
nodeProxy, 8080, /*pushServer*/"127.0.0.1:8090", logger));

clientConfig.client_cert = serverIdentity.second;
clientConfig.client_ca = serverCAIdentity.second;
clientConfig.client_cert = dht::crypto::generateIdentity("Client Certificate").second;
clientConfig.dht_config.node_config.maintain_storage = false;
clientConfig.threaded = true;
clientConfig.push_node_id = "dhtnode";
Expand Down

0 comments on commit c11aa26

Please sign in to comment.