Authorities list: implement InstallAll & ReplaceAll

This change implements the design in
https://gerrit.openbmc-project.xyz/c/openbmc/docs/+/49317.

InstallAll: enumerate all certs in the input file and install all of
them;
ReplaceAll: replace all certs with the new authorities list
Atomic: implemented via creating temporary folder and issuing swap.

Added ability to unit test service reload as well.

Tested:
1. Unit tests
2. Tested loading/deleting authorities list in QEMU.

```
root@xxx:~# busctl call xyz.openbmc_project.Certs.Manager.Authority.Ldap \
> /xyz/openbmc_project/certs/authority/ldap \
> xyz.openbmc_project.Certs.InstallAll \
> InstallAll s /tmp/trust_bundle.pem
as 3 "/xyz/openbmc_project/certs/authority/ldap/1"
"/xyz/openbmc_project/certs/authority/ldap/2"
"/xyz/openbmc_project/certs/authority/ldap/3"
root@xxx:~# ls /etc/ssl/certs/authority/
10a5d8b0.0  5b49ceaa.0	f3ddaa86.0  file0qmgPV	fileDbjTzW  fileR4TtjO
trust_bundle
root@xxx:~# busctl call
xyz.openbmc_project.Certs.Manager.Authority.Ldap
/xyz/openbmc_project/certs/authority/ldap
xyz.openbmc_project.Certs.ReplaceAll ReplaceAll s /tmp/trust_bundle.pem
root@xxx:~# ls /etc/ssl/certs/authority/
10a5d8b0.0  5b49ceaa.0	f3ddaa86.0  file1obsEZ	fileOqVoaC  filerUBZCj
trust_bundle

root@xxx:~# wget -qO- http://localhost/redfish/v1/Managers/bmc/Truststore/Certificates/
{
  "@odata.id": "/redfish/v1/Managers/bmc/Truststore/Certificates/",
  "@odata.type": "#CertificateCollection.CertificateCollection",
  "Description": "A Collection of TrustStore certificate instances",
  "Members": [
    {
      "@odata.id": "/redfish/v1/Managers/bmc/Truststore/Certificates/1"
    },
    {
      "@odata.id": "/redfish/v1/Managers/bmc/Truststore/Certificates/2"
    },
    {
      "@odata.id": "/redfish/v1/Managers/bmc/Truststore/Certificates/3"
    }
  ],
  "Members@odata.count": 3,
  "Name": "TrustStore Certificates Collection"
}
root@xxx:~# wget -qO- http://localhost/redfish/v1/Managers/bmc/Truststore/Certificates/1
{
  "@odata.id": "/redfish/v1/Managers/bmc/Truststore/Certificates/1",
  "@odata.type": "#Certificate.v1_0_0.Certificate",
  "CertificateString": "-----BEGIN CERTIFICATE-----\nMIICZTCCAgugAwIBAgIUANIf0jvaRNq1MdwxrXPnk25VrmYwCgYIKoZIzj0EAwIw\nVTETMBEGA1UEChMKY2FtcHVzLWFzaDENMAsGA1UECxMEcm9vdDEvMC0GA1UEAwwm\ne2QyZWQ1MGJkLTczMTQtNDgxZC04OWE0LTVkMjkxMmYyMGQ5NH0wIBcNNzAwMTAx\nMDAwMDAwWhgPOTk5OTEyMzEyMzU5NTlaMFUxEzARBgNVBAoTCmNhbXB1cy1hc2gx\nDTALBgNVBAsTBHJvb3QxLzAtBgNVBAMMJntkMmVkNTBiZC03MzE0LTQ4MWQtODlh\nNC01ZDI5MTJmMjBkOTR9MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7lp/J3Gj\nc4TKubuYtzpxu2D3STlwTwEjgFbTaLZnQ0KXt7pBrcYc3yY1t74WBluvzM9iok6Q\nDcEFX5aIYcoaAKOBtjCBszAOBgNVHQ8BAf8EBAMCAQYwKQYDVR0lBCIwIAYIKwYB\nBQUHAwEGCCsGAQUFBwMCBgorBgEEAdZ5AgcBMA8GA1UdEwEB/wQFMAMBAf8wHQYD\nVR0OBBYEFIPrX7lbeJhvHHcQ7iYOry50aYKYMBcGA1UdIAQQMA4wDAYKKwYBBAHW\neQIFBDAtBgNVHR4BAf8EIzAhoB8wHYYbLmNhbXB1cy1hc2gucHJvZC5nb29nbGUu\nY29tMAoGCCqGSM49BAMCA0gAMEUCIAS/ZrMPBj992vVVplwzH9DWDCSMu1rCgvqw\nam3byOT1AiEAyrr3FAP+7js7z+h8d94hTyy1kTn+4NOvUWrVzHUmJI8=\n-----END CERTIFICATE-----\n",
  "Description": "TrustStore Certificate",
  "Id": "1",
  "Issuer": {
    "CommonName": "{d2ed50bd-7314-481d-89a4-5d2912f20d94}",
    "Organization": "campus-ash",
    "OrganizationalUnit": "root"
  },
  "KeyUsage": [
    "CRLSigning",
    "ServerAuthentication",
    "ClientAuthentication",
    ""
  ],
  "Name": "TrustStore Certificate",
  "Subject": {
    "CommonName": "{d2ed50bd-7314-481d-89a4-5d2912f20d94}",
    "Organization": "campus-ash",
    "OrganizationalUnit": "root"
  },
  "ValidNotAfter": "9999-12-31T23:59:59+00:00",
  "ValidNotBefore": "1970-01-01T00:00:00+00:00"
}
```

Signed-off-by: Nan Zhou <nanzhoumails@gmail.com>
Change-Id: I495f5c1c1c4a2ac880dd3233be31b84a78d79a43
diff --git a/certificate.cpp b/certificate.cpp
index 66ea698..419f626 100644
--- a/certificate.cpp
+++ b/certificate.cpp
@@ -84,15 +84,39 @@
     {NID_code_sign, "CodeSigning"}};
 
 /**
- * @brief Copies the certificate from sourceFilePath to installFilePath
+ * @brief Dumps the PEM encoded certificate to installFilePath
  *
- * @param[in] sourceFilePath - Path to the source file.
- * @param[in] sourceFilePath - Path to the destination file.
+ * @param[in] pem - PEM encoded X509 certificate buffer.
+ * @param[in] certFilePath - Path to the destination file.
  *
  * @return void
  */
-void copyCertificate(const std::string& certSrcFilePath,
-                     const std::string& certFilePath)
+
+void dumpCertificate(const std::string& pem, const std::string& certFilePath)
+{
+    std::ofstream outputCertFileStream;
+
+    outputCertFileStream.exceptions(
+        std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
+
+    try
+    {
+        outputCertFileStream.open(certFilePath, std::ios::out);
+        outputCertFileStream << pem << "\n" << std::flush;
+        outputCertFileStream.close();
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>("Failed to dump certificate", entry("ERR=%s", e.what()),
+                        entry("SRC_PEM=%s", pem.c_str()),
+                        entry("DST=%s", certFilePath.c_str()));
+        elog<InternalFailure>();
+    }
+}
+} // namespace
+
+void Certificate::copyCertificate(const std::string& certSrcFilePath,
+                                  const std::string& certFilePath)
 {
     // Copy the certificate to the installation path
     // During bootup will be parsing existing file so no need to
@@ -125,7 +149,6 @@
         }
     }
 }
-} // namespace
 
 std::string
     Certificate::generateUniqueFilePath(const std::string& directoryPath)
@@ -241,6 +264,24 @@
     this->emit_object_added();
 }
 
