@@ -597,75 +597,85 @@ EvseSecurity::get_installed_certificates(const std::vector<CertificateType>& cer
597597 // retrieve v2g certificate chain
598598 if (std::find (certificate_types.begin (), certificate_types.end (), CertificateType::V2GCertificateChain) !=
599599 certificate_types.end ()) {
600-
601- // Internal since we already acquired the lock
602- const auto secc_key_pair =
603- this ->get_leaf_certificate_info_internal (LeafCertificateType::V2G, EncodingFormat::PEM);
604- if (secc_key_pair.status == GetCertificateInfoStatus::Accepted) {
605- fs::path certificate_path;
606-
607- if (secc_key_pair.info .value ().certificate .has_value ())
608- certificate_path = secc_key_pair.info .value ().certificate .value ();
609- else
610- certificate_path = secc_key_pair.info .value ().certificate_single .value ();
611-
612- try {
613- // Leaf V2G chain, containing (SECCLeaf->SubCA2->SubCA1) or (SECCLeaf)
614- X509CertificateBundle leaf_bundle (certificate_path, EncodingFormat::PEM);
615-
616- // V2G chain, containing the certs from the V2G bundle/folder,
617- // containing (SubCA2->SubCA1->V2GRoot) or (V2GRoot)
618- const auto ca_bundle_path = this ->ca_bundle_path_map .at (CaCertificateType::V2G);
619- X509CertificateBundle ca_bundle (ca_bundle_path, EncodingFormat::PEM);
620-
621- // Merge the bundles, adding only uniques for full chain
622- // (SubCA2->SubCA1->V2GRoot->SECCLeaf) in any order
623- for (auto & certif : leaf_bundle.split ()) {
624- ca_bundle.add_certificate_unique (std::move (certif));
600+ // Retrieve all valid leaf certificates, we will return
601+ // multiple chains for each valid leaf that we find
602+ CertificateQueryParams params;
603+ params.certificate_type = LeafCertificateType::V2G;
604+ params.include_all_valid = true ;
605+ params.remove_duplicates = true ;
606+
607+ GetCertificateFullInfoResult secc_key_pairs = get_full_leaf_certificate_info_internal (params);
608+
609+ if (secc_key_pairs.status == GetCertificateInfoStatus::Accepted) {
610+ for (const auto & secc_key_pair : secc_key_pairs.info ) {
611+ fs::path certificate_path;
612+
613+ if (secc_key_pair.certificate .has_value ()) {
614+ certificate_path = secc_key_pair.certificate .value ();
615+ } else if (secc_key_pair.certificate_single .has_value ()) {
616+ certificate_path = secc_key_pair.certificate_single .value ();
617+ } else {
618+ throw std::runtime_error (" Leaf certificate single/bundle not present, should never happen!" );
625619 }
626620
627- // Create the proper certificate hierarchy since the bundle is not ordered
628- X509CertificateHierarchy& hierarchy = ca_bundle.get_certificate_hierarchy ();
629- EVLOG_debug << " Hierarchy:(V2GCertificateChain)\n " << hierarchy.to_debug_string ();
630-
631- for (auto & root : hierarchy.get_hierarchy ()) {
632- CertificateHashDataChain certificate_hash_data_chain;
633- certificate_hash_data_chain.certificate_type = CertificateType::V2GCertificateChain;
634-
635- // Since the hierarchy starts with V2G (Root) -> SubCa1->SubCa2 we have to reorder:
636- // them with the leaf first when returning to:
637- // * Leaf [index 0]
638- // --- SubCa2 [index 1]
639- // --- SubCa1 [index 2]
640- // --- --- V2GRoot [index 3]
641- std::vector<CertificateHashData> hierarchy_hash_data;
642-
643- // For each root's descendant, excluding the root
644- X509CertificateHierarchy::for_each_descendant (
645- [&](const X509Node& child, int depth) { hierarchy_hash_data.push_back (child.hash ); }, root);
646-
647- // Now the hierarchy_hash_data contains SubCA1->SubCA2->SECCLeaf,
648- // reverse order iteration to conform to the required leaf-first order
649- if (hierarchy_hash_data.size ()) {
650- bool first_leaf = true ;
651-
652- // Reverse iteration
653- for (auto it = hierarchy_hash_data.rbegin (); it != hierarchy_hash_data.rend (); ++it) {
654- if (first_leaf) {
655- // Leaf is the last
656- certificate_hash_data_chain.certificate_hash_data = *it;
657- first_leaf = false ;
658- } else {
659- certificate_hash_data_chain.child_certificate_hash_data .push_back (*it);
621+ try {
622+ // Leaf V2G chain, containing (SECCLeaf->SubCA2->SubCA1) or (SECCLeaf)
623+ X509CertificateBundle leaf_bundle (certificate_path, EncodingFormat::PEM);
624+
625+ // V2G chain, containing the certs from the V2G bundle/folder,
626+ // containing (SubCA2->SubCA1->V2GRoot) or (V2GRoot)
627+ const auto ca_bundle_path = this ->ca_bundle_path_map .at (CaCertificateType::V2G);
628+ X509CertificateBundle ca_bundle (ca_bundle_path, EncodingFormat::PEM);
629+
630+ // Merge the bundles, adding only uniques for full chain
631+ // (SubCA2->SubCA1->V2GRoot->SECCLeaf) in any order
632+ for (auto & certif : leaf_bundle.split ()) {
633+ ca_bundle.add_certificate_unique (std::move (certif));
634+ }
635+
636+ // Create the proper certificate hierarchy since the bundle is not ordered
637+ X509CertificateHierarchy& hierarchy = ca_bundle.get_certificate_hierarchy ();
638+ EVLOG_debug << " Hierarchy:(V2GCertificateChain)\n " << hierarchy.to_debug_string ();
639+
640+ for (auto & root : hierarchy.get_hierarchy ()) {
641+ CertificateHashDataChain certificate_hash_data_chain;
642+ certificate_hash_data_chain.certificate_type = CertificateType::V2GCertificateChain;
643+
644+ // Since the hierarchy starts with V2G (Root) -> SubCa1->SubCa2 we have to reorder:
645+ // them with the leaf first when returning to:
646+ // * Leaf [index 0]
647+ // --- SubCa2 [index 1]
648+ // --- SubCa1 [index 2]
649+ // --- --- V2GRoot [index 3]
650+ std::vector<CertificateHashData> hierarchy_hash_data;
651+
652+ // For each root's descendant, excluding the root
653+ X509CertificateHierarchy::for_each_descendant (
654+ [&](const X509Node& child, int depth) { hierarchy_hash_data.push_back (child.hash ); }, root);
655+
656+ // Now the hierarchy_hash_data contains SubCA1->SubCA2->SECCLeaf,
657+ // reverse order iteration to conform to the required leaf-first order
658+ if (hierarchy_hash_data.size ()) {
659+ bool first_leaf = true ;
660+
661+ // Reverse iteration
662+ for (auto it = hierarchy_hash_data.rbegin (); it != hierarchy_hash_data.rend (); ++it) {
663+ if (first_leaf) {
664+ // Leaf is the last
665+ certificate_hash_data_chain.certificate_hash_data = *it;
666+ first_leaf = false ;
667+ } else {
668+ certificate_hash_data_chain.child_certificate_hash_data .push_back (*it);
669+ }
660670 }
661- }
662671
663- // Add to our chains
664- certificate_chains.push_back (certificate_hash_data_chain);
672+ // Add to our chains
673+ certificate_chains.push_back (certificate_hash_data_chain);
674+ }
665675 }
676+ } catch (const CertificateLoadException& e) {
677+ EVLOG_error << " Could not load installed leaf certificates: " << e.what ();
666678 }
667- } catch (const CertificateLoadException& e) {
668- EVLOG_error << " Could not load installed leaf certificates: " << e.what ();
669679 }
670680 }
671681 }
@@ -1103,7 +1113,7 @@ GetCertificateFullInfoResult EvseSecurity::get_all_valid_certificates_info(LeafC
11031113 std::lock_guard<std::mutex> guard (EvseSecurity::security_mutex);
11041114
11051115 GetCertificateFullInfoResult result =
1106- get_full_leaf_certificate_info_internal (certificate_type, encoding, include_ocsp, true , true );
1116+ get_full_leaf_certificate_info_internal ({ certificate_type, encoding, include_ocsp, true , true } );
11071117
11081118 // If we failed, simply return the result
11091119 if (result.status != GetCertificateInfoStatus::Accepted) {
@@ -1149,7 +1159,7 @@ GetCertificateInfoResult EvseSecurity::get_leaf_certificate_info(LeafCertificate
11491159GetCertificateInfoResult EvseSecurity::get_leaf_certificate_info_internal (LeafCertificateType certificate_type,
11501160 EncodingFormat encoding, bool include_ocsp) {
11511161 GetCertificateFullInfoResult result =
1152- get_full_leaf_certificate_info_internal (certificate_type, encoding, include_ocsp, false , false );
1162+ get_full_leaf_certificate_info_internal ({ certificate_type, encoding, include_ocsp, false , false } );
11531163 GetCertificateInfoResult internal_result;
11541164
11551165 internal_result.status = result.status ;
@@ -1160,10 +1170,10 @@ GetCertificateInfoResult EvseSecurity::get_leaf_certificate_info_internal(LeafCe
11601170 return internal_result;
11611171}
11621172
1163- GetCertificateFullInfoResult EvseSecurity::get_full_leaf_certificate_info_internal (LeafCertificateType certificate_type,
1164- EncodingFormat encoding,
1165- bool include_ocsp, bool include_root,
1166- bool include_all_valid) {
1173+ GetCertificateFullInfoResult
1174+ EvseSecurity::get_full_leaf_certificate_info_internal ( const CertificateQueryParams& params) {
1175+ auto certificate_type = params. certificate_type ;
1176+
11671177 EVLOG_info << " Requesting leaf certificate info: "
11681178 << conversions::leaf_certificate_type_to_string (certificate_type);
11691179
@@ -1224,15 +1234,31 @@ GetCertificateFullInfoResult EvseSecurity::get_full_leaf_certificate_info_intern
12241234 // Found at least one valid key
12251235 any_valid_key = true ;
12261236
1227- // Copy to latest valid
12281237 KeyPairInternal key_pair{chain.at (0 ), priv_key_path};
1229- valid_leafs.emplace_back (std::move (key_pair));
1238+
1239+ if (params.remove_duplicates ) {
1240+ // Filter the already added certificates, since we can have a case
1241+ // when a leaf is present in 2 files (single/chain) that causes it
1242+ // to be added to the list twice by the bundle parser
1243+ auto it = std::find_if (valid_leafs.begin (), valid_leafs.end (),
1244+ [&key_pair](const auto & in_key_pair) {
1245+ return in_key_pair.certificate == key_pair.certificate ;
1246+ });
1247+
1248+ // None found
1249+ if (it == valid_leafs.end ()) {
1250+ valid_leafs.emplace_back (std::move (key_pair));
1251+ }
1252+ } else {
1253+ // Copy to latest valid
1254+ valid_leafs.emplace_back (std::move (key_pair));
1255+ }
12301256
12311257 // We found, break
12321258 EVLOG_info << " Found valid leaf: [" << chain.at (0 ).get_file ().value () << " ]" ;
12331259
12341260 // Collect all if we don't include valid only
1235- if (include_all_valid == false ) {
1261+ if (params. include_all_valid == false ) {
12361262 EVLOG_info << " Not requiring all valid leafs, returning" ;
12371263 return false ;
12381264 }
@@ -1329,15 +1355,15 @@ GetCertificateFullInfoResult EvseSecurity::get_full_leaf_certificate_info_intern
13291355 }
13301356
13311357 // Both require the hierarchy build
1332- if (include_ocsp || include_root) {
1358+ if (params. include_ocsp || params. include_root ) {
13331359 X509CertificateBundle root_bundle (root_dir, EncodingFormat::PEM); // Required for hierarchy
13341360
13351361 // The hierarchy is required for both roots and the OCSP cache
13361362 auto hierarchy = X509CertificateHierarchy::build_hierarchy (root_bundle.split (), leaf_directory.split ());
13371363 EVLOG_debug << " Hierarchy for root/OCSP data: \n " << hierarchy.to_debug_string ();
13381364
13391365 // Include OCSP data if possible
1340- if (include_ocsp) {
1366+ if (params. include_ocsp ) {
13411367 // Search for OCSP data for each certificate
13421368 if (leaf_fullchain != nullptr ) {
13431369 for (const auto & chain_certif : *leaf_fullchain) {
@@ -1361,7 +1387,7 @@ GetCertificateFullInfoResult EvseSecurity::get_full_leaf_certificate_info_intern
13611387 }
13621388
13631389 // Include root data if possible
1364- if (include_root) {
1390+ if (params. include_root ) {
13651391 // Search for the root of any of the leafs
13661392 // present either in the chain or single
13671393 try {
@@ -1386,11 +1412,11 @@ GetCertificateFullInfoResult EvseSecurity::get_full_leaf_certificate_info_intern
13861412 info.certificate_count = chain_len;
13871413 info.password = this ->private_key_password ;
13881414
1389- if (include_ocsp) {
1415+ if (params. include_ocsp ) {
13901416 info.ocsp = certificate_ocsp;
13911417 }
13921418
1393- if (include_root && leafs_root.has_value ()) {
1419+ if (params. include_root && leafs_root.has_value ()) {
13941420 info.certificate_root = leafs_root.value ();
13951421 }
13961422
0 commit comments