Skip to content

Commit

Permalink
- added support for Hubject PnC requirements
Browse files Browse the repository at this point in the history
- this includes the introduction of additional ocpp configuration keys

Signed-off-by: pietfried <[email protected]>
  • Loading branch information
Pietfried committed Mar 21, 2023
1 parent b7f550b commit bb1399c
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 31 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.14)

project(ocpp
VERSION 0.6.0
VERSION 0.6.1
DESCRIPTION "A C++ implementation of the Open Charge Point Protocol"
LANGUAGES CXX
)
Expand Down
21 changes: 21 additions & 0 deletions config/v16/profile_schemas/Internal.json
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,27 @@
"readOnly": false,
"default": 604800,
"minimum": 86400
},
"SeccLeafSubjectCommonName": {
"$comment": "Common Name(s) of the SECC (EVSE) leaf certificate(s). The CN must be a SECCID. The field can contain optional multiple SECCIDs if necessary.",
"type": "string",
"readOnly": false
},
"SeccLeafSubjectCountry": {
"$comment": "County of the SECC (EVSE) leaf certificate. Indicates in which country the CPO operates.",
"type": "string",
"readOnly": false,
"default": "DE"
},
"SeccLeafSubjectOrganization": {
"$comment": "Organization of the SECC (EVSE) leaf certificate. Indicates which CPO operates this EVSE. Example: Hubject GmbH",
"type": "string",
"readOnly": false
},
"ConnectorEvseIds": {
"$comment": "Comma separated EVSEIDs for OCPP connectors starting with connector 1 in one string.",
"type": "string",
"readOnly": false
}
},
"additionalProperties": false
Expand Down
5 changes: 2 additions & 3 deletions include/ocpp/common/pki_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,8 @@ class PkiHandler {
void writeClientCertificate(const std::string& certificate, const CertificateSigningUseEnum& certificateUse);

/// \brief generates a certifcate signing request from the given parameters
std::string generateCsr(const CertificateSigningUseEnum& certificateType, const char* szCountry,
const char* szProvince, const char* szCity, const char* szOrganization,
const char* szCommon);
std::string generateCsr(const CertificateSigningUseEnum& certificateType, const std::string& szCountry,
const std::string& szOrganization, const std::string& szCommon);

/// \brief indicates if a central system root certificate is installed
bool isCentralSystemRootCertificateInstalled();
Expand Down
23 changes: 20 additions & 3 deletions include/ocpp/v16/charge_point_configuration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ class ChargePointConfiguration {
bool isConnectorPhaseRotationValid(std::string str);

public:
ChargePointConfiguration(const json& config, const std::string& ocpp_main_path, const std::string& user_config_path);
ChargePointConfiguration(const json& config, const std::string& ocpp_main_path,
const std::string& user_config_path);

// Internal config options
std::string getChargePointId();
Expand Down Expand Up @@ -306,15 +307,15 @@ class ChargePointConfiguration {
boost::optional<bool> getCentralContractValidationAllowed();
void setCentralContractValidationAllowed(const bool central_contract_validation_allowed);
boost::optional<KeyValue> getCentralContractValidationAllowedKeyValue();

boost::optional<int32_t> getCertSigningWaitMinimum();
void setCertSigningWaitMinimum(const int32_t cert_signing_wait_minimum);
boost::optional<KeyValue> getCertSigningWaitMinimumKeyValue();

boost::optional<int32_t> getCertSigningRepeatTimes();
void setCertSigningRepeatTimes(const int32_t cert_signing_repeat_times);
boost::optional<KeyValue> getCertSigningRepeatTimesKeyValue();

bool getContractValidationOffline();
void setContractValidationOffline(const bool contract_validation_offline);
KeyValue getContractValidationOfflineKeyValue();
Expand All @@ -323,6 +324,22 @@ class ChargePointConfiguration {
void setOcspRequestInterval(const int32_t ocsp_request_interval);
KeyValue getOcspRequestIntervalKeyValue();

boost::optional<std::string> getSeccLeafSubjectCommonName();
void setSeccLeafSubjectCommonName(const std::string& secc_leaf_subject_common_name);
boost::optional<KeyValue> getSeccLeafSubjectCommonNameKeyValue();

boost::optional<std::string> getSeccLeafSubjectCountry();
void setSeccLeafSubjectCountry(const std::string& secc_leaf_subject_country);
boost::optional<KeyValue> getSeccLeafSubjectCountryKeyValue();

boost::optional<std::string> getSeccLeafSubjectOrganization();
void setSeccLeafSubjectOrganization(const std::string& secc_leaf_subject_organization);
boost::optional<KeyValue> getSeccLeafSubjectOrganizationKeyValue();

boost::optional<std::string> getConnectorEvseIds();
void setConnectorEvseIds(const std::string& connector_evse_ids);
boost::optional<KeyValue> getConnectorEvseIdsKeyValue();

boost::optional<KeyValue> get(CiString<50> key);

std::vector<KeyValue> get_all_key_value();
Expand Down
4 changes: 2 additions & 2 deletions include/ocpp/v201/messages/Get15118EVCertificate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace v201 {
struct Get15118EVCertificateRequest : public ocpp::Message {
CiString<50> iso15118SchemaVersion;
CertificateActionEnum action;
CiString<5600> exiRequest;
CiString<7500> exiRequest;
boost::optional<CustomData> customData;

/// \brief Provides the type of this Get15118EVCertificate message as a human readable string
Expand All @@ -37,7 +37,7 @@ std::ostream& operator<<(std::ostream& os, const Get15118EVCertificateRequest& k
/// \brief Contains a OCPP Get15118EVCertificateResponse message
struct Get15118EVCertificateResponse : public ocpp::Message {
Iso15118EVCertificateStatusEnum status;
CiString<5600> exiResponse;
CiString<7500> exiResponse;
boost::optional<CustomData> customData;
boost::optional<StatusInfo> statusInfo;

Expand Down
19 changes: 9 additions & 10 deletions lib/ocpp/common/pki_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,9 +329,8 @@ void PkiHandler::writeClientCertificate(const std::string& certificateChain,
}
}

std::string PkiHandler::generateCsr(const CertificateSigningUseEnum& certificateType, const char* szCountry,
const char* szProvince, const char* szCity, const char* szOrganization,
const char* szCommon) {
std::string PkiHandler::generateCsr(const CertificateSigningUseEnum& certificateType, const std::string& szCountry,
const std::string& szOrganization, const std::string& szCommon) {
int rc;
RSA* r = NULL;
BN_ptr bn(BN_new(), ::BN_free);
Expand Down Expand Up @@ -380,16 +379,16 @@ std::string PkiHandler::generateCsr(const CertificateSigningUseEnum& certificate
// set subject of x509 req
X509_NAME_ptr x509_name_ptr(X509_REQ_get_subject_name(x509_req), ::X509_NAME_free);
assert(rc == 1);
X509_NAME_add_entry_by_txt(x509_name_ptr.get(), "C", MBSTRING_ASC, (const unsigned char*)szCountry, -1, -1, 0);
X509_NAME_add_entry_by_txt(x509_name_ptr.get(), "C", MBSTRING_ASC, (const unsigned char*)szCountry.c_str(), -1, -1,
0);
assert(rc == 1);
X509_NAME_add_entry_by_txt(x509_name_ptr.get(), "ST", MBSTRING_ASC, (const unsigned char*)szProvince, -1, -1, 0);
X509_NAME_add_entry_by_txt(x509_name_ptr.get(), "O", MBSTRING_ASC, (const unsigned char*)szOrganization.c_str(), -1,
-1, 0);
assert(rc == 1);
X509_NAME_add_entry_by_txt(x509_name_ptr.get(), "L", MBSTRING_ASC, (const unsigned char*)szCity, -1, -1, 0);
assert(rc == 1);
X509_NAME_add_entry_by_txt(x509_name_ptr.get(), "O", MBSTRING_ASC, (const unsigned char*)szOrganization, -1, -1, 0);
assert(rc == 1);
X509_NAME_add_entry_by_txt(x509_name_ptr.get(), "CN", MBSTRING_ASC, (const unsigned char*)szCommon, -1, -1, 0);
X509_NAME_add_entry_by_txt(x509_name_ptr.get(), "CN", MBSTRING_ASC, (const unsigned char*)szCommon.c_str(), -1, -1,
0);
assert(rc == 1);
X509_NAME_add_entry_by_txt(x509_name_ptr.get(), "DC", MBSTRING_ASC, (const unsigned char*)"CPO", -1, -1, 0);

// 5. set public key of x509 req
EVP_PKEY_assign_RSA(pKey.get(), r);
Expand Down
23 changes: 12 additions & 11 deletions lib/ocpp/v16/charge_point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1878,10 +1878,9 @@ void ChargePoint::sign_certificate(const ocpp::CertificateSigningUseEnum& certif

SignCertificateRequest req;

// TODO(piet): Fix csr data
std::string csr = this->pki_handler->generateCsr(certificate_signing_use, "DE", "BW", "Bad Schoenborn",
this->configuration->getCpoName().get().c_str(),
this->configuration->getChargeBoxSerialNumber().c_str());
const auto csr = this->pki_handler->generateCsr(
certificate_signing_use, this->configuration->getSeccLeafSubjectCountry().value_or("DE"),
this->configuration->getCpoName().get(), this->configuration->getChargeBoxSerialNumber());

req.csr = csr;
ocpp::Call<SignCertificateRequest> call(req, this->message_queue->createMessageId());
Expand Down Expand Up @@ -2425,10 +2424,11 @@ void ChargePoint::data_transfer_pnc_sign_certificate() {

ocpp::v201::SignCertificateRequest csr_req;

// TODO(piet): Fix csr data
const auto csr = this->pki_handler->generateCsr(ocpp::CertificateSigningUseEnum::V2GCertificate, "DE", "BW",
"Bad Schoenborn", this->configuration->getCpoName().get().c_str(),
this->configuration->getChargeBoxSerialNumber().c_str());
const auto csr = this->pki_handler->generateCsr(
ocpp::CertificateSigningUseEnum::V2GCertificate,
this->configuration->getSeccLeafSubjectCountry().value_or("DE"),
this->configuration->getSeccLeafSubjectOrganization().value_or(this->configuration->getCpoName().get()),
this->configuration->getSeccLeafSubjectCommonName().value_or(this->configuration->getChargeBoxSerialNumber()));
csr_req.csr = csr;
csr_req.certificateType = ocpp::v201::CertificateSigningUseEnum::V2GCertificate;
req.data.emplace(json(csr_req).dump());
Expand Down Expand Up @@ -2554,16 +2554,17 @@ void ChargePoint::handle_data_transfer_pnc_trigger_message(Call<DataTransferRequ

DataTransferResponse response;

if (this->configuration->getCpoName().has_value()) {
if (this->configuration->getCpoName().has_value() or
this->configuration->getSeccLeafSubjectOrganization().has_value()) {
response.status = DataTransferStatus::Accepted;
ocpp::v201::TriggerMessageResponse trigger_message_response;
trigger_message_response.status = ocpp::v201::TriggerMessageStatusEnum::Accepted;
response.data.emplace(json(trigger_message_response).dump());
} else {
EVLOG_warning << "Received Data Transfer TriggerMessage to trigger CSR but no "
"CpoName is set.";
"CpoName or SeccLeafSubjectOrganization is set.";
response.status = DataTransferStatus::Rejected;
response.data.emplace("No CpoName is set. Cannot trigger CSR");
response.data.emplace("No CpoName or SeccLeafSubjectOrganization is set. Cannot trigger CSR");
}

CallResult<DataTransferResponse> call_result(response, call.uniqueId);
Expand Down
113 changes: 113 additions & 0 deletions lib/ocpp/v16/charge_point_configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1544,6 +1544,119 @@ KeyValue ChargePointConfiguration::getOcspRequestIntervalKeyValue() {
return kv;
}

boost::optional<std::string> ChargePointConfiguration::getSeccLeafSubjectCommonName() {
boost::optional<std::string> secc_leaf_subject_common_name = boost::none;
if (this->config["Internal"].contains("SeccLeafSubjectCommonName")) {
secc_leaf_subject_common_name.emplace(this->config["Internal"]["SeccLeafSubjectCommonName"]);
}
return secc_leaf_subject_common_name;
}

void ChargePointConfiguration::setSeccLeafSubjectCommonName(const std::string& secc_leaf_subject_common_name) {
if (this->getSeccLeafSubjectCommonName() != boost::none) {
this->config["Internal"]["SeccLeafSubjectCommonName"] = secc_leaf_subject_common_name;
this->setInUserConfig("Internal", "SeccLeafSubjectCommonName", secc_leaf_subject_common_name);
}
}

boost::optional<KeyValue> ChargePointConfiguration::getSeccLeafSubjectCommonNameKeyValue() {
boost::optional<KeyValue> secc_leaf_subject_common_name_kv = boost::none;
auto secc_leaf_subject_common_name = this->getSeccLeafSubjectCommonName();
if (secc_leaf_subject_common_name != boost::none) {
KeyValue kv;
kv.key = "SeccLeafSubjectCommonName";
kv.readonly = false;
kv.value.emplace(secc_leaf_subject_common_name.value());
secc_leaf_subject_common_name_kv.emplace(kv);
}
return secc_leaf_subject_common_name_kv;
}


boost::optional<std::string> ChargePointConfiguration::getSeccLeafSubjectCountry() {
boost::optional<std::string> secc_leaf_subject_country = boost::none;
if (this->config["Internal"].contains("SeccLeafSubjectCountry")) {
secc_leaf_subject_country.emplace(this->config["Internal"]["SeccLeafSubjectCountry"]);
}
return secc_leaf_subject_country;
}

void ChargePointConfiguration::setSeccLeafSubjectCountry(const std::string& secc_leaf_subject_country) {
if (this->getSeccLeafSubjectCountry() != boost::none) {
this->config["Internal"]["SeccLeafSubjectCountry"] = secc_leaf_subject_country;
this->setInUserConfig("Internal", "SeccLeafSubjectCountry", secc_leaf_subject_country);
}
}

boost::optional<KeyValue> ChargePointConfiguration::getSeccLeafSubjectCountryKeyValue() {
boost::optional<KeyValue> secc_leaf_subject_country_kv = boost::none;
auto secc_leaf_subject_country = this->getSeccLeafSubjectCountry();
if (secc_leaf_subject_country != boost::none) {
KeyValue kv;
kv.key = "SeccLeafSubjectCountry";
kv.readonly = false;
kv.value.emplace(secc_leaf_subject_country.value());
secc_leaf_subject_country_kv.emplace(kv);
}
return secc_leaf_subject_country_kv;
}

boost::optional<std::string> ChargePointConfiguration::getSeccLeafSubjectOrganization() {
boost::optional<std::string> secc_leaf_subject_organization = boost::none;
if (this->config["Internal"].contains("SeccLeafSubjectOrganization")) {
secc_leaf_subject_organization.emplace(this->config["Internal"]["SeccLeafSubjectOrganization"]);
}
return secc_leaf_subject_organization;
}

void ChargePointConfiguration::setSeccLeafSubjectOrganization(const std::string& secc_leaf_subject_organization) {
if (this->getSeccLeafSubjectOrganization() != boost::none) {
this->config["Internal"]["SeccLeafSubjectOrganization"] = secc_leaf_subject_organization;
this->setInUserConfig("Internal", "SeccLeafSubjectOrganization", secc_leaf_subject_organization);
}
}

boost::optional<KeyValue> ChargePointConfiguration::getSeccLeafSubjectOrganizationKeyValue() {
boost::optional<KeyValue> secc_leaf_subject_organization_kv = boost::none;
auto secc_leaf_subject_organization = this->getSeccLeafSubjectOrganization();
if (secc_leaf_subject_organization != boost::none) {
KeyValue kv;
kv.key = "SeccLeafSubjectOrganization";
kv.readonly = false;
kv.value.emplace(secc_leaf_subject_organization.value());
secc_leaf_subject_organization_kv.emplace(kv);
}
return secc_leaf_subject_organization_kv;
}

boost::optional<std::string> ChargePointConfiguration::getConnectorEvseIds() {
boost::optional<std::string> connector_evse_ids = boost::none;
if (this->config["Internal"].contains("ConnectorEvseIds")) {
connector_evse_ids.emplace(this->config["Internal"]["ConnectorEvseIds"]);
}
return connector_evse_ids;
}

void ChargePointConfiguration::setConnectorEvseIds(const std::string& connector_evse_ids) {
if (this->getConnectorEvseIds() != boost::none) {
this->config["Internal"]["ConnectorEvseIds"] = connector_evse_ids;
this->setInUserConfig("Internal", "ConnectorEvseIds", connector_evse_ids);
}
}

boost::optional<KeyValue> ChargePointConfiguration::getConnectorEvseIdsKeyValue() {
boost::optional<KeyValue> connector_evse_ids_kv = boost::none;
auto connector_evse_ids = this->getConnectorEvseIds();
if (connector_evse_ids != boost::none) {
KeyValue kv;
kv.key = "ConnectorEvseIds";
kv.readonly = false;
kv.value.emplace(connector_evse_ids.value());
connector_evse_ids_kv.emplace(kv);
}
return connector_evse_ids_kv;
}

boost::optional<KeyValue> ChargePointConfiguration::get(CiString<50> key) {
// Core Profile
if (key == "AllowOfflineTxForUnknownId") {
Expand Down
2 changes: 1 addition & 1 deletion src/code_generator/generate_cpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def needs_enums(types):

def needs_types(types):
type_list = ['CiString<6>', 'CiString<8>', 'CiString<16>', 'CiString<20>', 'CiString<25>', 'CiString<32>', 'CiString<36>',
'CiString<50>', 'CiString<64>', 'CiString<255>', 'CiString<500>', 'CiString<1000>', 'CiString<2500>', 'CiString<5600>' 'DateTime']
'CiString<50>', 'CiString<64>', 'CiString<255>', 'CiString<500>', 'CiString<1000>', 'CiString<2500>', 'CiString<5600>', 'CiString<7500>', 'DateTime']
for t in types:
for property in t['properties']:
if property['type'] in type_list:
Expand Down

0 comments on commit bb1399c

Please sign in to comment.