+Certificate::Certificate(sdbusplus::bus::bus& bus, const std::string& objPath,
+                         const CertificateType& type,
+                         const std::string& installPath, X509_STORE& x509Store,
+                         const std::string& pem, Watch* watchPtr,
+                         Manager& parent) :
+    internal::CertificateInterface(bus, objPath.c_str(), true),
+    objectPath(objPath), certType(type), certInstallPath(installPath),
+    certWatch(watchPtr), manager(parent)
+{
+    // Generate certificate file path
+    certFilePath = generateUniqueFilePath(installPath);
+
+    // install the certificate
+    install(x509Store, pem);
+
+    this->emit_object_added();
+}
+
 Certificate::~Certificate()
 {
     if (!fs::remove(certFilePath))
@@ -343,6 +384,45 @@
     }
 }
 
+void Certificate::install(X509_STORE& x509Store, const std::string& pem)
+{
+    log<level::INFO>("Certificate install ", entry("PEM_STR=%s", pem.data()));
+
+    if (certType != CertificateType::Authority)
+    {
+        log<level::ERR>("Bulk install error: Unsupported Type; only authority "
+                        "supports bulk install",
+                        entry("TYPE=%s", certificateTypeToString(certType)));
+        elog<InternalFailure>();
+    }
+
+    // stop watch for user initiated certificate install
+    if (certWatch)
+    {
+        certWatch->stopWatch();
+    }
+
+    // Load Certificate file into the X509 structure.
+    internal::X509Ptr cert = parseCert(pem);
+    // Perform validation; no type specific compare keys function
+    validateCertificateAgainstStore(x509Store, *cert);
+    validateCertificateStartDate(*cert);
+    validateCertificateInSSLContext(*cert);
+
+    // Copy the PEM to the installation path
+    dumpCertificate(pem, certFilePath);
+    storageUpdate();
+    // Keep certificate ID
+    certId = generateCertId(*cert);
+    // Parse the certificate file and populate properties
+    populateProperties(*cert);
+    // restart watch
+    if (certWatch)
+    {
+        certWatch->startWatch();
+    }
+}
+
 void Certificate::populateProperties()
 {
     internal::X509Ptr cert = loadCert(certInstallPath);
@@ -595,4 +675,25 @@
 {
     manager.deleteCertificate(this);
 }
+
+std::string Certificate::getObjectPath()
+{
+    return objectPath;
+}
+
+std::string Certificate::getCertFilePath()
+{
+    return certFilePath;
+}
+
+void Certificate::setCertFilePath(const std::string& path)
+{
+    certFilePath = path;
+}
+
+void Certificate::setCertInstallPath(const std::string& path)
+{
+    certInstallPath = path;
+}
+
 } // namespace phosphor::certs
diff --git a/certificate.hpp b/certificate.hpp
index fa3191a..4e90ea1 100644
--- a/certificate.hpp
+++ b/certificate.hpp
@@ -101,6 +101,25 @@
                 CertificateType type, const std::string& installPath,
                 const std::string& uploadPath, Watch* watch, Manager& parent);
 
+    /** @brief Constructor for the Certificate Object; a variant for authorities
+     * list install
+     *  @param[in] bus - Bus to attach to.
+     *  @param[in] objPath - Object path to attach to
+     *  @param[in] type - Type of the certificate
+     *  @param[in] installPath - Path of the certificate to install
+     *  @param[in] x509Store - an initialized X509 store used for certificate
+     * validation; Certificate object doesn't own it
+     *  @param[in] pem - Content of the certificate file to upload; it shall be
+     * a single PEM encoded x509 certificate
+     *  @param[in] watchPtr - watch on self signed certificate
+     *  @param[in] parent - Pointer to the manager which owns the constructed
+     * Certificate object
+     */
+    Certificate(sdbusplus::bus::bus& bus, const std::string& objPath,
+                const CertificateType& type, const std::string& installPath,
+                X509_STORE& x509Store, const std::string& pem, Watch* watchPtr,
+                Manager& parent);
+
     /** @brief Validate and Replace/Install the certificate file
      *  Install/Replace the existing certificate file with another
      *  (possibly CA signed) Certificate file.
@@ -108,6 +127,15 @@
      */
     void install(const std::string& filePath);
 
+    /** @brief Validate and Replace/Install the certificate file
+     *  Install/Replace the existing certificate file with another
+     *  (possibly CA signed) Certificate file.
+     *  @param[in] x509Store - an initialized X509 store used for certificate
+     * validation; Certificate object doesn't own it
+     *  @param[in] pem - a string buffer which stores a PEM encoded certificate.
+     */
+    void install(X509_STORE& x509Store, const std::string& pem);
+
     /** @brief Validate certificate and replace the existing certificate
      *  @param[in] filePath - Certificate file path.
      */
@@ -144,6 +172,44 @@
      */
     void delete_() override;
 
+    /**
+     * @brief Generate file name which is unique in the provided directory.
+     *
+     * @param[in] directoryPath - Directory path.
+     *
+     * @return File path.
+     */
+    static std::string generateUniqueFilePath(const std::string& directoryPath);
+
+    /**
+     * @brief Copies the certificate from sourceFilePath to installFilePath
+     *
+     * @param[in] sourceFilePath - Path to the source file.
+     * @param[in] certFilePath - Path to the destination file.
+     *
+     * @return void
+     */
+    static void copyCertificate(const std::string& certSrcFilePath,
+                                const std::string& certFilePath);
+
+    /**
+     * @brief Returns the associated dbus object path.
+     */
+    std::string getObjectPath();
+
+    /**
+     * @brief Returns the associated cert file path.
+     */
+    std::string getCertFilePath();
+
+    /** @brief: Set the data member |certFilePath| to |path|
+     */
+    void setCertFilePath(const std::string& path);
+
+    /** @brief: Set the data member |certInstallPath| to |path|
+     */
+    void setCertInstallPath(const std::string& path);
+
   private:
     /**
      * @brief Populate certificate properties by parsing given certificate
@@ -173,15 +239,6 @@
     bool compareKeys(const std::string& filePath);
 
     /**
-     * @brief Generate file name which is unique in the provided directory.
-     *
-     * @param[in] directoryPath - Directory path.
-     *
-     * @return File path.
-     */
-    std::string generateUniqueFilePath(const std::string& directoryPath);
-
-    /**
      * @brief Generate authority certificate file path corresponding with
      * OpenSSL requirements.
      *
diff --git a/certs_manager.cpp b/certs_manager.cpp
index 831928f..d8e99b7 100644
--- a/certs_manager.cpp
+++ b/certs_manager.cpp
@@ -2,6 +2,8 @@
 
 #include "certs_manager.hpp"
 
+#include "x509_utils.hpp"
+
 #include <openssl/asn1.h>
 #include <openssl/bn.h>
 #include <openssl/ec.h>
@@ -22,6 +24,7 @@
 #include <cstdlib>
 #include <cstring>
 #include <exception>
+#include <fstream>
 #include <phosphor-logging/elog-errors.hpp>
 #include <phosphor-logging/elog.hpp>
 #include <phosphor-logging/log.hpp>
@@ -61,11 +64,70 @@
 using X509ReqPtr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>;
 using EVPPkeyPtr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
 using BignumPtr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
+using X509StorePtr = std::unique_ptr<X509_STORE, decltype(&::X509_STORE_free)>;
 
 constexpr int supportedKeyBitLength = 2048;
 constexpr int defaultKeyBitLength = 2048;
 // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
 constexpr auto defaultKeyCurveID = "secp224r1";
+// PEM certificate block markers, defined in go/rfc/7468.
+constexpr std::string_view beginCertificate = "-----BEGIN CERTIFICATE-----";
+constexpr std::string_view endCertificate = "-----END CERTIFICATE-----";
+
+/**
+ * @brief Splits the given authorities list file and returns an array of
+ * individual PEM encoded x509 certificate.
+ *
+ * @param[in] sourceFilePath - Path to the authorities list file.
+ *
+ * @return An array of individual PEM encoded x509 certificate
+ */
+std::vector<std::string> splitCertificates(const std::string& sourceFilePath)
+{
+    std::ifstream inputCertFileStream;
+    inputCertFileStream.exceptions(
+        std::ifstream::failbit | std::ifstream::badbit | std::ifstream::eofbit);
+
+    std::stringstream pemStream;
+    std::vector<std::string> certificatesList;
+    try
+    {
+        inputCertFileStream.open(sourceFilePath);
+        pemStream << inputCertFileStream.rdbuf();
+        inputCertFileStream.close();
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>("Failed to read certificates list",
+                        entry("ERR=%s", e.what()),
+                        entry("SRC=%s", sourceFilePath.c_str()));
+        elog<InternalFailure>();
+    }
+    std::string pem = pemStream.str();
+    size_t begin = 0;
+    // |begin| points to the current start position for searching the next
+    // |beginCertificate| block. When we find the beginning of the certificate,
+    // we extract the content between the beginning and the end of the current
+    // certificate. And finally we move |begin| to the end of the current
+    // certificate to start searching the next potential certificate.
+    for (begin = pem.find(beginCertificate, begin); begin != std::string::npos;
+         begin = pem.find(beginCertificate, begin))
+    {
+        size_t end = pem.find(endCertificate, begin);
+        if (end == std::string::npos)
+        {
+            log<level::ERR>(
+                "invalid PEM contains a BEGIN identifier without an END");
+            elog<InvalidCertificate>(InvalidCertificateReason(
+                "invalid PEM contains a BEGIN identifier without an END"));
+        }
+        end += endCertificate.size();
+        certificatesList.emplace_back(pem.substr(begin, end - begin));
+        begin = end;
+    }
+    return certificatesList;
+}
+
 } // namespace
 
 Manager::Manager(sdbusplus::bus::bus& bus, sdeventplus::Event& event,
@@ -217,6 +279,100 @@
     return certObjectPath;
 }
 
