net_ssl: Use EVP API to create EVP_PKEYs when available
What changed, and why it matters
This commit updates Monero's SSL certificate generation code to use newer OpenSSL 3.0 APIs instead of older, deprecated ones. It is primarily a compatibility and cleanup change: it replaces low-level RSA key creation with the modern EVP_RSA_gen function when OpenSSL 3.0+ is available, and removes an unused elliptic-curve certificate function. There is no direct security vulnerability being fixed here, but keeping up with current APIs reduces future maintenance risk and avoids deprecation warnings.
Treat as a routine maintenance/refactoring patch. Reviewers should verify that the EVP_RSA_gen path is exercised in OpenSSL 3.0 builds and that the legacy fallback still works on OpenSSL 1.1.x, but no urgent security response is warranted.
Security signals we found
Migration from deprecated OpenSSL low-level key APIs to EVP API
Removal of unused EC certificate generation code
No explicit vulnerability or bug fix described in commit message
No change to certificate parameters (still 4096-bit RSA, same validity period, same self-signed SHA-256)
Evidence from the diff
The patch refactors contrib/epee/src/net_ssl.cpp to call EVP_RSA_gen(4096) on OpenSSL >= 3.0, while retaining the legacy RSA_new/RSA_generate_key_ex/EVP_PKEY_assign_RSA path for older OpenSSL. It removes create_ec_ssl_certificate and its header declaration because it was unused (guarded by USE_EXTRA_EC_CERT, which appears not enabled). The change also cleans up several OpenSSL resource-management helper types. The commit message frames this as a response to OpenSSL 3.0 deprecation of RSA_free, EC_KEY_free, RSA_new, RSA_generate_key_ex, EC_KEY_set_group, and EC_KEY_generate_key.
Changed components
contrib/epee/src/net_ssl.cppcontrib/epee/include/net/net_ssl.hInspect captured patch +51 / −168
diff --git a/contrib/epee/include/net/net_ssl.h b/contrib/epee/include/net/net_ssl.h
index 5fd14b6..807a306 100644
--- a/contrib/epee/include/net/net_ssl.h
+++ b/contrib/epee/include/net/net_ssl.h
@@ -39,6 +39,7 @@
#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
@@ -151,7 +152,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);
/**
diff --git a/contrib/epee/src/net_ssl.cpp b/contrib/epee/src/net_ssl.cpp
index 69cc1cc..cfbcd95 100644
--- a/contrib/epee/src/net_ssl.cpp
+++ b/contrib/epee/src/net_ssl.cpp
@@ -65,14 +65,7 @@ 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>;
+ static constexpr unsigned int rsa_bits = 4096;
struct openssl_pkey_free
{
@@ -83,55 +76,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(rsa_bits)
+ */
+ openssl_pkey gen_rsa_pkey_compat()
{
- void operator()(BIGNUM* ptr) const noexcept
- {
- BN_free(ptr);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ return openssl_pkey{EVP_RSA_gen(rsa_bits)};
+#else
+ openssl_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(), 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.get(), 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
}
}
@@ -145,126 +151,14 @@ namespace net_utils
bool create_rsa_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert)
{
MINFO("Generating SSL certificate");
- pkey = EVP_PKEY_new();
+ openssl_pkey pkey_deleter = gen_rsa_pkey_compat();
+ pkey = pkey_deleter.get();
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");
+ MERROR("Failed to create new private key with gen_rsa_pkey_compat()");
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();
- if (!pkey)
- {
- MERROR("Failed to create new private key");
- 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)
{
@@ -384,17 +278,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))
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.