diff --git a/dependencies.yaml b/dependencies.yaml index 79487392f..090db0d71 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -31,7 +31,7 @@ websocketpp: cmake_condition: "LIBOCPP_ENABLE_DEPRECATED_WEBSOCKETPP" libevse-security: git: https://github.com/EVerest/libevse-security.git - git_tag: v0.6.0 + git_tag: 6e702ef5df568c2f9929a3c3b97a09c0cb4c5b21 libwebsockets: git: https://github.com/warmcat/libwebsockets.git git_tag: v4.3.3 diff --git a/include/ocpp/common/evse_security.hpp b/include/ocpp/common/evse_security.hpp index f0e8d6af2..042ee738b 100644 --- a/include/ocpp/common/evse_security.hpp +++ b/include/ocpp/common/evse_security.hpp @@ -80,25 +80,27 @@ class EvseSecurity { virtual bool is_ca_certificate_installed(const CaCertificateType& certificate_type) = 0; /// \brief Generates a certificate signing request for the given \p certificate_type , \p country , \p organization - /// and \p common . This function respects the requirements of OCPP specified for the CSMS initiated message - /// SignCertificate.req . + /// and \p common , uses the TPM if \p use_tpm is true /// \param certificate_type /// \param country /// \param organization /// \param common - /// \return the PEM formatted certificate signing request - virtual std::string generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, - const std::string& country, - const std::string& organization, const std::string& common, - bool use_tpm) = 0; - - /// \brief Searches the leaf certificate for the given \p certificate_type and retrieves the most recent certificate - /// that is already valid and the respective key . If no certificate is present or no key is matching the - /// certificate, this function returns std::nullopt + /// \param use_tpm If the TPM should be used for the CSR request + /// \return the status and an optional PEM formatted certificate signing request string + virtual GetCertificateSignRequestResult + generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, const std::string& country, + const std::string& organization, const std::string& common, bool use_tpm) = 0; + + /// \brief Searches the filesystem on the specified directories for the given \p certificate_type and retrieves the + /// most recent certificate that is already valid and the respective key. If no certificate is present or no key is + /// matching the certificate, this function returns a GetKeyPairStatus other than "Accepted". The function \ref + /// update_leaf_certificate will install two files for each leaf, one containing the single leaf and one containing + /// the leaf including any possible SUBCAs /// \param certificate_type type of the leaf certificate - /// \param encoding specifies PEM or DER format - /// \return key pair of certificate and key if present, else std::nullopt - virtual std::optional get_key_pair(const CertificateSigningUseEnum& certificate_type) = 0; + /// \param include_ocsp if OCSP data should be included + /// \return contains response result, with info related to the certificate chain and response status + virtual GetCertificateInfoResult get_leaf_certificate_info(const CertificateSigningUseEnum& certificate_type, + bool include_ocsp = false) = 0; /// \brief Updates the certificate and key links for the given \p certificate_type virtual bool update_certificate_links(const CertificateSigningUseEnum& certificate_type) = 0; diff --git a/include/ocpp/common/evse_security_impl.hpp b/include/ocpp/common/evse_security_impl.hpp index c6d4ce279..173b68a3d 100644 --- a/include/ocpp/common/evse_security_impl.hpp +++ b/include/ocpp/common/evse_security_impl.hpp @@ -48,10 +48,12 @@ class EvseSecurityImpl : public EvseSecurity { void update_ocsp_cache(const CertificateHashDataType& certificate_hash_data, const std::string& ocsp_response) override; bool is_ca_certificate_installed(const CaCertificateType& certificate_type) override; - std::string generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, - const std::string& country, const std::string& organization, - const std::string& common, bool use_tpm) override; - std::optional get_key_pair(const CertificateSigningUseEnum& certificate_type) override; + GetCertificateSignRequestResult + generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, const std::string& country, + const std::string& organization, const std::string& common, + bool use_tpm) override; + GetCertificateInfoResult get_leaf_certificate_info(const CertificateSigningUseEnum& certificate_type, + bool include_ocsp = false) override; bool update_certificate_links(const CertificateSigningUseEnum& certificate_type) override; std::string get_verify_file(const CaCertificateType& certificate_type) override; int get_leaf_expiry_days_count(const CertificateSigningUseEnum& certificate_type) override; @@ -59,10 +61,12 @@ class EvseSecurityImpl : public EvseSecurity { namespace conversions { +GetCertificateSignRequestStatus to_ocpp(evse_security::GetCertificateSignRequestStatus other); CaCertificateType to_ocpp(evse_security::CaCertificateType other); CertificateSigningUseEnum to_ocpp(evse_security::LeafCertificateType other); CertificateType to_ocpp(evse_security::CertificateType other); HashAlgorithmEnumType to_ocpp(evse_security::HashAlgorithm other); +GetCertificateInfoStatus to_ocpp(evse_security::GetCertificateInfoStatus other); InstallCertificateResult to_ocpp(evse_security::InstallCertificateResult other); CertificateValidationResult to_ocpp(evse_security::CertificateValidationResult other); DeleteCertificateResult to_ocpp(evse_security::DeleteCertificateResult other); @@ -70,7 +74,8 @@ DeleteCertificateResult to_ocpp(evse_security::DeleteCertificateResult other); CertificateHashDataType to_ocpp(evse_security::CertificateHashData other); CertificateHashDataChain to_ocpp(evse_security::CertificateHashDataChain other); OCSPRequestData to_ocpp(evse_security::OCSPRequestData other); -KeyPair to_ocpp(evse_security::KeyPair other); +CertificateOCSP to_ocpp(evse_security::CertificateOCSP other); +CertificateInfo to_ocpp(evse_security::CertificateInfo other); evse_security::CaCertificateType from_ocpp(CaCertificateType other); evse_security::LeafCertificateType from_ocpp(LeafCertificateType other); @@ -83,7 +88,8 @@ evse_security::DeleteCertificateResult from_ocpp(DeleteCertificateResult other); evse_security::CertificateHashData from_ocpp(CertificateHashDataType other); evse_security::CertificateHashDataChain from_ocpp(CertificateHashDataChain other); evse_security::OCSPRequestData from_ocpp(OCSPRequestData other); -evse_security::KeyPair from_ocpp(KeyPair other); +evse_security::CertificateOCSP from_ocpp(CertificateOCSP other); +evse_security::CertificateInfo from_ocpp(CertificateInfo other); }; // namespace conversions diff --git a/include/ocpp/common/types.hpp b/include/ocpp/common/types.hpp index 87a447e58..4a1f0655f 100644 --- a/include/ocpp/common/types.hpp +++ b/include/ocpp/common/types.hpp @@ -531,11 +531,43 @@ struct OCSPRequestData { std::string responderUrl; }; -struct KeyPair { - fs::path certificate_path; // path to the full certificate chain - fs::path certificate_single_path; // path to the single leaf certificate - fs::path key_path; // path to private key of the leaf certificate - std::optional password; // optional password for the private key +enum class GetCertificateSignRequestStatus { + Accepted, + InvalidRequestedType, ///< Requested a CSR for non CSMS/V2G leafs + KeyGenError, ///< The key could not be generated with the requested/default parameters + GenerationError, ///< Any other error when creating the CSR +}; + +enum class GetCertificateInfoStatus { + Accepted, + Rejected, + NotFound, + NotFoundValid, + PrivateKeyNotFound, +}; + +struct GetCertificateSignRequestResult { + GetCertificateSignRequestStatus status; + std::optional csr; +}; + +struct CertificateOCSP { + CertificateHashDataType hash; + std::optional ocsp_path; +}; + +struct CertificateInfo { + std::optional certificate_path; // path to the full certificate chain + std::optional certificate_single_path; // path to the single leaf certificate + int certificate_count; // count of certs in the chain + fs::path key_path; // path to private key of the leaf certificate + std::optional password; // optional password for the private key + std::vector ocsp; // OCSP data if requested +}; + +struct GetCertificateInfoResult { + GetCertificateInfoStatus status; + std::optional info; }; enum class LeafCertificateType { @@ -580,6 +612,11 @@ enum class FirmwareStatusNotification { SignatureVerified }; +namespace conversions { +/// \brief Converts GetCertificateSignRequestStatus to string +std::string generate_certificate_signing_request_status_to_string(const GetCertificateSignRequestStatus status); +} // namespace conversions + namespace conversions { /// \brief Converts ocpp::FirmwareStatusNotification to v16::FirmwareStatus @@ -613,6 +650,7 @@ namespace security_events { inline const std::string FIRMWARE_UPDATED = "FirmwareUpdated"; // CRITICAL inline const std::string FAILEDTOAUTHENTICATEATCSMS = "FailedToAuthenticateAtCsms"; inline const std::string CSMSFAILEDTOAUTHENTICATE = "CsmsFailedToAuthenticate"; +inline const std::string CSRGENERATIONFAILED = "CSRGenerationFailed"; inline const std::string SETTINGSYSTEMTIME = "SettingSystemTime"; // CRITICAL inline const std::string RESET_OR_REBOOT = "ResetOrReboot"; // CRITICAL inline const std::string STARTUP_OF_THE_DEVICE = "StartupOfTheDevice"; // CRITICAL diff --git a/lib/ocpp/common/evse_security_impl.cpp b/lib/ocpp/common/evse_security_impl.cpp index 7815c960d..ef3df8628 100644 --- a/lib/ocpp/common/evse_security_impl.cpp +++ b/lib/ocpp/common/evse_security_impl.cpp @@ -96,23 +96,34 @@ bool EvseSecurityImpl::is_ca_certificate_installed(const CaCertificateType& cert return this->evse_security->is_ca_certificate_installed(conversions::from_ocpp(certificate_type)); } -std::string EvseSecurityImpl::generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, - const std::string& country, - const std::string& organization, - const std::string& common, bool use_tpm) { - return this->evse_security->generate_certificate_signing_request(conversions::from_ocpp(certificate_type), country, - organization, common, use_tpm); -} - -std::optional EvseSecurityImpl::get_key_pair(const CertificateSigningUseEnum& certificate_type) { - const auto key_pair = - this->evse_security->get_key_pair(conversions::from_ocpp(certificate_type), evse_security::EncodingFormat::PEM); - - if (key_pair.status == evse_security::GetKeyPairStatus::Accepted && key_pair.pair.has_value()) { - return conversions::to_ocpp(key_pair.pair.value()); - } else { - return std::nullopt; +GetCertificateSignRequestResult +EvseSecurityImpl::generate_certificate_signing_request(const CertificateSigningUseEnum& certificate_type, + const std::string& country, const std::string& organization, + const std::string& common, bool use_tpm) { + auto csr_response = this->evse_security->generate_certificate_signing_request( + conversions::from_ocpp(certificate_type), country, organization, common, use_tpm); + + GetCertificateSignRequestResult result; + + result.status = conversions::to_ocpp(csr_response.status); + result.csr = csr_response.csr; + + return result; +} + +GetCertificateInfoResult EvseSecurityImpl::get_leaf_certificate_info(const CertificateSigningUseEnum& certificate_type, + bool include_ocsp) { + const auto info_response = this->evse_security->get_leaf_certificate_info( + conversions::from_ocpp(certificate_type), evse_security::EncodingFormat::PEM, include_ocsp); + + GetCertificateInfoResult result; + + result.status = conversions::to_ocpp(info_response.status); + if (info_response.info.has_value()) { + result.info = conversions::to_ocpp(info_response.info.value()); } + + return result; } bool EvseSecurityImpl::update_certificate_links(const CertificateSigningUseEnum& certificate_type) { @@ -129,6 +140,22 @@ int EvseSecurityImpl::get_leaf_expiry_days_count(const CertificateSigningUseEnum namespace conversions { +GetCertificateSignRequestStatus to_ocpp(evse_security::GetCertificateSignRequestStatus other) { + switch (other) { + case evse_security::GetCertificateSignRequestStatus::Accepted: + return GetCertificateSignRequestStatus::Accepted; + case evse_security::GetCertificateSignRequestStatus::InvalidRequestedType: + return GetCertificateSignRequestStatus::InvalidRequestedType; + case evse_security::GetCertificateSignRequestStatus::KeyGenError: + return GetCertificateSignRequestStatus::KeyGenError; + case evse_security::GetCertificateSignRequestStatus::GenerationError: + return GetCertificateSignRequestStatus::GenerationError; + default: + throw std::runtime_error( + "Could not convert evse_security::GetCertificateSignRequestStatus to GetCertificateSignRequestStatus"); + } +} + CaCertificateType to_ocpp(evse_security::CaCertificateType other) { switch (other) { case evse_security::CaCertificateType::V2G: @@ -187,6 +214,24 @@ HashAlgorithmEnumType to_ocpp(evse_security::HashAlgorithm other) { } } +GetCertificateInfoStatus to_ocpp(evse_security::GetCertificateInfoStatus other) { + switch (other) { + case evse_security::GetCertificateInfoStatus::Accepted: + return GetCertificateInfoStatus::Accepted; + case evse_security::GetCertificateInfoStatus::Rejected: + return GetCertificateInfoStatus::Rejected; + case evse_security::GetCertificateInfoStatus::NotFound: + return GetCertificateInfoStatus::NotFound; + case evse_security::GetCertificateInfoStatus::NotFoundValid: + return GetCertificateInfoStatus::NotFoundValid; + case evse_security::GetCertificateInfoStatus::PrivateKeyNotFound: + return GetCertificateInfoStatus::PrivateKeyNotFound; + default: + throw std::runtime_error( + "Could not convert evse_security::GetCertificateInfoStatus to GetCertificateInfoStatus"); + } +} + InstallCertificateResult to_ocpp(evse_security::InstallCertificateResult other) { switch (other) { case evse_security::InstallCertificateResult::InvalidSignature: @@ -284,12 +329,27 @@ OCSPRequestData to_ocpp(evse_security::OCSPRequestData other) { return lhs; } -KeyPair to_ocpp(evse_security::KeyPair other) { - KeyPair lhs; +CertificateOCSP to_ocpp(evse_security::CertificateOCSP other) { + CertificateOCSP lhs; + lhs.hash = to_ocpp(other.hash); + lhs.ocsp_path = other.ocsp_path; + return lhs; +} + +CertificateInfo to_ocpp(evse_security::CertificateInfo other) { + CertificateInfo lhs; lhs.certificate_path = other.certificate; lhs.certificate_single_path = other.certificate_single; + lhs.certificate_count = other.certificate_count; lhs.key_path = other.key; lhs.password = other.password; + + if (other.ocsp.empty() == false) { + for (auto& ocsp_data : other.ocsp) { + lhs.ocsp.push_back(to_ocpp(ocsp_data)); + } + } + return lhs; } @@ -440,12 +500,27 @@ evse_security::OCSPRequestData from_ocpp(OCSPRequestData other) { return lhs; } -evse_security::KeyPair from_ocpp(KeyPair other) { - evse_security::KeyPair lhs; +evse_security::CertificateOCSP from_ocpp(CertificateOCSP other) { + evse_security::CertificateOCSP lhs; + lhs.hash = from_ocpp(other.hash); + lhs.ocsp_path = other.ocsp_path; + return lhs; +} + +evse_security::CertificateInfo from_ocpp(CertificateInfo other) { + evse_security::CertificateInfo lhs; lhs.certificate = other.certificate_path; lhs.certificate_single = other.certificate_single_path; + lhs.certificate_count = other.certificate_count; lhs.key = other.key_path; lhs.password = other.password; + + if (other.ocsp.empty() == false) { + for (auto& ocsp_data : other.ocsp) { + lhs.ocsp.push_back(from_ocpp(ocsp_data)); + } + } + return lhs; } diff --git a/lib/ocpp/common/types.cpp b/lib/ocpp/common/types.cpp index 976bb1092..8342b613c 100644 --- a/lib/ocpp/common/types.cpp +++ b/lib/ocpp/common/types.cpp @@ -996,6 +996,23 @@ std::string double_to_string(double d) { } // namespace conversions +namespace conversions { +std::string generate_certificate_signing_request_status_to_string(const GetCertificateSignRequestStatus status) { + switch (status) { + case GetCertificateSignRequestStatus::Accepted: + return "Accepted"; + case GetCertificateSignRequestStatus::InvalidRequestedType: + return "InvalidRequestedType"; + case GetCertificateSignRequestStatus::KeyGenError: + return "KeyGenError"; + case GetCertificateSignRequestStatus::GenerationError: + return "GenerationError"; + default: + throw std::out_of_range("Could not convert GetCertificateSignRequestStatus to string"); + } +} +} // namespace conversions + namespace conversions { v16::FirmwareStatus firmware_status_notification_to_firmware_status(const FirmwareStatusNotification status) { switch (status) { diff --git a/lib/ocpp/common/websocket/websocket_libwebsockets.cpp b/lib/ocpp/common/websocket/websocket_libwebsockets.cpp index 66cc89575..53e752436 100644 --- a/lib/ocpp/common/websocket/websocket_libwebsockets.cpp +++ b/lib/ocpp/common/websocket/websocket_libwebsockets.cpp @@ -448,21 +448,28 @@ void WebsocketTlsTPM::client_loop() { std::optional password; if (this->connection_options.security_profile == 3) { + const auto certificate_response = + this->evse_security->get_leaf_certificate_info(CertificateSigningUseEnum::ChargingStationCertificate); - const auto certificate_key_pair = - this->evse_security->get_key_pair(CertificateSigningUseEnum::ChargingStationCertificate); - - if (!certificate_key_pair.has_value()) { + if (certificate_response.status != ocpp::GetCertificateInfoStatus::Accepted or + !certificate_response.info.has_value()) { EVLOG_AND_THROW(std::runtime_error( "Connecting with security profile 3 but no client side certificate is present or valid")); } - path_chain = certificate_key_pair.value().certificate_path; - if (path_chain.empty()) { - path_chain = certificate_key_pair.value().certificate_single_path; + const auto& certificate_info = certificate_response.info.value(); + + if (certificate_info.certificate_path.has_value()) { + path_chain = certificate_info.certificate_path.value(); + } else if (certificate_info.certificate_single_path.has_value()) { + path_chain = certificate_info.certificate_single_path.value(); + } else { + EVLOG_AND_THROW(std::runtime_error( + "Connecting with security profile 3 but no client side certificate is present or valid")); } - path_key = certificate_key_pair.value().key_path; - password = certificate_key_pair.value().password; + + path_key = certificate_info.key_path; + password = certificate_info.password; } SSL_CTX* ssl_ctx = nullptr; diff --git a/lib/ocpp/common/websocket/websocket_tls.cpp b/lib/ocpp/common/websocket/websocket_tls.cpp index 4f95eed54..cfd5023d4 100644 --- a/lib/ocpp/common/websocket/websocket_tls.cpp +++ b/lib/ocpp/common/websocket/websocket_tls.cpp @@ -171,33 +171,42 @@ tls_context WebsocketTLS::on_tls_init(std::string hostname, websocketpp::connect } if (security_profile == 3) { - const auto certificate_key_pair = - this->evse_security->get_key_pair(CertificateSigningUseEnum::ChargingStationCertificate); + const auto certificate_result = + this->evse_security->get_leaf_certificate_info(CertificateSigningUseEnum::ChargingStationCertificate); - if (certificate_key_pair.has_value() && certificate_key_pair.value().password.has_value()) { - std::string passwd = certificate_key_pair.value().password.value(); - context->set_password_callback( - [passwd](auto max_len, auto purpose) { return passwd.substr(0, max_len); }); - } - - if (!certificate_key_pair.has_value()) { + if (certificate_result.status != GetCertificateInfoStatus::Accepted || + !certificate_result.info.has_value()) { EVLOG_AND_THROW(std::runtime_error( "Connecting with security profile 3 but no client side certificate is present or valid")); } + const auto& certificate_info = certificate_result.info.value(); + + if (certificate_info.password.has_value()) { + std::string passwd = certificate_info.password.value(); + context->set_password_callback( + [passwd](auto max_len, auto purpose) { return passwd.substr(0, max_len); }); + } + // certificate_path contains the chain if not empty. Use certificate chain if available, else use // certificate_single_path - auto certificate_path = certificate_key_pair.value().certificate_path; - if (certificate_path.empty()) { - certificate_path = certificate_key_pair.value().certificate_single_path; + fs::path certificate_path; + + if (certificate_info.certificate_path.has_value()) { + certificate_path = certificate_info.certificate_path.value(); + } else if (certificate_info.certificate_single_path.has_value()) { + certificate_path = certificate_info.certificate_single_path.value(); + } else { + EVLOG_AND_THROW(std::runtime_error( + "Connecting with security profile 3 but no client side certificate is present or valid")); } EVLOG_info << "Using certificate: " << certificate_path; if (SSL_CTX_use_certificate_chain_file(context->native_handle(), certificate_path.c_str()) != 1) { EVLOG_AND_THROW(std::runtime_error("Could not use client certificate file within SSL context")); } - EVLOG_info << "Using key file: " << certificate_key_pair.value().key_path; - if (SSL_CTX_use_PrivateKey_file(context->native_handle(), certificate_key_pair.value().key_path.c_str(), + EVLOG_info << "Using key file: " << certificate_info.key_path; + if (SSL_CTX_use_PrivateKey_file(context->native_handle(), certificate_info.key_path.c_str(), SSL_FILETYPE_PEM) != 1) { EVLOG_AND_THROW(std::runtime_error("Could not set private key file within SSL context")); } diff --git a/lib/ocpp/v16/charge_point_impl.cpp b/lib/ocpp/v16/charge_point_impl.cpp index aae18d5d5..f3f93290d 100644 --- a/lib/ocpp/v16/charge_point_impl.cpp +++ b/lib/ocpp/v16/charge_point_impl.cpp @@ -1426,9 +1426,10 @@ void ChargePointImpl::handleChangeConfigurationRequest(ocpp::Callevse_security - ->get_key_pair(ocpp::CertificateSigningUseEnum::ChargingStationCertificate) - .has_value()) { + this->evse_security + ->get_leaf_certificate_info( + ocpp::CertificateSigningUseEnum::ChargingStationCertificate) + .status != ocpp::GetCertificateInfoStatus::Accepted) { EVLOG_warning << "New security level set to 3 but no Client Certificate is installed"; response.status = ConfigurationStatus::Rejected; } else if (security_profile > 3) { @@ -2239,12 +2240,26 @@ void ChargePointImpl::sign_certificate(const ocpp::CertificateSigningUseEnum& ce EVLOG_info << "Create CSR (TPM=" << this->configuration->getUseTPM() << ")"; SignCertificateRequest req; - const auto csr = this->evse_security->generate_certificate_signing_request( + const auto response = this->evse_security->generate_certificate_signing_request( certificate_signing_use, this->configuration->getSeccLeafSubjectCountry().value_or("DE"), this->configuration->getCpoName().value(), this->configuration->getChargeBoxSerialNumber(), this->configuration->getUseTPM()); - req.csr = csr; + if (response.status != GetCertificateSignRequestStatus::Accepted || !response.csr.has_value()) { + EVLOG_error << "Create CSR (TPM=" << this->configuration->getUseTPM() << ")" + << " failed for:" + << ocpp::conversions::certificate_signing_use_enum_to_string(certificate_signing_use); + + std::string gen_error = + "Sign certificate failed due to:" + + ocpp::conversions::generate_certificate_signing_request_status_to_string(response.status); + this->securityEventNotification(ocpp::security_events::CSRGENERATIONFAILED, gen_error, true); + + return; + } + + req.csr = response.csr.value(); + ocpp::Call call(req, this->message_queue->createMessageId()); this->send(call, initiated_by_trigger_message); } @@ -2925,14 +2940,24 @@ void ChargePointImpl::data_transfer_pnc_sign_certificate() { ocpp::v201::SignCertificateRequest csr_req; - const auto csr = this->evse_security->generate_certificate_signing_request( + const auto result = this->evse_security->generate_certificate_signing_request( ocpp::CertificateSigningUseEnum::V2GCertificate, this->configuration->getSeccLeafSubjectCountry().value_or("DE"), this->configuration->getSeccLeafSubjectOrganization().value_or(this->configuration->getCpoName().value()), this->configuration->getSeccLeafSubjectCommonName().value_or(this->configuration->getChargeBoxSerialNumber()), this->configuration->getUseTPM()); - csr_req.csr = csr; + if (result.status != GetCertificateSignRequestStatus::Accepted || !result.csr.has_value()) { + EVLOG_error << "Could not request new V2GCertificate, because the CSR was not successful."; + + std::string gen_error = "Data transfer pnc csr failed due to:" + + ocpp::conversions::generate_certificate_signing_request_status_to_string(result.status); + this->securityEventNotification(ocpp::security_events::CSRGENERATIONFAILED, gen_error, true); + + return; + } + + csr_req.csr = result.csr.value(); csr_req.certificateType = ocpp::v201::CertificateSigningUseEnum::V2GCertificate; req.data.emplace(json(csr_req).dump()); diff --git a/lib/ocpp/v201/charge_point.cpp b/lib/ocpp/v201/charge_point.cpp index 928ba27d0..bc0299888 100644 --- a/lib/ocpp/v201/charge_point.cpp +++ b/lib/ocpp/v201/charge_point.cpp @@ -1623,8 +1623,9 @@ bool ChargePoint::validate_set_variable(const SetVariableData& set_variable_data } if (network_profile.securityProfile == 3 and - !this->evse_security->get_key_pair(ocpp::CertificateSigningUseEnum::ChargingStationCertificate) - .has_value()) { + this->evse_security + ->get_leaf_certificate_info(ocpp::CertificateSigningUseEnum::ChargingStationCertificate) + .status != ocpp::GetCertificateInfoStatus::Accepted) { EVLOG_warning << "SecurityProfile of configurationSlot: " << configuration_slot << " is 3 but no CSMS Leaf Certificate is installed"; return false; @@ -1799,9 +1800,21 @@ void ChargePoint::sign_certificate_req(const ocpp::CertificateSigningUseEnum& ce bool should_use_tpm = this->device_model->get_optional_value(ControllerComponentVariables::UseTPM).value_or(false); - const auto csr = this->evse_security->generate_certificate_signing_request( + const auto result = this->evse_security->generate_certificate_signing_request( certificate_signing_use, country.value(), organization.value(), common.value(), should_use_tpm); - req.csr = csr; + + if (result.status != GetCertificateSignRequestStatus::Accepted || !result.csr.has_value()) { + EVLOG_error << "CSR generation was unsuccessful for sign request: " + << ocpp::conversions::certificate_signing_use_enum_to_string(certificate_signing_use); + + std::string gen_error = "Sign certificate req failed due to:" + + ocpp::conversions::generate_certificate_signing_request_status_to_string(result.status); + this->security_event_notification_req(ocpp::security_events::CSRGENERATIONFAILED, + std::optional>(gen_error), true, true); + return; + } + + req.csr = result.csr.value(); this->awaited_certificate_signing_use_enum = certificate_signing_use; diff --git a/tests/evse_security_mock.hpp b/tests/evse_security_mock.hpp index 1fe2ec3e0..b9b206876 100644 --- a/tests/evse_security_mock.hpp +++ b/tests/evse_security_mock.hpp @@ -26,10 +26,11 @@ class EvseSecurityMock : public EvseSecurity { MOCK_METHOD(std::vector, get_mo_ocsp_request_data, (const std::string&), (override)); MOCK_METHOD(void, update_ocsp_cache, (const CertificateHashDataType&, const std::string&), (override)); MOCK_METHOD(bool, is_ca_certificate_installed, (const CaCertificateType&), (override)); - MOCK_METHOD(std::string, generate_certificate_signing_request, + MOCK_METHOD(GetCertificateSignRequestResult, generate_certificate_signing_request, (const CertificateSigningUseEnum&, const std::string&, const std::string&, const std::string&, bool), (override)); - MOCK_METHOD(std::optional, get_key_pair, (const CertificateSigningUseEnum&), (override)); + MOCK_METHOD(GetCertificateInfoResult, get_leaf_certificate_info, (const CertificateSigningUseEnum&, bool), + (override)); MOCK_METHOD(bool, update_certificate_links, (const CertificateSigningUseEnum&), (override)); MOCK_METHOD(std::string, get_verify_file, (const CaCertificateType&), (override)); MOCK_METHOD(int, get_leaf_expiry_days_count, (const CertificateSigningUseEnum&), (override));