Merge pull request #8344

70bbd25 core_rpc_server: new file: rpc_ssl.fingerprint (Jeffrey Ryan)
This commit is contained in:
luigi1111 2023-07-06 21:27:16 -05:00
commit 3a72c47445
No known key found for this signature in database
GPG key ID: F4ACA0183641E010
4 changed files with 129 additions and 25 deletions

View file

@ -151,6 +151,33 @@ namespace net_utils
bool create_ec_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert);
bool create_rsa_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert);
/**
* @brief Create a human-readable X509 certificate fingerprint
*
* Example output: "12:A3:92:19:87:D2:A2:A5:77:94:82:29:B9:5A:91:01:AB:5F:75:16:9A:BA:CD:3D:D3:69:3D:6A:87:DC:E8:0E"
*
* @param[in] cert The certificate which will be used to create the fingerprint
* @param[in] fdig The digest algorithm to use, defaults to SHA-256 b/c that is what ssl_options_t uses
* @return The human-readable fingerprint string
*
* @throw boost::system_error if there is an OpenSSL error
*/
std::string get_hr_ssl_fingerprint(const X509 *cert, const EVP_MD *fdig = EVP_sha256());
/**
* @brief Create a human-readable fingerprint from the contents of an X509 certificate
*
* Should be equivalent to the command `openssl x509 -in <cert file> -fingerprint -sha256 -noout`
* Example output: "12:A3:92:19:87:D2:A2:A5:77:94:82:29:B9:5A:91:01:AB:5F:75:16:9A:BA:CD:3D:D3:69:3D:6A:87:DC:E8:0E"
*
* @param[in] cert_path The path to an X509 certificate which will be used to create the fingerprint
* @param[in] fdig The digest algorithm to use, defaults to SHA-256 b/c that is what ssl_options_t uses
* @return The human-readable fingerprint string
*
* @throw boost::system_error if there is an OpenSSL error or file I/O error
*/
std::string get_hr_ssl_fingerprint_from_file(const std::string& cert_path, const EVP_MD *fdig = EVP_sha256());
//! Store private key for `ssl` at `base + ".key"` unencrypted and certificate for `ssl` at `base + ".crt"`.
boost::system::error_code store_ssl_keys(boost::asio::ssl::context& ssl, const boost::filesystem::path& base);
}

View file

@ -641,6 +641,56 @@ bool ssl_options_t::handshake(
return true;
}
std::string get_hr_ssl_fingerprint(const X509 *cert, const EVP_MD *fdig)
{
unsigned int j;
unsigned int n;
unsigned char md[EVP_MAX_MD_SIZE];
std::string fingerprint;
CHECK_AND_ASSERT_THROW_MES(cert && fdig, "Pointer args to get_hr_ssl_fingerprint cannot be null");
if (!X509_digest(cert, fdig, md, &n))
{
const unsigned long ssl_err_val = static_cast<int>(ERR_get_error());
const boost::system::error_code ssl_err_code = boost::asio::error::ssl_errors(static_cast<int>(ssl_err_val));
MERROR("Failed to create SSL fingerprint: " << ERR_reason_error_string(ssl_err_val));
throw boost::system::system_error(ssl_err_code, ERR_reason_error_string(ssl_err_val));
}
fingerprint.resize(n * 3 - 1);
char *out = &fingerprint[0];
for (j = 0; j < n; ++j)
{
snprintf(out, 3 + (j + 1 < n), "%02X%s", md[j], (j + 1 == n) ? "" : ":");
out += 3;
}
return fingerprint;
}
std::string get_hr_ssl_fingerprint_from_file(const std::string& cert_path, const EVP_MD *fdig) {
// Open file for reading
FILE* fp = fopen(cert_path.c_str(), "r");
if (!fp)
{
const boost::system::error_code err_code(errno, boost::system::system_category());
throw boost::system::system_error(err_code, "Failed to open certificate file '" + cert_path + "'");
}
std::unique_ptr<FILE, decltype(&fclose)> file(fp, &fclose);
// Extract certificate structure from file
X509* ssl_cert_handle = PEM_read_X509(file.get(), NULL, NULL, NULL);
if (!ssl_cert_handle) {
const unsigned long ssl_err_val = static_cast<int>(ERR_get_error());
const boost::system::error_code ssl_err_code = boost::asio::error::ssl_errors(static_cast<int>(ssl_err_val));
MERROR("OpenSSL error occurred while loading certificate at '" + cert_path + "'");
throw boost::system::system_error(ssl_err_code, ERR_reason_error_string(ssl_err_val));
}
std::unique_ptr<X509, decltype(&X509_free)> ssl_cert(ssl_cert_handle, &X509_free);
// Get the fingerprint from X509 structure
return get_hr_ssl_fingerprint(ssl_cert.get(), fdig);
}
bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s)
{
if (s == "enabled")
@ -705,6 +755,29 @@ boost::system::error_code store_ssl_keys(boost::asio::ssl::context& ssl, const b
return boost::asio::error::ssl_errors(ERR_get_error());
if (std::fclose(file.release()) != 0)
return {errno, boost::system::system_category()};
// write SHA-256 fingerprint file
const boost::filesystem::path fp_file{base.string() + ".fingerprint"};
file.reset(std::fopen(fp_file.string().c_str(), "w"));
if (!file)
return {errno, boost::system::system_category()};
const auto fp_perms = (boost::filesystem::owner_read | boost::filesystem::group_read | boost::filesystem::others_read);
boost::filesystem::permissions(fp_file, fp_perms, error);
if (error)
return error;
try
{
const std::string fingerprint = get_hr_ssl_fingerprint(ssl_cert);
if (fingerprint.length() != fwrite(fingerprint.c_str(), sizeof(char), fingerprint.length(), file.get()))
return {errno, boost::system::system_category()};
}
catch (const boost::system::system_error& fperr)
{
return fperr.code();
}
if (std::fclose(file.release()) != 0)
return {errno, boost::system::system_category()};
return error;
}