+std::vector<sdbusplus::message::object_path>
+    Manager::installAll(const std::string filePath)
+{
+    if (certType != CertificateType::Authority)
+    {
+        elog<NotAllowed>(
+            NotAllowedReason("The InstallAll interface is only allowed for "
+                             "Authority certificates"));
+    }
+
+    if (!installedCerts.empty())
+    {
+        elog<NotAllowed>(NotAllowedReason(
+            "There are already root certificates; Call DeleteAll then "
+            "InstallAll, or use ReplaceAll"));
+    }
+
+    fs::path sourceFile(filePath);
+    if (!fs::exists(sourceFile))
+    {
+        log<level::ERR>("File is Missing", entry("FILE=%s", filePath.c_str()));
+        elog<InternalFailure>();
+    }
+    std::vector<std::string> authorities = splitCertificates(sourceFile);
+    if (authorities.size() > maxNumAuthorityCertificates)
+    {
+        elog<NotAllowed>(NotAllowedReason("Certificates limit reached"));
+    }
+
+    fs::path authorityStore(certInstallPath);
+    fs::path authoritiesListFile =
+        authorityStore / defaultAuthoritiesListFileName;
+
+    // Atomically install all the certificates
+    fs::path tempPath = Certificate::generateUniqueFilePath(authorityStore);
+    fs::create_directory(tempPath);
+    // Copies the authorities list
+    Certificate::copyCertificate(sourceFile,
+                                 tempPath / defaultAuthoritiesListFileName);
+    std::vector<std::unique_ptr<Certificate>> tempCertificates;
+    uint64_t tempCertIdCounter = certIdCounter;
+    X509StorePtr x509Store = getX509Store(sourceFile);
+    for (const auto& authority : authorities)
+    {
+        std::string certObjectPath =
+            objectPath + '/' + std::to_string(tempCertIdCounter);
+        tempCertificates.emplace_back(std::make_unique<Certificate>(
+            bus, certObjectPath, certType, tempPath, *x509Store, authority,
+            certWatchPtr.get(), *this));
+        tempCertIdCounter++;
+    }
+
+    // We are good now, issue swap
+    installedCerts = std::move(tempCertificates);
+    certIdCounter = tempCertIdCounter;
+    // Rename all the certificates including the authorities list
+    for (const fs::path& f : fs::directory_iterator(tempPath))
+    {
+        if (fs::is_symlink(f))
+        {
+            continue;
+        }
+        fs::rename(/*from=*/f, /*to=*/certInstallPath / f.filename());
+    }
+    // Update file locations and create symbol links
+    for (const auto& cert : installedCerts)
+    {
+        cert->setCertInstallPath(certInstallPath);
+        cert->setCertFilePath(certInstallPath /
+                              fs::path(cert->getCertFilePath()).filename());
+        cert->storageUpdate();
+    }
+    // Remove the temporary folder
+    fs::remove_all(tempPath);
+
+    std::vector<sdbusplus::message::object_path> objects;
+    for (const auto& certificate : installedCerts)
+    {
+        objects.emplace_back(certificate->getObjectPath());
+    }
+
+    reloadOrReset(unitToRestart);
+    return objects;
+}
+
+std::vector<sdbusplus::message::object_path>
+    Manager::replaceAll(std::string filePath)
+{
+    installedCerts.clear();
+    certIdCounter = 1;
+    storageUpdate();
+    return installAll(std::move(filePath));
+}
+
 void Manager::deleteAll()
 {
     // TODO: #Issue 4 when a certificate is deleted system auto generates
@@ -225,6 +381,17 @@
     // deletion if only applicable for REST server and Bmcweb does not allow
     // deletion of certificates
     installedCerts.clear();
+    // If the authorities list exists, delete it as well
+    if (certType == CertificateType::Authority)
+    {
+        if (fs::path authoritiesList =
+                fs::path(certInstallPath) / defaultAuthoritiesListFileName;
+            fs::exists(authoritiesList))
+        {
+            fs::remove(authoritiesList);
+        }
+    }
+    certIdCounter = 1;
     storageUpdate();
     reloadOrReset(unitToRestart);
 }
@@ -757,6 +924,22 @@
             log<level::ERR>("Certificate installation path exists and it is "
                             "not a directory");
             elog<InternalFailure>();
+        }
+
+        // If the authorities list exists, recover from it and return
+        if (fs::path authoritiesListFilePath =
+                fs::path(certInstallPath) / defaultAuthoritiesListFileName;
+            fs::exists(authoritiesListFilePath))
+        {
+            // remove all other files and directories
+            for (auto& path : fs::directory_iterator(certInstallPath))
+            {
+                if (path.path() != authoritiesListFilePath)
+                {
+                    fs::remove_all(path);
+                }
+            }
+            installAll(authoritiesListFilePath);
             return;
         }
 
diff --git a/certs_manager.hpp b/certs_manager.hpp
index 89887f7..fe35a3f 100644
--- a/certs_manager.hpp
+++ b/certs_manager.hpp
@@ -18,6 +18,8 @@
 #include <vector>
 #include <xyz/openbmc_project/Certs/CSR/Create/server.hpp>
 #include <xyz/openbmc_project/Certs/Install/server.hpp>
+#include <xyz/openbmc_project/Certs/InstallAll/server.hpp>
+#include <xyz/openbmc_project/Certs/ReplaceAll/server.hpp>
 #include <xyz/openbmc_project/Collection/DeleteAll/server.hpp>
 
 namespace phosphor::certs
@@ -28,7 +30,9 @@
 using ManagerInterface = sdbusplus::server::object_t<
     sdbusplus::xyz::openbmc_project::Certs::server::Install,
     sdbusplus::xyz::openbmc_project::Certs::CSR::server::Create,
-    sdbusplus::xyz::openbmc_project::Collection::server::DeleteAll>;
+    sdbusplus::xyz::openbmc_project::Collection::server::DeleteAll,
+    sdbusplus::xyz::openbmc_project::Certs::server::InstallAll,
+    sdbusplus::xyz::openbmc_project::Certs::server::ReplaceAll>;
 }
 
 class Manager : public internal::ManagerInterface
