Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion contrib/epee/include/net/net_ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@
#include <boost/asio/ssl.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/system/error_code.hpp>
#include <openssl/evp.h>

#define SSL_FINGERPRINT_SIZE 32
#define SSL_RSA_BITS 4096

namespace epee
{
Expand Down Expand Up @@ -143,7 +145,6 @@ namespace net_utils
bool is_ssl(const unsigned char *data, size_t len);
bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s);

bool create_ec_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert);
bool create_rsa_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert);

//! Store private key for `ssl` at `base + ".key"` unencrypted and certificate for `ssl` at `base + ".crt"`.
Expand Down
214 changes: 48 additions & 166 deletions contrib/epee/src/net_ssl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,6 @@ static void add_windows_root_certs(SSL_CTX *ctx) noexcept;

namespace
{
struct openssl_bio_free
{
void operator()(BIO* ptr) const noexcept
{
BIO_free(ptr);
}
};
using openssl_bio = std::unique_ptr<BIO, openssl_bio_free>;

struct openssl_pkey_free
{
void operator()(EVP_PKEY* ptr) const noexcept
Expand All @@ -70,55 +61,68 @@ namespace
};
using openssl_pkey = std::unique_ptr<EVP_PKEY, openssl_pkey_free>;

struct openssl_rsa_free
boost::system::error_code load_ca_file(boost::asio::ssl::context& ctx, const std::string& path)
{
void operator()(RSA* ptr) const noexcept
SSL_CTX* const ssl_ctx = ctx.native_handle(); // could be moved from context
if (ssl_ctx == nullptr)
return {boost::asio::error::invalid_argument};

if (!SSL_CTX_load_verify_locations(ssl_ctx, path.c_str(), nullptr))
{
RSA_free(ptr);
return boost::system::error_code{
int(::ERR_get_error()), boost::asio::error::get_ssl_category()
};
}
};
using openssl_rsa = std::unique_ptr<RSA, openssl_rsa_free>;
return boost::system::error_code{};
}

struct openssl_bignum_free
/**
* @brief Generate a new RSA private key. For OpenSSL >= 3.0, same as EVP_RSA_gen(SSL_RSA_BITS)
*/
EVP_PKEY* gen_rsa_pkey_compat()
{
void operator()(BIGNUM* ptr) const noexcept
{
BN_free(ptr);
#if OPENSSL_VERSION_MAJOR >= 3
return EVP_RSA_gen(SSL_RSA_BITS);
#else
EVP_PKEY* pkey = EVP_PKEY_new();
if (!pkey) {
MERROR("Error allocating EVP private key");
return NULL;
}
};
using openssl_bignum = std::unique_ptr<BIGNUM, openssl_bignum_free>;

struct openssl_ec_key_free
{
void operator()(EC_KEY* ptr) const noexcept
std::unique_ptr<RSA, decltype(&RSA_free)> rsa(RSA_new(), &RSA_free);
if (!rsa)
{
EC_KEY_free(ptr);
MERROR("Error allocating RSA private key");
return NULL;
}
};
using openssl_ec_key = std::unique_ptr<EC_KEY, openssl_ec_key_free>;

struct openssl_group_free
{
void operator()(EC_GROUP* ptr) const noexcept
std::unique_ptr<BIGNUM, decltype(&BN_free)> exponent(BN_new(), &BN_free);
if (!exponent)
{
EC_GROUP_free(ptr);
MERROR("Error allocating exponent");
return NULL;
}
};
using openssl_group = std::unique_ptr<EC_GROUP, openssl_group_free>;

boost::system::error_code load_ca_file(boost::asio::ssl::context& ctx, const std::string& path)
{
SSL_CTX* const ssl_ctx = ctx.native_handle(); // could be moved from context
if (ssl_ctx == nullptr)
return {boost::asio::error::invalid_argument};
BN_set_word(exponent.get(), RSA_F4);

if (!SSL_CTX_load_verify_locations(ssl_ctx, path.c_str(), nullptr))
if (RSA_generate_key_ex(rsa.get(), SSL_RSA_BITS, exponent.get(), nullptr) != 1)
{
return boost::system::error_code{
int(::ERR_get_error()), boost::asio::error::get_ssl_category()
};
MERROR("Error generating RSA private key");
return NULL;
}
return boost::system::error_code{};

if (EVP_PKEY_assign_RSA(pkey, rsa.get()) <= 0)
{
MERROR("Error assigning RSA private key");
return NULL;
}

// the RSA key is now managed by the EVP_PKEY structure
(void)rsa.release();

return pkey;
#endif
}
}

