aboutsummaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
authorJeffrey Ryan <jeffreyryan@tutanota.com>2022-05-19 15:27:30 -0500
committerjeffro256 <jeffro256@tutanota.com>2023-07-02 19:38:51 -0500
commit70bbd2536b0eb005458f7dee419eef503ff9e0cb (patch)
treebd821c083bc992dbb457670cff6ac3d5a93b1947 /contrib
parentMerge pull request #8842 (diff)
downloadmonero-70bbd2536b0eb005458f7dee419eef503ff9e0cb.tar.xz
core_rpc_server: new file: rpc_ssl.fingerprint
Diffstat (limited to 'contrib')
-rw-r--r--contrib/epee/include/net/net_ssl.h27
-rw-r--r--contrib/epee/src/net_ssl.cpp73
2 files changed, 100 insertions, 0 deletions
diff --git a/contrib/epee/include/net/net_ssl.h b/contrib/epee/include/net/net_ssl.h
index c79a3acc1..c6ef925ba 100644
--- a/contrib/epee/include/net/net_ssl.h
+++ b/contrib/epee/include/net/net_ssl.h
@@ -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);
}
diff --git a/contrib/epee/src/net_ssl.cpp b/contrib/epee/src/net_ssl.cpp
index ff9c48c34..3822eb16d 100644
--- a/contrib/epee/src/net_ssl.cpp
+++ b/contrib/epee/src/net_ssl.cpp
@@ -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;
}