@@ -73,6 +77,27 @@
      */
     std::string install(const std::string filePath) override;
 
+    /** @brief Implementation for InstallAll
+     *  Install the authority list and restart the associated services.
+     *
+     *  @param[in] path - Path of the file that contains a list of root
+     * certificates.
+     *
+     *  @return D-Bus object path to created objects.
+     */
+    std::vector<sdbusplus::message::object_path>
+        installAll(std::string path) override;
+
+    /** @brief Implementation for ReplaceAll
+     *  Replace the current authority lists and restart the associated services.
+     *
+     *  @param[in] path - Path of file that contains multiple root certificates.
+     *
+     *  @return D-Bus object path to created objects.
+     */
+    std::vector<sdbusplus::message::object_path>
+        replaceAll(std::string filePath) override;
+
     /** @brief Implementation for DeleteAll
      *  Delete all objects in the collection.
      */
@@ -175,6 +200,12 @@
      */
     std::vector<std::unique_ptr<Certificate>>& getCertificates();
 
+    /** @brief Systemd unit reload or reset helper function
+     *  Reload if the unit supports it and use a restart otherwise.
+     *  @param[in] unit - service need to reload.
+     */
+    virtual void reloadOrReset(const std::string& unit);
+
   private:
     void generateCSRHelper(std::vector<std::string> alternativeNames,
                            std::string challengePassword, std::string city,
@@ -262,12 +293,6 @@
      */
     void storageUpdate();
 
-    /** @brief Systemd unit reload or reset helper function
-     *  Reload if the unit supports it and use a restart otherwise.
-     *  @param[in] unit - service need to reload.
-     */
-    void reloadOrReset(const std::string& unit);
-
     /** @brief Check if provided certificate is unique across all certificates
      * on the internal list.
      *  @param[in] certFilePath - Path to the file with certificate for
diff --git a/config.h.in b/config.h.in
index 6e6a3e7..b363fd3 100644
--- a/config.h.in
+++ b/config.h.in
@@ -18,3 +18,6 @@
 
 /* The maximum number of Authority certificates the service allows. */
 inline constexpr size_t maxNumAuthorityCertificates = @authority_limit@;
+
+/* The default name of the authorities list file. */
+inline constexpr char defaultAuthoritiesListFileName[] = "@authorities_list_name@";
diff --git a/meson.build b/meson.build
index 68c56c6..c647bc8 100644
--- a/meson.build
+++ b/meson.build
@@ -43,6 +43,10 @@
     'authority_limit',
      get_option('authority-limit')
 )
+config_data.set(
+    'authorities_list_name',
+     get_option('authorities-list-name')
+)
 
 configure_file(
     input: 'config.h.in',
diff --git a/meson_options.txt b/meson_options.txt
index c08b9eb..18190e3 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -20,3 +20,9 @@
     type: 'feature',
     description: 'Install nslcd authority cert configs',
 )
+
+option('authorities-list-name',
+    type: 'string',
+    value: 'trust_bundle',
+    description: 'File name of the authorities list',
+)
diff --git a/test/certs_manager_test.cpp b/test/certs_manager_test.cpp
index 6e4c37f..b3a1563 100644
--- a/test/certs_manager_test.cpp
+++ b/test/certs_manager_test.cpp
@@ -23,11 +23,13 @@
 #include <sdbusplus/bus.hpp>
 #include <sdeventplus/event.hpp>
 #include <string>
+#include <unordered_set>
 #include <utility>
 #include <vector>
 #include <xyz/openbmc_project/Certs/error.hpp>
 #include <xyz/openbmc_project/Common/error.hpp>
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 namespace phosphor::certs
@@ -37,6 +39,31 @@
 namespace fs = std::filesystem;
 using ::sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
 using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+using ::testing::Eq;
+using ::testing::Return;
+// Compares two files; returns true only if the two are the same
+bool compareFiles(const std::string& file1, const std::string& file2)
+{
+    std::ifstream f1(file1, std::ifstream::binary | std::ifstream::ate);
+    std::ifstream f2(file2, std::ifstream::binary | std::ifstream::ate);
+
+    if (f1.fail() || f2.fail())
+    {
+        return false; // file problem
+    }
+
+    if (f1.tellg() != f2.tellg())
+    {
+        return false; // size mismatch
+    }
+
+    // seek back to beginning and use std::equal to compare contents
+    f1.seekg(0, std::ifstream::beg);
+    f2.seekg(0, std::ifstream::beg);
+    return std::equal(std::istreambuf_iterator<char>(f1.rdbuf()),
+                      std::istreambuf_iterator<char>(),
+                      std::istreambuf_iterator<char>(f2.rdbuf()));
+}
 
 /**
  * Class to generate certificate file and test verification of certificate file
@@ -228,23 +255,39 @@
     phosphor::certs::CSR* csr_;
 };
 
+class ManagerInTest : public phosphor::certs::Manager
+{
+  public:
+    static constexpr std::string_view unitToRestartInTest =
+        "xyz.openbmc_project.awesome-service";
+    ManagerInTest(sdbusplus::bus::bus& bus, sdeventplus::Event& event,
+                  const char* path, CertificateType type,
+                  const std::string& unit, const std::string& installPath) :
+        Manager(bus, event, path, type, unit, installPath)
+    {
+    }
+
+    MOCK_METHOD(void, reloadOrReset, (const std::string&), (override));
+};
+
 /** @brief Check if server install routine is invoked for server setup
  */
 TEST_F(TestCertificates, InvokeServerInstall)
 {
     std::string endpoint("https");
-    std::string unit;
     CertificateType type = CertificateType::Server;
     std::string installPath(certDir + "/" + certificateFile);
     std::string verifyPath(installPath);
-    std::string verifyUnit(unit);
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
     auto objPath = std::string(objectNamePrefix) + '/' +
                    certificateTypeToString(type) + '/' + endpoint;
     auto event = sdeventplus::Event::get_default();
     // Attach the bus to sd_event to service user requests
     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
-    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
-                    std::move(installPath));
+    ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+                          installPath);
+    EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+        .WillOnce(Return());
     MainApp mainApp(&manager);
     mainApp.install(certificateFile);
     EXPECT_TRUE(fs::exists(verifyPath));
@@ -255,18 +298,19 @@
 TEST_F(TestCertificates, InvokeClientInstall)
 {
     std::string endpoint("ldap");
-    std::string unit;
     CertificateType type = CertificateType::Server;
     std::string installPath(certDir + "/" + certificateFile);
     std::string verifyPath(installPath);
-    std::string verifyUnit(unit);
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
     auto objPath = std::string(objectNamePrefix) + '/' +
                    certificateTypeToString(type) + '/' + endpoint;
     auto event = sdeventplus::Event::get_default();
     // Attach the bus to sd_event to service user requests
     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
-    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
-                    std::move(installPath));
+    ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+                          installPath);
+    EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+        .WillOnce(Return());
     MainApp mainApp(&manager);
     mainApp.install(certificateFile);
     EXPECT_TRUE(fs::exists(verifyPath));
@@ -277,17 +321,18 @@
 TEST_F(TestCertificates, InvokeAuthorityInstall)
 {
     std::string endpoint("ldap");
-    std::string unit;
     CertificateType type = CertificateType::Authority;
     std::string verifyDir(certDir);
-    std::string verifyUnit(unit);
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
     auto objPath = std::string(objectNamePrefix) + '/' +
                    certificateTypeToString(type) + '/' + endpoint;
     auto event = sdeventplus::Event::get_default();
     // Attach the bus to sd_event to service user requests
     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
-    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
-                    std::move(certDir));
+    ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+                          verifyDir);
+    EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+        .WillOnce(Return());
     MainApp mainApp(&manager);
     // install the default certificate that's valid from today to 100 years
     // later