Expand All @@ -132,125 +136,14 @@ namespace net_utils
bool create_rsa_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert)
{
MINFO("Generating SSL certificate");
pkey = EVP_PKEY_new();
if (!pkey)
{
MERROR("Failed to create new private key");
return false;
}

openssl_pkey pkey_deleter{pkey};
openssl_rsa rsa{RSA_new()};
if (!rsa)
{
MERROR("Error allocating RSA private key");
return false;
}

openssl_bignum exponent{BN_new()};
if (!exponent)
{
MERROR("Error allocating exponent");
return false;
}

BN_set_word(exponent.get(), RSA_F4);

if (RSA_generate_key_ex(rsa.get(), 4096, exponent.get(), nullptr) != 1)
{
MERROR("Error generating RSA private key");
return false;
}

if (EVP_PKEY_assign_RSA(pkey, rsa.get()) <= 0)
{
MERROR("Error assigning RSA private key");
return false;
}

// the RSA key is now managed by the EVP_PKEY structure
(void)rsa.release();

cert = X509_new();
if (!cert)
{
MERROR("Failed to create new X509 certificate");
return false;
}
ASN1_INTEGER_set(X509_get_serialNumber(cert), 1);
X509_gmtime_adj(X509_get_notBefore(cert), 0);
X509_gmtime_adj(X509_get_notAfter(cert), 3600 * 24 * 182); // half a year
if (!X509_set_pubkey(cert, pkey))
{
MERROR("Error setting pubkey on certificate");
X509_free(cert);
return false;
}
X509_NAME *name = X509_get_subject_name(cert);
X509_set_issuer_name(cert, name);

if (X509_sign(cert, pkey, EVP_sha256()) == 0)
{
MERROR("Error signing certificate");
X509_free(cert);
return false;
}
(void)pkey_deleter.release();
return true;
}

bool create_ec_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert, int type)
{
MINFO("Generating SSL certificate");
pkey = EVP_PKEY_new();
pkey = gen_rsa_pkey_compat();
if (!pkey)
{
MERROR("Failed to create new private key");
MERROR("Failed to create new private key with gen_rsa_pkey_compat()");
return false;
}

openssl_pkey pkey_deleter{pkey};
openssl_ec_key ec_key{EC_KEY_new()};
if (!ec_key)
{
MERROR("Error allocating EC private key");
return false;
}

EC_GROUP *group = EC_GROUP_new_by_curve_name(type);
if (!group)
{
MERROR("Error getting EC group " << type);
return false;
}
openssl_group group_deleter{group};

EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED);

if (!EC_GROUP_check(group, NULL))
{
MERROR("Group failed check: " << ERR_reason_error_string(ERR_get_error()));
return false;
}
if (EC_KEY_set_group(ec_key.get(), group) != 1)
{
MERROR("Error setting EC group");
return false;
}
if (EC_KEY_generate_key(ec_key.get()) != 1)
{
MERROR("Error generating EC private key");
return false;
}
if (EVP_PKEY_assign_EC_KEY(pkey, ec_key.get()) <= 0)
{
MERROR("Error assigning EC private key");
return false;
}

// the key is now managed by the EVP_PKEY structure
(void)ec_key.release();

cert = X509_new();
if (!cert)
Expand Down Expand Up @@ -362,17 +255,6 @@ boost::asio::ssl::context ssl_options_t::create_context() const
X509 *cert;
bool ok = false;

#ifdef USE_EXTRA_EC_CERT
CHECK_AND_ASSERT_THROW_MES(create_ec_ssl_certificate(pkey, cert, NID_secp256k1), "Failed to create certificate");
CHECK_AND_ASSERT_THROW_MES(SSL_CTX_use_certificate(ctx, cert), "Failed to use generated certificate");
if (!SSL_CTX_use_PrivateKey(ctx, pkey))
MERROR("Failed to use generated EC private key for " << NID_secp256k1);
else
ok = true;
X509_free(cert);
EVP_PKEY_free(pkey);
#endif

CHECK_AND_ASSERT_THROW_MES(create_rsa_ssl_certificate(pkey, cert), "Failed to create certificate");
CHECK_AND_ASSERT_THROW_MES(SSL_CTX_use_certificate(ctx, cert), "Failed to use generated certificate");
if (!SSL_CTX_use_PrivateKey(ctx, pkey))
Expand Down