From e6dbf7472156e5ac5017bef36f6d31d9b65d7e1e Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 6 Mar 2024 19:08:28 +0000 Subject: [PATCH] Tidy up to use common functions --- Development/nmos/est_behaviour.cpp | 31 +-- Development/nmos/est_utils.cpp | 314 ++++++----------------------- Development/nmos/est_utils.h | 21 +- Development/ssl/ssl_utils.cpp | 9 +- Development/ssl/ssl_utils.h | 6 +- 5 files changed, 91 insertions(+), 290 deletions(-) diff --git a/Development/nmos/est_behaviour.cpp b/Development/nmos/est_behaviour.cpp index 91e67558e..74faae086 100644 --- a/Development/nmos/est_behaviour.cpp +++ b/Development/nmos/est_behaviour.cpp @@ -20,6 +20,7 @@ #include "nmos/random.h" #include "nmos/slog.h" #include "nmos/thread_utils.h" // for wait_until, reverse_lock_guard +#include "ssl/ssl_utils.h" namespace nmos { @@ -686,13 +687,13 @@ namespace nmos pplx::task do_cacerts_renewal_monitor(nmos::model& model, est_shared_state& est_state, slog::base_gate& gate, const pplx::cancellation_token& token) { // split cacerts chain to list - auto ca_certs = split_certificate_chain(utility::us2s(est_state.cacerts)); + auto ca_certs = ssl::experimental::split_certificate_chain(utility::us2s(est_state.cacerts)); // find the nearest to expiry time from the list of CA certs auto expiry = std::chrono::seconds{ -1 }; for (auto& cert : ca_certs) { - auto tmp = std::chrono::seconds(certificate_expiry_from_now(cert, 0.8)); + auto tmp = std::chrono::seconds((int)ssl::experimental::certificate_expiry_from_now(cert, 0.8)); if (std::chrono::seconds(-1) == expiry || tmp < expiry) { expiry = tmp; @@ -742,7 +743,7 @@ namespace nmos // "Renewal of the TLS Certificate SHOULD be attempted no sooner than 50% of the certificate's expiry time or before the 'Not Before' date on the certificate. // It is RECOMMENDED that certificate renewal is performed after 80% of the expiry time." // see https://specs.amwa.tv/bcp-003-03/releases/v1.0.0/docs/1.0._Certificate_Provisioning.html#certificate-renewal - cert_state.delay = std::chrono::seconds(certificate_expiry_from_now(utility::us2s(cert_state.cert), 0.8)); + cert_state.delay = std::chrono::seconds((int)ssl::experimental::certificate_expiry_from_now(utility::us2s(cert_state.cert), 0.8)); // get Certificate Revocation List URLs from certificate with_write_lock(cert_state.mutex, [&cert_state] @@ -819,7 +820,7 @@ namespace nmos // "Renewal of the TLS Certificate SHOULD be attempted no sooner than 50% of the certificate's expiry time or before the 'Not Before' date on the certificate. // It is RECOMMENDED that certificate renewal is performed after 80% of the expiry time." // see https://specs.amwa.tv/bcp-003-03/releases/v1.0.0/docs/1.0._Certificate_Provisioning.html#certificate-renewal - cert_state.delay = std::chrono::seconds(certificate_expiry_from_now(utility::us2s(result.second), 0.8)); + cert_state.delay = std::chrono::seconds((int)ssl::experimental::certificate_expiry_from_now(utility::us2s(result.second), 0.8)); with_write_lock(cert_state.mutex, [&cert_state] { @@ -1071,20 +1072,20 @@ namespace nmos if (!cacerts.empty()) { // split the cacerts chain to list - auto ca_certs = split_certificate_chain(utility::us2s(cacerts)); + auto ca_certs = ssl::experimental::split_certificate_chain(utility::us2s(cacerts)); std::string issuer_name; for (auto& cert : ca_certs) { // verify certificate's NotBefore, Not After, Common Name, Subject Alternative Name and chain of trust - const auto cert_info = cert_information(cert); + const auto cert_info = ssl::experimental::get_certificate_info(cert); // verify the chain against the certificate issuer name and the issuer certificate common name if (!issuer_name.empty()) { - if (boost::to_upper_copy(issuer_name) != boost::to_upper_copy(cert_info.common_name)) { throw est_exception("invalid CA chain of trust"); } + if (boost::to_upper_copy(issuer_name) != boost::to_upper_copy(cert_info.subject_common_name)) { throw est_exception("invalid CA chain of trust"); } } - issuer_name = cert_info.issuer_name; + issuer_name = cert_info.issuer_common_name; // Not Before const auto now = time(NULL); @@ -1150,7 +1151,7 @@ namespace nmos if (state.receive_ca_certificate) { // extract the Root CA from chain, Root CA is always presented in the end of the CA chain - const auto ca_certs = split_certificate_chain(utility::us2s(cacerts)); + const auto ca_certs = ssl::experimental::split_certificate_chain(utility::us2s(cacerts)); state.receive_ca_certificate(utility::s2us(ca_certs.back())); } @@ -1229,7 +1230,7 @@ namespace nmos if (!certificate.empty()) { // verify certificate's NotBefore, Not After, Common Name, Subject Alternative Name and chain of trust - const auto cert_info = nmos::experimental::details::cert_information(utility::us2s(certificate)); + const auto cert_info = ssl::experimental::get_certificate_info(utility::us2s(certificate)); // Not Before const auto now = time(NULL); @@ -1239,15 +1240,15 @@ namespace nmos if (cert_info.not_after < now) { throw est_exception("certificate has expired"); } // Common Name - if (boost::to_upper_copy(utility::us2s(FQDN)) != boost::to_upper_copy(cert_info.common_name)) { throw est_exception("invalid Common Name"); } + if (boost::to_upper_copy(utility::us2s(FQDN)) != boost::to_upper_copy(cert_info.subject_common_name)) { throw est_exception("invalid Common Name"); } // Subject Alternative Name - auto found_san = std::find_if(cert_info.sans.begin(), cert_info.sans.end(), [&FQDN](const std::string& san) { return boost::to_upper_copy(utility::us2s(FQDN)) == boost::to_upper_copy(san); }); - if (cert_info.sans.end() == found_san) { throw est_exception("invalid Subject Alternative Name"); } + auto found_san = std::find_if(cert_info.subject_alternative_names.begin(), cert_info.subject_alternative_names.end(), [&FQDN](const std::string& san) { return boost::to_upper_copy(utility::us2s(FQDN)) == boost::to_upper_copy(san); }); + if (cert_info.subject_alternative_names.end() == found_san) { throw est_exception("invalid Subject Alternative Name"); } // chain of trust - const auto cacerts_info = nmos::experimental::details::cert_information(utility::us2s(cacerts)); - if (cacerts_info.common_name != cert_info.issuer_name) { throw est_exception("invalid chain of trust"); } + const auto cacerts_info = ssl::experimental::get_certificate_info(utility::us2s(cacerts)); + if (cacerts_info.subject_common_name != cert_info.issuer_common_name) { throw est_exception("invalid chain of trust"); } } else { diff --git a/Development/nmos/est_utils.cpp b/Development/nmos/est_utils.cpp index 15cadf64a..7d034064c 100644 --- a/Development/nmos/est_utils.cpp +++ b/Development/nmos/est_utils.cpp @@ -10,6 +10,7 @@ #include #include #include // for X509V3_EXT_conf_nid +#include "ssl/ssl_utils.h" namespace nmos { @@ -17,221 +18,10 @@ namespace nmos { namespace details { - typedef std::unique_ptr BIO_ptr; typedef std::unique_ptr BIGNUM_ptr; typedef std::unique_ptr EVP_PKEY_ptr; typedef std::unique_ptr X509_REQ_ptr; - typedef std::unique_ptr X509_ptr; - typedef std::unique_ptr ASN1_TIME_ptr; typedef std::unique_ptr EVP_PKEY_CTX_ptr; - typedef std::unique_ptr GENERAL_NAMES_ptr; - - // get last openssl error - std::string last_openssl_error() - { - char buffer[1024] = {0}; - ERR_error_string_n(ERR_get_error(), buffer, sizeof(buffer)); - return buffer; - } - - // get common name from subject - std::string common_name(X509* x509) - { - auto subject_name = X509_get_subject_name(x509); - if (!subject_name) - { - throw est_exception("failed to get subject: X509_get_subject_name failure: " + last_openssl_error()); - } - auto name = X509_NAME_oneline(subject_name, NULL, 0); - std::string subject(name); - OPENSSL_free(name); - - // exmaple subject format - // e.g. subject=/DC=AMWA Workshop/CN=GBDEVWIND-8GGX.workshop.nmos.tv - const std::string common_name_prefix{ "CN=" }; - std::vector tokens; - boost::split(tokens, subject, boost::is_any_of("/")); - auto found_common_name_token = std::find_if(tokens.begin(), tokens.end(), [&common_name_prefix](const std::string& token) - { - return std::string::npos != token.find(common_name_prefix); - }); - if (tokens.end() != found_common_name_token) - { - return found_common_name_token->substr(common_name_prefix.length()); - } - return ""; - } - - // get issuer name from issuer - std::string issuer_name(X509* x509) - { - auto issuer_name = X509_get_issuer_name(x509); - if (!issuer_name) - { - throw est_exception("failed to get issuer: X509_get_issuer_name failure: " + last_openssl_error()); - } - auto name = X509_NAME_oneline(issuer_name, NULL, 0); - std::string issuer(name); - OPENSSL_free(name); - - // e.g. issuer=/C=GB/ST=England/O=NMOS Testing Ltd/CN=ica.workshop.nmos.tv - const std::string common_name_prefix{ "CN=" }; - std::vector tokens; - boost::split(tokens, issuer, boost::is_any_of("/")); - auto found_common_name_token = std::find_if(tokens.begin(), tokens.end(), [&common_name_prefix](const std::string& token) - { - return std::string::npos != token.find(common_name_prefix); - }); - if (tokens.end() != found_common_name_token) - { - return found_common_name_token->substr(common_name_prefix.length()); - } - return ""; - } - - // get subject alternative names - std::vector subject_alt_names(X509* x509) - { - std::vector sans; - GENERAL_NAMES_ptr subject_alt_names((GENERAL_NAMES*)X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL), &GENERAL_NAMES_free); - for (auto idx = 0; idx < sk_GENERAL_NAME_num(subject_alt_names.get()); idx++) - { - auto gen = sk_GENERAL_NAME_value(subject_alt_names.get(), idx); - if (gen->type == GEN_URI || gen->type == GEN_DNS || gen->type == GEN_EMAIL) - { - auto asn1_str = gen->d.uniformResourceIdentifier; -#if (OPENSSL_VERSION_NUMBER >= 0x1010100fL) - auto san = std::string(reinterpret_cast(ASN1_STRING_get0_data(asn1_str)), ASN1_STRING_length(asn1_str)); -#else - auto san = std::string(reinterpret_cast(ASN1_STRING_data(asn1_str)), ASN1_STRING_length(asn1_str)); -#endif - sans.push_back(san); - } - else - { - // hmm, not supporting other type of subject alt name - } - } - return sans; - } - - // get certificate information, such as expire date, it is represented as the number of seconds from 1970-01-01T0:0:0Z as measured in UTC - cert_info cert_information(const std::string& cert_data) - { - BIO_ptr bio(BIO_new(BIO_s_mem()), &BIO_free); - if ((size_t)BIO_write(bio.get(), cert_data.data(), (int)cert_data.size()) != cert_data.size()) - { - throw est_exception("failed to load cert to bio: BIO_write failure: " + last_openssl_error()); - } - - X509_ptr x509(PEM_read_bio_X509_AUX(bio.get(), NULL, NULL, NULL), &X509_free); - if (!x509) - { - throw est_exception("failed to load cert: PEM_read_bio_X509_AUX failure: " + last_openssl_error()); - } - - auto sans = subject_alt_names(x509.get()); - - auto _common_name = common_name(x509.get()); - if (_common_name.empty()) - { - throw est_exception("missing Common Name"); - } - - auto _issuer_name = issuer_name(x509.get()); - if (_issuer_name.empty()) - { - throw est_exception("missing Issuer Common Name"); - } - - // X509_get_notAfter returns the time that the cert expires, in Abstract Syntax Notation - // According to the openssl documentation, the returned value is an internal pointer which MUST NOT be freed -#if (OPENSSL_VERSION_NUMBER >= 0x1010100fL) - auto not_before = X509_get0_notBefore(x509.get()); -#else - auto not_before = X509_get_notBefore(x509.get()); -#endif - if(!not_before) - { - throw est_exception("failed to get notBefore: X509_get0_notBefore failure: " + last_openssl_error()); - } -#if (OPENSSL_VERSION_NUMBER >= 0x1010100fL) - auto not_after = X509_get0_notAfter(x509.get()); -#else - auto not_after = X509_get_notAfter(x509.get()); -#endif - if (!not_after) - { - throw est_exception("failed to get notAfter: X509_get0_notAfter failure: " + last_openssl_error()); - } -#if (OPENSSL_VERSION_NUMBER >= 0x1010100fL) - tm not_before_tm; - if (!ASN1_TIME_to_tm(not_before, ¬_before_tm)) - { - throw est_exception("failed to convert notBefore ASN1_TIME to tm: ASN1_TIME_to_tm failure: " + last_openssl_error()); - } - auto not_before_time = mktime(¬_before_tm); - tm not_after_tm; - if (!ASN1_TIME_to_tm(not_after, ¬_after_tm)) - { - throw est_exception("failed to convert not_after ASN1_TIME to tm: ASN1_TIME_to_tm failure: " + last_openssl_error()); - } - auto not_after_time = mktime(¬_after_tm); -#else - // Construct another ASN1_TIME for the unix epoch, get the difference - // between them and use that to calculate a unix timestamp representing - // when the cert expires - ASN1_TIME_ptr epoch(ASN1_TIME_new(), &ASN1_STRING_free); - ASN1_TIME_set_string(epoch.get(), "700101000000Z"); - int days{ 0 }; - int seconds{ 0 }; - - if (!ASN1_TIME_diff(&days, &seconds, epoch.get(), not_before)) - { - throw est_exception("failed to get the days and seconds value of not_before: ASN1_TIME_diff failure: " + last_openssl_error()); - } - time_t not_before_time = (days * 24 * 60 * 60) + seconds; - if (!ASN1_TIME_diff(&days, &seconds, epoch.get(), not_after)) - { - throw est_exception("failed to get the days and seconds value of not_after: ASN1_TIME_diff failure: " + last_openssl_error()); - } - time_t not_after_time = (days * 24 * 60 * 60) + seconds; -#endif - return{ _common_name, _issuer_name, not_before_time, not_after_time, sans }; - } - - // split certificate chain to list - std::vector split_certificate_chain(const std::string& cert_data) - { - std::vector certs; - const std::string begin_delimiter{ "-----BEGIN CERTIFICATE-----" }; - const std::string end_delimiter{ "-----END CERTIFICATE-----" }; - size_t start = 0; - size_t end = 0; - do - { - start = cert_data.find(begin_delimiter, start); - end = cert_data.find(end_delimiter, start); - - if (std::string::npos != start && std::string::npos != end) - { - certs.push_back(cert_data.substr(start, end - start + end_delimiter.length())); - start = end + end_delimiter.length(); - } - - } while (std::string::npos != start && std::string::npos != end); - - return certs; - } - - // calculate the number of seconds to expire with the given ratio - int certificate_expiry_from_now(const std::string& cert_data, double ratio) - { - const auto cert_info = cert_information(cert_data); - const auto now = time(NULL); - const auto from_now = difftime(cert_info.not_after, now); - return (int)(from_now > 0 ? from_now * ratio : 0); - } std::shared_ptr make_rsa_key(const std::string& private_key_data = {}, const std::string& password = {}) { @@ -241,24 +31,24 @@ namespace nmos EVP_PKEY_CTX_ptr ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL), &EVP_PKEY_CTX_free); if (!ctx) { - throw est_exception("failed to generate RSA key: EVP_PKEY_CTX_new_id failure: " + last_openssl_error()); + throw est_exception("failed to generate RSA key: EVP_PKEY_CTX_new_id failure: " + ssl::experimental::last_openssl_error()); } // generate the RSA key if (0 >= EVP_PKEY_keygen_init(ctx.get())) { - throw est_exception("failed to generate RSA key: EVP_PKEY_keygen_init failure: " + last_openssl_error()); + throw est_exception("failed to generate RSA key: EVP_PKEY_keygen_init failure: " + ssl::experimental::last_openssl_error()); } if (0 >= EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), 2048)) { - throw est_exception("failed to generate RSA key: EVP_PKEY_CTX_set_rsa_keygen_bits failure: " + last_openssl_error()); + throw est_exception("failed to generate RSA key: EVP_PKEY_CTX_set_rsa_keygen_bits failure: " + ssl::experimental::last_openssl_error()); } EVP_PKEY* pkey_temp = NULL; if (0 >= EVP_PKEY_keygen(ctx.get(), &pkey_temp)) { - throw est_exception("failed to generate RSA key: EVP_PKEY_keygen failure: " + last_openssl_error()); + throw est_exception("failed to generate RSA key: EVP_PKEY_keygen failure: " + ssl::experimental::last_openssl_error()); } // create a EVP_PKEY to store key @@ -268,15 +58,15 @@ namespace nmos } else { - BIO_ptr bio(BIO_new(BIO_s_mem()), &BIO_free); + ssl::experimental::BIO_ptr bio(BIO_new(BIO_s_mem()), &BIO_free); if ((size_t)BIO_write(bio.get(), private_key_data.data(), (int)private_key_data.size()) != private_key_data.size()) { - throw est_exception("failed to load RSA key: BIO_write failure: " + last_openssl_error()); + throw est_exception("failed to load RSA key: BIO_write failure: " + ssl::experimental::last_openssl_error()); } std::shared_ptr pkey(PEM_read_bio_PrivateKey(bio.get(), NULL, NULL, const_cast(password.c_str())), &EVP_PKEY_free); if (!pkey) { - throw est_exception("failed to load RSA key: PEM_read_bio_PrivateKey failure: " + last_openssl_error()); + throw est_exception("failed to load RSA key: PEM_read_bio_PrivateKey failure: " + ssl::experimental::last_openssl_error()); } return pkey; } @@ -290,13 +80,13 @@ namespace nmos EVP_PKEY_CTX_ptr ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL), &EVP_PKEY_CTX_free); if (!ctx) { - throw est_exception("failed to generate ECDSA key: EVP_PKEY_CTX_new_id failure: " + last_openssl_error()); + throw est_exception("failed to generate ECDSA key: EVP_PKEY_CTX_new_id failure: " + ssl::experimental::last_openssl_error()); } // generate the ECDSA key if (0 >= EVP_PKEY_keygen_init(ctx.get())) { - throw est_exception("failed to generate ECDSA key: EVP_PKEY_keygen_init failure: " + last_openssl_error()); + throw est_exception("failed to generate ECDSA key: EVP_PKEY_keygen_init failure: " + ssl::experimental::last_openssl_error()); } // use the ANSI X9.62 Prime 256v1 curve @@ -304,13 +94,13 @@ namespace nmos // See https://tools.ietf.org/search/rfc4492#appendix-A if (0 >= EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), NID_X9_62_prime256v1)) { - throw est_exception("failed to generate ECDSA key: EVP_PKEY_CTX_set_ec_paramgen_curve_nid failure: " + last_openssl_error()); + throw est_exception("failed to generate ECDSA key: EVP_PKEY_CTX_set_ec_paramgen_curve_nid failure: " + ssl::experimental::last_openssl_error()); } EVP_PKEY* pkey_temp = NULL; if (0 >= EVP_PKEY_keygen(ctx.get(), &pkey_temp)) { - throw est_exception("failed to generate ECDSA key: EVP_PKEY_keygen failure: " + last_openssl_error()); + throw est_exception("failed to generate ECDSA key: EVP_PKEY_keygen failure: " + ssl::experimental::last_openssl_error()); } // create a EVP_PKEY to store key @@ -320,15 +110,15 @@ namespace nmos } else { - BIO_ptr bio(BIO_new(BIO_s_mem()), &BIO_free); + ssl::experimental::BIO_ptr bio(BIO_new(BIO_s_mem()), &BIO_free); if ((size_t)BIO_write(bio.get(), private_key_data.data(), (int)private_key_data.size()) != private_key_data.size()) { - throw est_exception("failed to load ECDSA key: BIO_write failure: " + last_openssl_error()); + throw est_exception("failed to load ECDSA key: BIO_write failure: " + ssl::experimental::last_openssl_error()); } std::shared_ptr pkey(PEM_read_bio_PrivateKey(bio.get(), NULL, NULL, const_cast(password.c_str())), EVP_PKEY_free); if (!pkey) { - throw est_exception("failed to load ECDSA key: PEM_read_bio_PrivateKey failure: " + last_openssl_error()); + throw est_exception("failed to load ECDSA key: PEM_read_bio_PrivateKey failure: " + ssl::experimental::last_openssl_error()); } return pkey; } @@ -352,7 +142,7 @@ namespace nmos auto certs = p7->d.sign->cert; if (certs) { - BIO_ptr bio(BIO_new(BIO_s_mem()), &BIO_free); + ssl::experimental::BIO_ptr bio(BIO_new(BIO_s_mem()), &BIO_free); for (auto idx = 0; idx < sk_X509_num(certs); idx++) { @@ -370,9 +160,17 @@ namespace nmos OPENSSL_free(p); BIO_puts(out, "\n\nnotBefore="); +#if (OPENSSL_VERSION_NUMBER >= 0x1010100fL) ASN1_TIME_print(out, X509_get0_notBefore(x)); +#else + ASN1_TIME_print(out, X509_get_notBefore(x)); +#endif BIO_puts(out, "\n\nnotAfter="); +#if (OPENSSL_VERSION_NUMBER >= 0x1010100fL) ASN1_TIME_print(out, X509_get0_notAfter(x)); +#else + ASN1_TIME_print(out, X509_get_notAfter(x)); +#endif BIO_puts(out, "\n\n"); }; cert_info(bio.get(), x509); @@ -385,7 +183,7 @@ namespace nmos std::string pem(size_t(buf->length), 0); if ((size_t)BIO_read(bio.get(), (void*)pem.data(), (int)pem.length()) != pem.length()) { - throw est_exception("failed to convert PKCS7 to pem: BIO_read failure: " + last_openssl_error()); + throw est_exception("failed to convert PKCS7 to pem: BIO_read failure: " + ssl::experimental::last_openssl_error()); } return pem; } @@ -398,7 +196,7 @@ namespace nmos // convert Private key to pem format std::string to_pem(std::shared_ptr pkey) { - BIO_ptr bio(BIO_new(BIO_s_mem()), &BIO_free); + ssl::experimental::BIO_ptr bio(BIO_new(BIO_s_mem()), &BIO_free); if (PEM_write_bio_PrivateKey(bio.get(), pkey.get(), NULL, NULL, 0, NULL, NULL)) { BUF_MEM* buf; @@ -406,13 +204,13 @@ namespace nmos std::string pem(size_t(buf->length), 0); if ((size_t)BIO_read(bio.get(), (void*)pem.data(), (int)pem.length()) != pem.length()) { - throw est_exception("failed to convert private key to pem: BIO_read failure: " + last_openssl_error()); + throw est_exception("failed to convert private key to pem: BIO_read failure: " + ssl::experimental::last_openssl_error()); } return pem; } else { - throw est_exception("failed to convert private key to pem: PEM_write_bio_PrivateKey failure: " + last_openssl_error()); + throw est_exception("failed to convert private key to pem: PEM_write_bio_PrivateKey failure: " + ssl::experimental::last_openssl_error()); } } @@ -433,10 +231,10 @@ namespace nmos // convert PKCS7 to pem format // it is based on openssl example // See https://github.com/openssl/openssl/blob/master/apps/pkcs7.c - BIO_ptr bio(BIO_new(BIO_s_mem()), &BIO_free); // BIO_free_all + ssl::experimental::BIO_ptr bio(BIO_new(BIO_s_mem()), &BIO_free); // BIO_free_all if ((size_t)BIO_write(bio.get(), pkcs7.data(), (int)pkcs7.size()) != pkcs7.size()) { - throw est_exception("failed to load PKCS7: BIO_write failure: " + last_openssl_error()); + throw est_exception("failed to load PKCS7: BIO_write failure: " + ssl::experimental::last_openssl_error()); } std::shared_ptr p7(PEM_read_bio_PKCS7(bio.get(), NULL, NULL, NULL), PKCS7_free); if (p7) @@ -445,7 +243,7 @@ namespace nmos } else { - throw est_exception("failed to load PKCS7: PEM_read_bio_PKCS7 failure: " + last_openssl_error()); + throw est_exception("failed to load PKCS7: PEM_read_bio_PKCS7 failure: " + ssl::experimental::last_openssl_error()); } } @@ -458,12 +256,12 @@ namespace nmos X509_REQ_ptr x509_req(X509_REQ_new(), &X509_REQ_free); if (!x509_req) { - throw est_exception("failed to create x509 req: X509_REQ_new failure: " + last_openssl_error()); + throw est_exception("failed to create x509 req: X509_REQ_new failure: " + ssl::experimental::last_openssl_error()); } // set version of x509 request if (!X509_REQ_set_version(x509_req.get(), version)) { - throw est_exception("failed to set version of x509 req: X509_REQ_set_version failure: " + last_openssl_error()); + throw est_exception("failed to set version of x509 req: X509_REQ_set_version failure: " + ssl::experimental::last_openssl_error()); } // set subject of x509 request @@ -475,7 +273,7 @@ namespace nmos if (!X509_NAME_add_entry_by_txt(x509_name, subject.c_str(), MBSTRING_ASC, (const unsigned char*)value.c_str(), -1, -1, 0)) { std::stringstream ss; - ss << "failed to set '" << subject << "' of x509 req: X509_NAME_add_entry_by_txt failure: " << last_openssl_error(); + ss << "failed to set '" << subject << "' of x509 req: X509_NAME_add_entry_by_txt failure: " << ssl::experimental::last_openssl_error(); throw est_exception(ss.str()); } } @@ -511,7 +309,7 @@ namespace nmos if (!extension) { std::stringstream ss; - ss << "failed to create '" << nid << "' extension: X509V3_EXT_conf_nid failure: " << last_openssl_error(); + ss << "failed to create '" << nid << "' extension: X509V3_EXT_conf_nid failure: " << ssl::experimental::last_openssl_error(); // release all previous added extension sk_X509_EXTENSION_pop_free(extensions, X509_EXTENSION_free); @@ -526,7 +324,7 @@ namespace nmos // add extensions to x509 request if (!X509_REQ_add_extensions(x509_req.get(), extensions)) { - throw est_exception("failed to add extnsions to x509 req: X509_REQ_add_extensions failure: " + last_openssl_error()); + throw est_exception("failed to add extnsions to x509 req: X509_REQ_add_extensions failure: " + ssl::experimental::last_openssl_error()); } // release x509 extensions sk_X509_EXTENSION_pop_free(extensions, X509_EXTENSION_free); @@ -534,17 +332,17 @@ namespace nmos // set public key of x509 req if (!X509_REQ_set_pubkey(x509_req.get(), pkey.get())) { - throw est_exception("failed to set public key of x509 req: X509_REQ_set_pubkey failure: " + last_openssl_error()); + throw est_exception("failed to set public key of x509 req: X509_REQ_set_pubkey failure: " + ssl::experimental::last_openssl_error()); } // set sign key of x509 req if (0 >= X509_REQ_sign(x509_req.get(), pkey.get(), EVP_sha256())) { - throw est_exception("failed to set sign key of x509 req: X509_REQ_sign failure: " + last_openssl_error()); + throw est_exception("failed to set sign key of x509 req: X509_REQ_sign failure: " + ssl::experimental::last_openssl_error()); } // generate x509 req in pem format - BIO_ptr bio(BIO_new(BIO_s_mem()), &BIO_free); + ssl::experimental::BIO_ptr bio(BIO_new(BIO_s_mem()), &BIO_free); if (PEM_write_bio_X509_REQ(bio.get(), x509_req.get())) { BUF_MEM* buf; @@ -552,13 +350,13 @@ namespace nmos std::string pem(size_t(buf->length), 0); if ((size_t)BIO_read(bio.get(), (void*)pem.data(), (int)pem.length()) != pem.length()) { - throw est_exception("failed to generate CSR: BIO_read failure: " + last_openssl_error()); + throw est_exception("failed to generate CSR: BIO_read failure: " + ssl::experimental::last_openssl_error()); } return pem; } else { - throw est_exception("failed to generate CSR: PEM_write_bio_X509_REQ failure: " + last_openssl_error()); + throw est_exception("failed to generate CSR: PEM_write_bio_X509_REQ failure: " + ssl::experimental::last_openssl_error()); } } @@ -576,18 +374,18 @@ namespace nmos return{ to_pem(pkey), make_X509_req(pkey, common_name, country, state, city, organization, organizational_unit, email_address) }; } - std::vector x509_crl_urls(const std::string& cert_data) + std::vector x509_crl_urls(const std::string& certificate) { - BIO_ptr bio(BIO_new(BIO_s_mem()), &BIO_free); - if ((size_t)BIO_write(bio.get(), cert_data.data(), (int)cert_data.size()) != cert_data.size()) + ssl::experimental::BIO_ptr bio(BIO_new(BIO_s_mem()), &BIO_free); + if ((size_t)BIO_write(bio.get(), certificate.data(), (int)certificate.size()) != certificate.size()) { - throw est_exception("failed to load cert to bio: BIO_write failure: " + last_openssl_error()); + throw est_exception("failed to load cert to bio: BIO_write failure: " + ssl::experimental::last_openssl_error()); } - X509_ptr x509(PEM_read_bio_X509_AUX(bio.get(), NULL, NULL, NULL), &X509_free); + ssl::experimental::X509_ptr x509(PEM_read_bio_X509_AUX(bio.get(), NULL, NULL, NULL), &X509_free); if (!x509) { - throw est_exception("failed to load cert: PEM_read_bio_X509_AUX failure: " + last_openssl_error()); + throw est_exception("failed to load cert: PEM_read_bio_X509_AUX failure: " + ssl::experimental::last_openssl_error()); } std::vector list; @@ -602,7 +400,11 @@ namespace nmos { auto gen = sk_GENERAL_NAME_value(distpoint->name.fullname, i); auto asn1_str = gen->d.uniformResourceIdentifier; +#if (OPENSSL_VERSION_NUMBER >= 0x1010100fL) list.push_back(std::string((const char*)ASN1_STRING_get0_data(asn1_str), ASN1_STRING_length(asn1_str))); +#else + list.push_back(std::string((const char*)ASN1_STRING_data(asn1_str), ASN1_STRING_length(asn1_str))); +#endif } } else if (distpoint->type == 1) //relativename X509NAME @@ -612,7 +414,11 @@ namespace nmos { auto e = sk_X509_NAME_ENTRY_value(sk_relname, i); auto d = X509_NAME_ENTRY_get_data(e); +#if (OPENSSL_VERSION_NUMBER >= 0x1010100fL) list.push_back(std::string((const char*)ASN1_STRING_get0_data(d), ASN1_STRING_length(d))); +#else + list.push_back(std::string((const char*)ASN1_STRING_data(d), ASN1_STRING_length(d))); +#endif } } } @@ -655,13 +461,13 @@ namespace nmos return false; } - bool is_revoked_by_crl(const std::string& cert_data, const std::string& cert_issuer_data, const std::string& crl_data) + bool is_revoked_by_crl(const std::string& certificate, const std::string& cert_issuer_data, const std::string& crl_data) { - BIO_ptr bio_cert(BIO_new(BIO_s_mem()), &BIO_free); - BIO_puts(bio_cert.get(), cert_data.c_str()); - BIO_ptr bio_cert_issuer(BIO_new(BIO_s_mem()), &BIO_free); + ssl::experimental::BIO_ptr bio_cert(BIO_new(BIO_s_mem()), &BIO_free); + BIO_puts(bio_cert.get(), certificate.c_str()); + ssl::experimental::BIO_ptr bio_cert_issuer(BIO_new(BIO_s_mem()), &BIO_free); BIO_puts(bio_cert_issuer.get(), cert_issuer_data.c_str()); - BIO_ptr bio_crl(BIO_new(BIO_s_mem()), &BIO_free); + ssl::experimental::BIO_ptr bio_crl(BIO_new(BIO_s_mem()), &BIO_free); BIO_puts(bio_crl.get(), crl_data.c_str()); return is_revoked_by_crl( diff --git a/Development/nmos/est_utils.h b/Development/nmos/est_utils.h index 99326334b..454a7927d 100644 --- a/Development/nmos/est_utils.h +++ b/Development/nmos/est_utils.h @@ -16,23 +16,6 @@ namespace nmos namespace details { - struct cert_info - { - std::string common_name; - std::string issuer_name; - time_t not_before; - time_t not_after; - std::vector sans; - }; - // get certificate information, such as expire date, it is represented as the number of seconds from 1970-01-01T0:0:0Z as measured in UTC - cert_info cert_information(const std::string& cert_data); - - // split certificate chain to list - std::vector split_certificate_chain(const std::string& certificate_data); - - // calculate the number of seconds to expire with the given ratio - int certificate_expiry_from_now(const std::string& cert_data, double ratio = 1.0); - std::string make_pem_from_pkcs7(const std::string& pkcs7); typedef std::pair csr; // csr represented private key pem and csr pem @@ -41,9 +24,9 @@ namespace nmos csr make_ecdsa_csr(const std::string& common_name, const std::string& country, const std::string& state, const std::string& city, const std::string& organization, const std::string& organizational_unit, const std::string& email_address, const std::string& private_key_data = {}, const std::string& password = {}); - std::vector x509_crl_urls(const std::string& cert_data); + std::vector x509_crl_urls(const std::string& certificate); - bool is_revoked_by_crl(const std::string& certificate_file, const std::string& certificate_issuer_file, const std::string& crl_data); + bool is_revoked_by_crl(const std::string& certificate, const std::string& cert_issuer_data, const std::string& crl_data); } } } diff --git a/Development/ssl/ssl_utils.cpp b/Development/ssl/ssl_utils.cpp index 9dd4cdb90..0d10be459 100644 --- a/Development/ssl/ssl_utils.cpp +++ b/Development/ssl/ssl_utils.cpp @@ -204,7 +204,7 @@ namespace ssl } // calculate the number of seconds until expiry of the specified certificate - // 0 is returned if certificate has already expired + // if the certificate has expired, the value of 0 will be returned double certificate_expiry_from_now(const std::string& certificate) { const auto certificate_info = get_certificate_info(certificate); @@ -212,5 +212,12 @@ namespace ssl const auto from_now = difftime(certificate_info.not_after, now); return (std::max)(0.0, from_now); } + + // calculate the number of the factor of seconds until expiry of the specified certificate + // if the certificate has expired, the value of 0 will be returned + double certificate_expiry_from_now(const std::string& certificate, double ratio) + { + return certificate_expiry_from_now(certificate) * ratio; + } } } diff --git a/Development/ssl/ssl_utils.h b/Development/ssl/ssl_utils.h index 2cf2853c6..9e8286470 100644 --- a/Development/ssl/ssl_utils.h +++ b/Development/ssl/ssl_utils.h @@ -44,8 +44,12 @@ namespace ssl std::vector split_certificate_chain(const std::string& certificate_chain); // calculate the number of seconds until expiry of the specified certificate - // 0 is returned if certificate has already expired + // if the certificate has expired, the value of 0 will be returned double certificate_expiry_from_now(const std::string& certificate); + + // calculate the number of the factor of seconds until expiry of the specified certificate + // if the certificate has expired, the value of 0 will be returned + double certificate_expiry_from_now(const std::string& certificate, double ratio); } }