@@ -319,17 +364,18 @@
 TEST_F(TestCertificates, InvokeAuthorityInstallNeverExpiredRootCert)
 {
     std::string endpoint("ldap");
-    std::string unit;
     CertificateType type = CertificateType::Authority;
     std::string verifyDir(certDir);
-    std::string verifyUnit(unit);
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
     auto objPath = std::string(objectNamePrefix) + '/' +
                    certificateTypeToString(type) + '/' + endpoint;
     auto event = sdeventplus::Event::get_default();
     // Attach the bus to sd_event to service user requests
     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
-    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
-                    std::move(certDir));
+    ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+                          certDir);
+    EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+        .WillOnce(Return());
     MainApp mainApp(&manager);
 
     // install the certificate that's valid from the Unix Epoch to Dec 31, 9999
@@ -359,17 +405,18 @@
 TEST_F(TestCertificates, InvokeInstallSameCertTwice)
 {
     std::string endpoint("ldap");
-    std::string unit;
     CertificateType type = CertificateType::Authority;
     std::string verifyDir(certDir);
-    std::string verifyUnit(unit);
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
     auto objPath = std::string(objectNamePrefix) + '/' +
                    certificateTypeToString(type) + '/' + endpoint;
     auto event = sdeventplus::Event::get_default();
     // Attach the bus to sd_event to service user requests
     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
-    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
-                    std::move(certDir));
+    ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+                          std::move(certDir));
+    EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+        .WillOnce(Return());
     MainApp mainApp(&manager);
     mainApp.install(certificateFile);
 
@@ -414,17 +461,19 @@
 TEST_F(TestCertificates, InvokeInstallSameSubjectTwice)
 {
     std::string endpoint("ldap");
-    std::string unit;
     CertificateType type = CertificateType::Authority;
     std::string verifyDir(certDir);
-    std::string verifyUnit(unit);
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
     auto objPath = std::string(objectNamePrefix) + '/' +
                    certificateTypeToString(type) + '/' + endpoint;
     auto event = sdeventplus::Event::get_default();
     // Attach the bus to sd_event to service user requests
     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
-    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
-                    std::move(certDir));
+    ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+                          certDir);
+    EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+        .WillOnce(Return())
+        .WillOnce(Return());
     MainApp mainApp(&manager);
     mainApp.install(certificateFile);
 
@@ -470,17 +519,18 @@
 TEST_F(TestCertificates, InvokeInstallAuthCertLimit)
 {
     std::string endpoint("ldap");
-    std::string unit;
     CertificateType type = CertificateType::Authority;
     std::string verifyDir(certDir);
-    std::string verifyUnit(unit);
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
     auto objPath = std::string(objectNamePrefix) + '/' +
                    certificateTypeToString(type) + '/' + endpoint;
     auto event = sdeventplus::Event::get_default();
     // Attach the bus to sd_event to service user requests
     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
-    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
-                    std::move(certDir));
+    ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+                          certDir);
+    EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+        .WillRepeatedly(Return());
     MainApp mainApp(&manager);
 
     std::vector<std::unique_ptr<Certificate>>& certs =
@@ -545,19 +595,19 @@
 TEST_F(TestCertificates, CompareInstalledCertificate)
 {
     std::string endpoint("ldap");
-    std::string unit;
     CertificateType type = CertificateType::Client;
-    ;
     std::string installPath(certDir + "/" + certificateFile);
     std::string verifyPath(installPath);
-    std::string verifyUnit(unit);
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
     auto objPath = std::string(objectNamePrefix) + '/' +
                    certificateTypeToString(type) + '/' + endpoint;
     auto event = sdeventplus::Event::get_default();
     // Attach the bus to sd_event to service user requests
     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
-    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
-                    std::move(installPath));
+    ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+                          installPath);
+    EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+        .WillOnce(Return());
     MainApp mainApp(&manager);
     mainApp.install(certificateFile);
     EXPECT_TRUE(fs::exists(verifyPath));
@@ -569,12 +619,10 @@
 TEST_F(TestCertificates, TestNoCertificateFile)
 {
     std::string endpoint("ldap");
-    std::string unit;
     CertificateType type = CertificateType::Client;
-    ;
     std::string installPath(certDir + "/" + certificateFile);
     std::string verifyPath(installPath);
-    std::string verifyUnit(unit);
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
     auto objPath = std::string(objectNamePrefix) + '/' +
                    certificateTypeToString(type) + '/' + endpoint;
     std::string uploadFile = "nofile.pem";
@@ -585,8 +633,8 @@
                 auto event = sdeventplus::Event::get_default();
                 // Attach the bus to sd_event to service user requests
                 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
-                Manager manager(bus, event, objPath.c_str(), type,
-                                std::move(unit), std::move(installPath));
+                ManagerInTest manager(bus, event, objPath.c_str(), type,
+                                      verifyUnit, installPath);
                 MainApp mainApp(&manager);
                 mainApp.install(uploadFile);
             }
@@ -604,17 +652,20 @@
 TEST_F(TestCertificates, TestReplaceCertificate)
 {
     std::string endpoint("ldap");
-    std::string unit;
     CertificateType type = CertificateType::Server;
     std::string installPath(certDir + "/" + certificateFile);
     std::string verifyPath(installPath);
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
     auto objPath = std::string(objectNamePrefix) + '/' +
                    certificateTypeToString(type) + '/' + endpoint;
     auto event = sdeventplus::Event::get_default();
     // Attach the bus to sd_event to service user requests
     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
-    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
-                    std::move(installPath));
+    ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+                          std::move(installPath));
+    EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+        .WillOnce(Return())
+        .WillOnce(Return());
     MainApp mainApp(&manager);
     mainApp.install(certificateFile);
     EXPECT_TRUE(fs::exists(verifyPath));
@@ -631,23 +682,25 @@
 TEST_F(TestCertificates, TestAuthorityReplaceCertificate)
 {
     std::string endpoint("ldap");
-    std::string unit;
     CertificateType type = CertificateType::Authority;
     std::string verifyDir(certDir);
-    std::string verifyUnit(unit);
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
     auto objPath = std::string(objectNamePrefix) + '/' +
                    certificateTypeToString(type) + '/' + endpoint;
     auto event = sdeventplus::Event::get_default();
     // Attach the bus to sd_event to service user requests
     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
-    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
-                    std::move(certDir));
+    ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+                          certDir);
+    constexpr const unsigned int REPLACE_ITERATIONS = 10;
+    EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+        .Times(REPLACE_ITERATIONS + 1)
+        .WillRepeatedly(Return());
     MainApp mainApp(&manager);
     mainApp.install(certificateFile);
 
     std::vector<std::unique_ptr<Certificate>>& certs =
         manager.getCertificates();
-    constexpr const unsigned int REPLACE_ITERATIONS = 10;
 
     for (unsigned int i = 0; i < REPLACE_ITERATIONS; i++)
     {
@@ -679,17 +732,18 @@
 TEST_F(TestCertificates, TestStorageDeleteCertificate)
 {
     std::string endpoint("ldap");
-    std::string unit;
     CertificateType type = CertificateType::Authority;
     std::string verifyDir(certDir);
-    std::string verifyUnit(unit);
+    std::string verifyUnit((ManagerInTest::unitToRestartInTest));
     auto objPath = std::string(objectNamePrefix) + '/' +
                    certificateTypeToString(type) + '/' + endpoint;
     auto event = sdeventplus::Event::get_default();
     // Attach the bus to sd_event to service user requests
     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
-    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
-                    std::move(certDir));
+    ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+                          certDir);
+    EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+        .WillRepeatedly(Return());
     MainApp mainApp(&manager);
 
     // Check if certificate placeholder dir is empty
@@ -731,12 +785,10 @@
 TEST_F(TestCertificates, TestEmptyCertificateFile)
 {
     std::string endpoint("ldap");
-    std::string unit;
     CertificateType type = CertificateType::Client;
-    ;
     std::string installPath(certDir + "/" + certificateFile);
     std::string verifyPath(installPath);
-    std::string verifyUnit(unit);
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
     auto objPath = std::string(objectNamePrefix) + '/' +
                    certificateTypeToString(type) + '/' + endpoint;
     std::string emptyFile("emptycert.pem");
@@ -750,8 +802,8 @@
                 auto event = sdeventplus::Event::get_default();
                 // Attach the bus to sd_event to service user requests
                 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
-                Manager manager(bus, event, objPath.c_str(), type,
-                                std::move(unit), std::move(installPath));
+                ManagerInTest manager(bus, event, objPath.c_str(), type,
+                                      verifyUnit, installPath);
                 MainApp mainApp(&manager);
                 mainApp.install(emptyFile);
             }
@@ -770,9 +822,7 @@
 TEST_F(TestCertificates, TestInvalidCertificateFile)
 {
     std::string endpoint("ldap");
-    std::string unit;
     CertificateType type = CertificateType::Client;
-    ;
 
     std::ofstream ofs;
     ofs.open(certificateFile, std::ofstream::out);
@@ -783,7 +833,7 @@
 
     std::string installPath(certDir + "/" + certificateFile);
     std::string verifyPath(installPath);
-    std::string verifyUnit(unit);
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
     auto objPath = std::string(objectNamePrefix) + '/' +
                    certificateTypeToString(type) + '/' + endpoint;
     EXPECT_THROW(
@@ -793,8 +843,8 @@
                 auto event = sdeventplus::Event::get_default();
                 // Attach the bus to sd_event to service user requests
                 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
-                Manager manager(bus, event, objPath.c_str(), type,
-                                std::move(unit), std::move(installPath));
+                ManagerInTest manager(bus, event, objPath.c_str(), type,
+                                      verifyUnit, installPath);
                 MainApp mainApp(&manager);
                 mainApp.install(certificateFile);
             }
@@ -859,12 +909,10 @@
 TEST_F(TestInvalidCertificate, TestMissingPrivateKey)
 {
     std::string endpoint("ldap");
-    std::string unit;
     CertificateType type = CertificateType::Client;
-    ;
     std::string installPath(certDir + "/" + certificateFile);
     std::string verifyPath(installPath);
-    std::string verifyUnit(unit);
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
     auto objPath = std::string(objectNamePrefix) + '/' +
                    certificateTypeToString(type) + '/' + endpoint;
     EXPECT_THROW(
@@ -874,8 +922,8 @@
                 auto event = sdeventplus::Event::get_default();
                 // Attach the bus to sd_event to service user requests
                 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
-                Manager manager(bus, event, objPath.c_str(), type,
-                                std::move(unit), std::move(installPath));
+                ManagerInTest manager(bus, event, objPath.c_str(), type,
+                                      verifyUnit, installPath);
                 MainApp mainApp(&manager);
                 mainApp.install(certificateFile);
             }
@@ -893,12 +941,10 @@
 TEST_F(TestInvalidCertificate, TestMissingCeritificate)
 {
     std::string endpoint("ldap");
-    std::string unit;
     CertificateType type = CertificateType::Client;
-    ;
     std::string installPath(certDir + "/" + keyFile);
     std::string verifyPath(installPath);
-    std::string verifyUnit(unit);
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
     auto objPath = std::string(objectNamePrefix) + '/' +
                    certificateTypeToString(type) + '/' + endpoint;
     EXPECT_THROW(
@@ -908,8 +954,8 @@
                 auto event = sdeventplus::Event::get_default();
                 // Attach the bus to sd_event to service user requests
                 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
-                Manager manager(bus, event, objPath.c_str(), type,
-                                std::move(unit), std::move(installPath));
+                ManagerInTest manager(bus, event, objPath.c_str(), type,
+                                      verifyUnit, installPath);
                 MainApp mainApp(&manager);
                 mainApp.install(keyFile);
             }
@@ -930,18 +976,17 @@
     using NotAllowed =
         sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
     std::string endpoint("ldap");
-    std::string unit;
     CertificateType type = CertificateType::Client;
-    ;
     std::string installPath(certDir + "/" + certificateFile);
     std::string verifyPath(installPath);
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
     auto objPath = std::string(objectNamePrefix) + '/' +
                    certificateTypeToString(type) + '/' + endpoint;
     auto event = sdeventplus::Event::get_default();
     // Attach the bus to sd_event to service user requests
     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
-    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
-                    std::move(installPath));
+    ManagerInTest manager(bus, event, objPath.c_str(), type, verifyUnit,
+                          installPath);
     MainApp mainApp(&manager);
     mainApp.install(certificateFile);
     EXPECT_TRUE(fs::exists(verifyPath));
@@ -1413,17 +1458,407 @@
 TEST_F(TestCertificates, TestGenerateRSAPrivateKeyFile)
 {
     std::string endpoint("https");
-    std::string unit;
     CertificateType type = CertificateType::Server;
     std::string installPath(certDir + "/" + certificateFile);
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
     auto objPath = std::string(objectNamePrefix) + '/' +
                    certificateTypeToString(type) + '/' + endpoint;
     auto event = sdeventplus::Event::get_default();
 
     EXPECT_FALSE(fs::exists(rsaPrivateKeyFilePath));
-    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
-                    std::move(installPath));
+    Manager manager(bus, event, objPath.c_str(), type, verifyUnit, installPath);
     EXPECT_TRUE(fs::exists(rsaPrivateKeyFilePath));
 }
+
+/**
+ * Class to test Authorities List installation and replacement
+ */
+class AuthoritiesListTest : public testing::Test
+{
+  public:
+    AuthoritiesListTest() :
+        bus(sdbusplus::bus::new_default()),
+        authoritiesListFolder(
+            Certificate::generateUniqueFilePath(fs::temp_directory_path()))
+    {
+        fs::create_directory(authoritiesListFolder);
+        createAuthoritiesList(maxNumAuthorityCertificates);
+    }
+    ~AuthoritiesListTest() override
+    {
+        fs::remove_all(authoritiesListFolder);
+    }
+
+  protected:
+    // Creates a testing authorities list which consists of |count| root
+    // certificates
+    void createAuthoritiesList(int count)
+    {
+        fs::path srcFolder = fs::temp_directory_path();
+        srcFolder = Certificate::generateUniqueFilePath(srcFolder);
+        fs::create_directory(srcFolder);
+        createSingleAuthority(srcFolder, "root_0");
+        sourceAuthoritiesListFile = srcFolder / "root_0_cert";
+        for (int i = 1; i < count; ++i)
+        {
+            std::string name = "root_" + std::to_string(i);
+            createSingleAuthority(srcFolder, name);
+            appendContentFromFile(sourceAuthoritiesListFile,
+                                  srcFolder / (name + "_cert"));
+        }
+    }
+
+    // Creates a single self-signed root certificate in given |path|; the key
+    // will be |path|/|cn|_key, the cert will be |path|/|cn|_cert, and the cn
+    // will be "/O=openbmc-project.xyz/C=US/ST=CA/CN=|cn|"
+    static void createSingleAuthority(const std::string& path,
+                                      const std::string& cn)
+    {
+        std::string key = fs::path(path) / (cn + "_key");
+        std::string cert = fs::path(path) / (cn + "_cert");
+        std::string cmd = "openssl req -x509 -sha256 -newkey rsa:2048 -keyout ";
+        cmd += key + " -out " + cert + " -nodes --days 365000 ";
+        cmd += "-subj /O=openbmc-project.xyz/CN=" + cn;
+        ASSERT_EQ(std::system(cmd.c_str()), 0);
+    }
+
+    // Appends the content of the |from| file to the |to| file.
+    static void appendContentFromFile(const std::string& to,
+                                      const std::string& from)
+    {
+        ASSERT_NO_THROW({
+            std::ifstream inputCertFileStream;
+            std::ofstream outputCertFileStream;
+            inputCertFileStream.exceptions(std::ifstream::failbit |
+                                           std::ifstream::badbit |
+                                           std::ifstream::eofbit);
+            outputCertFileStream.exceptions(std::ofstream::failbit |
+                                            std::ofstream::badbit |
+                                            std::ofstream::eofbit);
+            inputCertFileStream.open(from);
+            outputCertFileStream.open(to, std::ios::app);
+            outputCertFileStream << inputCertFileStream.rdbuf() << std::flush;
+            inputCertFileStream.close();
+            outputCertFileStream.close();
+        });
+    }
+
+    // Appends the content of the |from| buffer to the |to| file.
+    static void setContentFromString(const std::string& to,
+                                     const std::string& from)
+    {
+        ASSERT_NO_THROW({
+            std::ofstream outputCertFileStream;
+            outputCertFileStream.exceptions(std::ofstream::failbit |
+                                            std::ofstream::badbit |
+                                            std::ofstream::eofbit);
+            outputCertFileStream.open(to, std::ios::out);
+            outputCertFileStream << from << std::flush;
+            outputCertFileStream.close();
+        });
+    }
+
+    // Verifies the effect of InstallAll or ReplaceAll
+    void verifyCertificates(std::vector<std::unique_ptr<Certificate>>& certs)
+    {
+        // The trust bundle file has been copied over
+        EXPECT_FALSE(fs::is_empty(authoritiesListFolder));
+        EXPECT_TRUE(
+            compareFiles(authoritiesListFolder / defaultAuthoritiesListFileName,
+                         sourceAuthoritiesListFile));
+
+        ASSERT_EQ(certs.size(), maxNumAuthorityCertificates);
+        // Check attributes and alias
+        for (size_t i = 0; i < certs.size(); ++i)
+        {
+            std::string name = "root_" + std::to_string(i);
+            EXPECT_EQ(certs[i]->subject(), "O=openbmc-project.xyz,CN=" + name);
+            EXPECT_EQ(certs[i]->issuer(), "O=openbmc-project.xyz,CN=" + name);
+            std::string symbolLink =
+                authoritiesListFolder /
+                (certs[i]->getCertId().substr(0, 8) + ".0");
+            ASSERT_TRUE(fs::exists(symbolLink));
+            compareFileAgainstString(symbolLink, certs[i]->certificateString());
+        }
+    }
+
+    // Expects that the content of |path| file is |buffer|.
+    static void compareFileAgainstString(const std::string& path,
+                                         const std::string& buffer)
+    {
+        ASSERT_NO_THROW({
+            std::ifstream inputCertFileStream;
+            inputCertFileStream.exceptions(std::ifstream::failbit |
+                                           std::ifstream::badbit |
+                                           std::ifstream::eofbit);
+            inputCertFileStream.open(path);
+            std::stringstream read;
+            read << inputCertFileStream.rdbuf();
+            inputCertFileStream.close();
+            EXPECT_EQ(read.str(), buffer);
+        });
+    };
+
+    sdbusplus::bus::bus bus;
+    fs::path authoritiesListFolder;
+    fs::path sourceAuthoritiesListFile;
+};
+
+// Tests that the Authority Manager installs all the certificates in an
+// authorities list
+TEST_F(AuthoritiesListTest, InstallAll)
+{
+    std::string endpoint("ldap");
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
+    CertificateType type = CertificateType::Authority;
+
+    std::string object = std::string(objectNamePrefix) + '/' +
+                         certificateTypeToString(type) + '/' + endpoint;
+    auto event = sdeventplus::Event::get_default();
+    // Attach the bus to sd_event to service user requests
+    bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
+    ManagerInTest manager(bus, event, object.c_str(), type, verifyUnit,
+                          authoritiesListFolder);
+    EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+        .WillOnce(Return());
+    ASSERT_TRUE(manager.getCertificates().empty());
+
+    std::vector<sdbusplus::message::object_path> objects =
+        manager.installAll(sourceAuthoritiesListFile);
+    for (size_t i = 0; i < manager.getCertificates().size(); ++i)
+    {
+        EXPECT_EQ(manager.getCertificates()[i]->getObjectPath(), objects[i]);
+    }
+    verifyCertificates(manager.getCertificates());
+}
+
+// Tests that the Authority Manager recovers from the authorities list persisted
+// in the installation path at boot up
+TEST_F(AuthoritiesListTest, RecoverAtBootUp)
+{
+    std::string endpoint("ldap");
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
+    CertificateType type = CertificateType::Authority;
+
+    std::string object = std::string(objectNamePrefix) + '/' +
+                         certificateTypeToString(type) + '/' + endpoint;
+    auto event = sdeventplus::Event::get_default();
+    // Attach the bus to sd_event to service user requests
+    bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
+
+    // Copy the trust bundle into the installation path before creating an
+    // Authority Manager
+    fs::copy_file(/*from=*/sourceAuthoritiesListFile,
+                  authoritiesListFolder / defaultAuthoritiesListFileName);
+    // Create some noise as well
+    fs::copy_file(/*from=*/sourceAuthoritiesListFile,
+                  authoritiesListFolder / "should_be_deleted");
+
+    ManagerInTest manager(bus, event, object.c_str(), type, verifyUnit,
+                          authoritiesListFolder);
+
+    ASSERT_EQ(manager.getCertificates().size(), maxNumAuthorityCertificates);
+
+    // Check attributes and alias
+    std::unordered_set<std::string> expectedFiles = {authoritiesListFolder /
+                                                     "trust_bundle"};
+    std::vector<std::unique_ptr<Certificate>>& certs =
+        manager.getCertificates();
+    for (size_t i = 0; i < certs.size(); ++i)
+    {
+        std::string name = "root_" + std::to_string(i);
+        EXPECT_EQ(certs[i]->subject(), "O=openbmc-project.xyz,CN=" + name);
+        EXPECT_EQ(certs[i]->issuer(), "O=openbmc-project.xyz,CN=" + name);
+        std::string symbolLink =
+            authoritiesListFolder / (certs[i]->getCertId().substr(0, 8) + ".0");
+        expectedFiles.insert(symbolLink);
+        expectedFiles.insert(certs[i]->getCertFilePath());
+        ASSERT_TRUE(fs::exists(symbolLink));
+        compareFileAgainstString(symbolLink, certs[i]->certificateString());
+    }
+
+    // Check folder content
+    for (auto& path : fs::directory_iterator(authoritiesListFolder))
+    {
+        EXPECT_NE(path, authoritiesListFolder / "should_be_deleted");
+        expectedFiles.erase(path.path());
+    }
+    EXPECT_TRUE(expectedFiles.empty());
+}
+
+TEST_F(AuthoritiesListTest, InstallAndDelete)
+{
+    std::string endpoint("ldap");
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
+    CertificateType type = CertificateType::Authority;
+
+    std::string object = std::string(objectNamePrefix) + '/' +
+                         certificateTypeToString(type) + '/' + endpoint;
+
+    auto event = sdeventplus::Event::get_default();
+    // Attach the bus to sd_event to service user requests
+    bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
+    ManagerInTest manager(bus, event, object.c_str(), type, verifyUnit,
+                          authoritiesListFolder);
+    EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+        .WillOnce(Return())
+        .WillOnce(Return());
+    ASSERT_TRUE(manager.getCertificates().empty());
+    ASSERT_EQ(manager.installAll(sourceAuthoritiesListFile).size(),
+              maxNumAuthorityCertificates);
+    manager.deleteAll();
+    EXPECT_TRUE(manager.getCertificates().empty());
+    // Check folder content
+    for (const fs::path& f : fs::directory_iterator(authoritiesListFolder))
+    {
+        EXPECT_THAT(f.filename(), testing::AnyOf(".", ".."));
+    }
+}
+
+TEST_F(AuthoritiesListTest, InstallAllWrongManagerType)
+{
+    std::string endpoint("ldap");
+    CertificateType type = CertificateType::Server;
+
+    std::string object = std::string(objectNamePrefix) + '/' +
+                         certificateTypeToString(type) + '/' + endpoint;
+
+    auto event = sdeventplus::Event::get_default();
+    // Attach the bus to sd_event to service user requests
+    bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
+    ManagerInTest serverManager(bus, event, object.c_str(), type, "",
+                                authoritiesListFolder);
+    EXPECT_THROW(serverManager.installAll(sourceAuthoritiesListFile),
+                 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed);
+
+    type = CertificateType::Client;
+    object = std::string(objectNamePrefix) + '/' +
+             certificateTypeToString(type) + '/' + endpoint;
+    ManagerInTest clientManager(bus, event, object.c_str(), type, "",
+                                authoritiesListFolder);
+    EXPECT_THROW(clientManager.installAll(sourceAuthoritiesListFile),
+                 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed);
+}
+
+TEST_F(AuthoritiesListTest, InstallAllTwice)
+{
+    std::string endpoint("ldap");
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
+    CertificateType type = CertificateType::Authority;
+
+    std::string object = std::string(objectNamePrefix) + '/' +
+                         certificateTypeToString(type) + '/' + endpoint;
+
+    auto event = sdeventplus::Event::get_default();
+    // Attach the bus to sd_event to service user requests
+    bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
+    ManagerInTest manager(bus, event, object.c_str(), type, verifyUnit,
+                          authoritiesListFolder);
+    EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+        .WillOnce(Return());
+    ASSERT_TRUE(manager.getCertificates().empty());
+
+    ASSERT_EQ(manager.installAll(sourceAuthoritiesListFile).size(),
+              maxNumAuthorityCertificates);
+    EXPECT_THROW(manager.installAll(sourceAuthoritiesListFile).size(),
+                 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed);
+}
+
+TEST_F(AuthoritiesListTest, InstallAllMissSourceFile)
+{
+    std::string endpoint("ldap");
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
+    CertificateType type = CertificateType::Authority;
+
+    std::string object = std::string(objectNamePrefix) + '/' +
+                         certificateTypeToString(type) + '/' + endpoint;
+
+    auto event = sdeventplus::Event::get_default();
+    // Attach the bus to sd_event to service user requests
+    bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
+    ManagerInTest manager(bus, event, object.c_str(), type, verifyUnit,
+                          authoritiesListFolder);
+
+    EXPECT_THROW(manager.installAll(authoritiesListFolder / "trust_bundle"),
+                 InternalFailure);
+}
+
+TEST_F(AuthoritiesListTest, TooManyRootCertificates)
+{
+    std::string endpoint("ldap");
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
+    CertificateType type = CertificateType::Authority;
+
+    std::string object = std::string(objectNamePrefix) + '/' +
+                         certificateTypeToString(type) + '/' + endpoint;
+
+    auto event = sdeventplus::Event::get_default();
+    // Attach the bus to sd_event to service user requests
+    bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
+    ManagerInTest manager(bus, event, object.c_str(), type, verifyUnit,
+                          authoritiesListFolder);
+    createAuthoritiesList(maxNumAuthorityCertificates + 1);
+    EXPECT_THROW(manager.installAll(sourceAuthoritiesListFile),
+                 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed);
+}
+
+TEST_F(AuthoritiesListTest, CertInWrongFormat)
+{
+    std::string endpoint("ldap");
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
+    CertificateType type = CertificateType::Authority;
+
+    std::string object = std::string(objectNamePrefix) + '/' +
+                         certificateTypeToString(type) + '/' + endpoint;
+
+    auto event = sdeventplus::Event::get_default();
+    // Attach the bus to sd_event to service user requests
+    bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
+
+    ManagerInTest manager(bus, event, object.c_str(), type, verifyUnit,
+                          authoritiesListFolder);
+
+    // Replace the authorities list with non-valid PEM encoded x509 certificate
+    setContentFromString(sourceAuthoritiesListFile, "blah-blah");
+    EXPECT_THROW(manager.installAll(sourceAuthoritiesListFile),
+                 InvalidCertificate);
+    setContentFromString(sourceAuthoritiesListFile,
+                         "-----BEGIN CERTIFICATE-----");
+    EXPECT_THROW(manager.installAll(sourceAuthoritiesListFile),
+                 InvalidCertificate);
+}
+
+TEST_F(AuthoritiesListTest, ReplaceAll)
+{
+    std::string endpoint("ldap");
+    std::string verifyUnit(ManagerInTest::unitToRestartInTest);
+    CertificateType type = CertificateType::Authority;
+
+    std::string object = std::string(objectNamePrefix) + '/' +
+                         certificateTypeToString(type) + '/' + endpoint;
+
+    auto event = sdeventplus::Event::get_default();
+    // Attach the bus to sd_event to service user requests
+    bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
+    ManagerInTest manager(bus, event, object.c_str(), type, verifyUnit,
+                          authoritiesListFolder);
+    EXPECT_CALL(manager, reloadOrReset(Eq(ManagerInTest::unitToRestartInTest)))
+        .WillOnce(Return())
+        .WillOnce(Return());
+    manager.installAll(sourceAuthoritiesListFile);
+
+    // Replace the current list with a different list
+    fs::remove_all(sourceAuthoritiesListFile.parent_path());
+    createAuthoritiesList(maxNumAuthorityCertificates);
+    std::vector<sdbusplus::message::object_path> objects =
+        manager.replaceAll(sourceAuthoritiesListFile);
+
+    for (size_t i = 0; i < manager.getCertificates().size(); ++i)
+    {
+        EXPECT_EQ(manager.getCertificates()[i]->getObjectPath(), objects[i]);
+    }
+    verifyCertificates(manager.getCertificates());
+}
+
 } // namespace
 } // namespace phosphor::certs
diff --git a/x509_utils.cpp b/x509_utils.cpp
index 9a589dd..ba1a2ef 100644
--- a/x509_utils.cpp
+++ b/x509_utils.cpp
@@ -225,4 +225,31 @@
 
     return {idBuff};
 }
+
+std::unique_ptr<X509, decltype(&::X509_free)> parseCert(const std::string& pem)
+{
+    if (pem.size() > INT_MAX)
+    {
+        log<level::ERR>("Error occurred during parseCert: PEM is too long");
+        elog<InvalidCertificate>(Reason("Invalid PEM: too long"));
+    }
+    X509Ptr cert(X509_new(), ::X509_free);
+    if (!cert)
+    {
+        log<level::ERR>("Error occurred during X509_new call",
+                        entry("ERRCODE=%lu", ERR_get_error()));
+        elog<InternalFailure>();
+    }
+
+    BIOMemPtr bioCert(BIO_new_mem_buf(pem.data(), static_cast<int>(pem.size())),
+                      ::BIO_free);
+    X509* x509 = cert.get();
+    if (!PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr))
+    {
+        log<level::ERR>("Error occurred during PEM_read_bio_X509 call",
+                        entry("PEM=%s", pem.c_str()));
+        elog<InternalFailure>();
+    }
+    return cert;
+}
 } // namespace phosphor::certs
diff --git a/x509_utils.hpp b/x509_utils.hpp
index e439aad..6e193fc 100644
--- a/x509_utils.hpp
+++ b/x509_utils.hpp
@@ -58,4 +58,9 @@
  */
 std::string generateCertId(X509& cert);
 
+/** @brief Parses PEM string into the X509 structure.
+ *  @param[in] pem - PEM encoded X509 certificate buffer.
+ *  @return pointer to the X509 structure.
+ */
+std::unique_ptr<X509, decltype(&::X509_free)> parseCert(const std::string& pem);
 } // namespace phosphor::certs