Add Generate Key and Certificate Signing Request (CSR)

Generates Private key and CSR file, at present supporing
only RSA algorithm type.

-The generateCSR method defined in Create interface is implemented
by manager class to Create CSR and PrivateKey files.

-The cSR method defined in View interface is implemented by CSR
class to view CSR file.

- Generate CSR is time consuming operation and it might time-out
the D-Bus call. Forking process and performing CSR generation in
the child process, adding the process ID of the child process to the
SD Event loop so that callback is received when the chid process
is done with the CSR generation.

- As the GenerateCSR method returns immediately, caller need
to wait on InterfacesAdded signal that is generated after completion
of the CSR request. The caller then invokes cSR method of
CSR interface to read the CSR.

- For any failure in Generate CSR CSR object is created with error
status.

- CSR object raises exception if error is set else CSR data is
returned to the caller.

- To cater for failure cases caller need to start a timer, which
will be terminated after getting InterfaceAdded signal or upon timeout.

-Added Unit tests.
Tested:
1) Added unit tests to verify CSR generation
2) Tested with Redfish to generate and view CSR
curl -c cjar -b cjar -k -H "X-Auth-Token: $bmc_token" -X POST
https://${bmc}/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/
-d @generate.jon

{
  "CSRString": "-----BEGIN CERTIFICATE REQUEST---7E=\n-----END CERTIFICATE
REQUEST-----\n",
  "CertificateCollection": {
    "@odata.id": "/redfish/v1/AccountService/LDAP/Certificates/"
  }
}
Change-Id: I1e3ae8df45f87bfd8903f552d93c4df1af7c569f
Signed-off-by: Marri Devender Rao <devenrao@in.ibm.com>
Signed-off-by: Nagaraju Goruganti <ngorugan@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 02cc10d..8808069 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -19,6 +19,7 @@
 
 phosphor_certificate_manager_LDFLAGS = \
 	$(SDBUSPLUS_LIBS) \
+	$(SDEVENTPLUS_LIBS) \
 	$(PHOSPHOR_DBUS_INTERFACES_LIBS) \
 	$(PHOSPHOR_LOGGING_LIBS) \
 	$(OPENSSL_LIBS) \
@@ -26,6 +27,7 @@
 
 phosphor_certificate_manager_CXXFLAGS = \
 	$(SYSTEMD_CFLAGS) \
+	$(SDEVENTPLUS_LIBS) \
 	$(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
 	$(PHOSPHOR_LOGGING_CFLAGS)
 
diff --git a/certs_manager.cpp b/certs_manager.cpp
index 2a90589..3f7a17e 100644
--- a/certs_manager.cpp
+++ b/certs_manager.cpp
@@ -1,5 +1,8 @@
 #include "certs_manager.hpp"
 
+#include <openssl/pem.h>
+#include <unistd.h>
+
 #include <phosphor-logging/elog-errors.hpp>
 #include <xyz/openbmc_project/Certs/error.hpp>
 #include <xyz/openbmc_project/Common/error.hpp>
@@ -7,23 +10,19 @@
 {
 namespace certs
 {
-
 using InternalFailure =
     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
 
-/** @brief Constructor to put object onto bus at a dbus path.
- *  @param[in] bus - Bus to attach to.
- *  @param[in] path - Path to attach at.
- *  @param[in] type - Type of the certificate.
- *  @param[in] unit - Unit consumed by this certificate.
- *  @param[in] installPath - Certificate installation path.
- */
-Manager::Manager(sdbusplus::bus::bus& bus, const char* path,
-                 const CertificateType& type, UnitsToRestart&& unit,
-                 CertInstallPath&& installPath) :
+using X509_REQ_Ptr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>;
+using BIGNUM_Ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
+
+Manager::Manager(sdbusplus::bus::bus& bus, sdeventplus::Event& event,
+                 const char* path, const CertificateType& type,
+                 UnitsToRestart&& unit, CertInstallPath&& installPath) :
     Ifaces(bus, path),
-    bus(bus), objectPath(path), certType(type), unitToRestart(std::move(unit)),
-    certInstallPath(std::move(installPath))
+    bus(bus), event(event), objectPath(path), certType(type),
+    unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)),
+    childPtr(nullptr)
 {
     using InvalidCertificate =
         sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
@@ -82,5 +81,286 @@
         certificatePtr.reset(nullptr);
     }
 }
+
+std::string Manager::generateCSR(
+    std::vector<std::string> alternativeNames, std::string challengePassword,
+    std::string city, std::string commonName, std::string contactPerson,
+    std::string country, std::string email, std::string givenName,
+    std::string initials, int64_t keyBitLength, std::string keyCurveId,
+    std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
+    std::string organization, std::string organizationalUnit, std::string state,
+    std::string surname, std::string unstructuredName)
+{
+    // We support only one CSR.
+    csrPtr.reset(nullptr);
+    auto pid = fork();
+    if (pid == -1)
+    {
+        log<level::ERR>("Error occurred during forking process");
+        report<InternalFailure>();
+    }
+    else if (pid == 0)
+    {
+        try
+        {
+            generateCSRHelper(alternativeNames, challengePassword, city,
+                              commonName, contactPerson, country, email,
+                              givenName, initials, keyBitLength, keyCurveId,
+                              keyPairAlgorithm, keyUsage, organization,
+                              organizationalUnit, state, surname,
+                              unstructuredName);
+            exit(EXIT_SUCCESS);
+        }
+        catch (const InternalFailure& e)
+        {
+            // commit the error reported in child process and exit
+            // Callback method from SDEvent Loop looks for exit status
+            exit(EXIT_FAILURE);
+            commit<InternalFailure>();
+        }
+    }
+    else
+    {
+        using namespace sdeventplus::source;
+        Child::Callback callback = [this](Child& eventSource,
+                                          const siginfo_t* si) {
+            eventSource.set_enabled(Enabled::On);
+            if (si->si_status != 0)
+            {
+                this->createCSRObject(Status::FAILURE);
+            }
+            else
+            {
+                this->createCSRObject(Status::SUCCESS);
+            }
+        };
+        try
+        {
+            sigset_t ss;
+            if (sigemptyset(&ss) < 0)
+            {
+                log<level::ERR>("Unable to initialize signal set");
+                elog<InternalFailure>();
+            }
+            if (sigaddset(&ss, SIGCHLD) < 0)
+            {
+                log<level::ERR>("Unable to add signal to signal set");
+                elog<InternalFailure>();
+            }
+
+            // Block SIGCHLD first, so that the event loop can handle it
+            if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0)
+            {
+                log<level::ERR>("Unable to block signal");
+                elog<InternalFailure>();
+            }
+            if (childPtr)
+            {
+                childPtr.reset();
+            }
+            childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
+                                               std::move(callback));
+        }
+        catch (const InternalFailure& e)
+        {
+            commit<InternalFailure>();
+        }
+    }
+    auto csrObjectPath = objectPath + '/' + "csr";
+    return csrObjectPath;
+}
+
+void Manager::generateCSRHelper(
+    std::vector<std::string> alternativeNames, std::string challengePassword,
+    std::string city, std::string commonName, std::string contactPerson,
+    std::string country, std::string email, std::string givenName,
+    std::string initials, int64_t keyBitLength, std::string keyCurveId,
+    std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
+    std::string organization, std::string organizationalUnit, std::string state,
+    std::string surname, std::string unstructuredName)
+{
+    int ret = 0;
+
+    // set version of x509 req
+    int nVersion = 1;
+    // TODO: Issue#6 need to make version number configurable
+    X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free);
+    ret = X509_REQ_set_version(x509Req.get(), nVersion);
+    if (ret == 0)
+    {
+        log<level::ERR>("Error occured during X509_REQ_set_version call");
+        elog<InternalFailure>();
+    }
+
+    // set subject of x509 req
+    X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
+
+    if (!alternativeNames.empty())
+    {
+        for (auto& name : alternativeNames)
+        {
+            addEntry(x509Name, "subjectAltName", name);
+        }
+    }
+    addEntry(x509Name, "challengePassword", challengePassword);
+    addEntry(x509Name, "L", city);
+    addEntry(x509Name, "CN", commonName);
+    addEntry(x509Name, "name", contactPerson);
+    addEntry(x509Name, "C", country);
+    addEntry(x509Name, "emailAddress", email);
+    addEntry(x509Name, "GN", givenName);
+    addEntry(x509Name, "initials", initials);
+    addEntry(x509Name, "algorithm", keyPairAlgorithm);
+    if (!keyUsage.empty())
+    {
+        for (auto& usage : keyUsage)
+        {
+            addEntry(x509Name, "keyUsage", usage);
+        }
+    }
+    addEntry(x509Name, "O", organization);
+    addEntry(x509Name, "ST", state);
+    addEntry(x509Name, "SN", surname);
+    addEntry(x509Name, "unstructuredName", unstructuredName);
+
+    // Generate private key and write to file
+    EVP_PKEY_Ptr pKey = writePrivateKey(keyBitLength, x509Req);
+
+    // set sign key of x509 req
+    ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
+    if (ret <= 0)
+    {
+        log<level::ERR>("Error occured while signing key of x509");
+        elog<InternalFailure>();
+    }
+    log<level::INFO>("Writing CSR to file");
+    std::string path = fs::path(certInstallPath).parent_path();
+    std::string csrFilePath = path + '/' + CSR_FILE_NAME;
+    writeCSR(csrFilePath, x509Req);
+}
+
+EVP_PKEY_Ptr Manager::writePrivateKey(int64_t keyBitLength,
+                                      X509_REQ_Ptr& x509Req)
+{
+    int ret = 0;
+    // generate rsa key
+    BIGNUM_Ptr bne(BN_new(), ::BN_free);
+    ret = BN_set_word(bne.get(), RSA_F4);
+    if (ret == 0)
+    {
+        log<level::ERR>("Error occured during BN_set_word call");
+        elog<InternalFailure>();
+    }
+
+    // set keybit length to default value if not set
+    if (keyBitLength <= 0)
+    {
+        keyBitLength = 2048;
+    }
+    RSA* rsa = RSA_new();
+    ret = RSA_generate_key_ex(rsa, keyBitLength, bne.get(), NULL);
+    if (ret != 1)
+    {
+        free(rsa);
+        log<level::ERR>("Error occured during RSA_generate_key_ex call",
+                        entry("KEYBITLENGTH=%PRIu64", keyBitLength));
+        elog<InternalFailure>();
+    }
+
+    // set public key of x509 req
+    EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
+    EVP_PKEY_assign_RSA(pKey.get(), rsa);
+    ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
+    if (ret == 0)
+    {
+        log<level::ERR>("Error occured while setting Public key");
+        elog<InternalFailure>();
+    }
+
+    log<level::ERR>("Writing private key to file");
+    // write private key to file
+    std::string path = fs::path(certInstallPath).parent_path();
+    std::string privKeyPath = path + '/' + PRIV_KEY_FILE_NAME;
+
+    FILE* fp = std::fopen(privKeyPath.c_str(), "w");
+    if (fp == NULL)
+    {
+        ret = -1;
+        log<level::ERR>("Error occured creating private key file");
+        elog<InternalFailure>();
+    }
+    ret = PEM_write_PrivateKey(fp, pKey.get(), NULL, NULL, 0, 0, NULL);
+    std::fclose(fp);
+    if (ret == 0)
+    {
+        log<level::ERR>("Error occured while writing private key to file");
+        elog<InternalFailure>();
+    }
+    return pKey;
+}
+
+void Manager::addEntry(X509_NAME* x509Name, const char* field,
+                       const std::string& bytes)
+{
+    if (bytes.empty())
+    {
+        return;
+    }
+    int ret = X509_NAME_add_entry_by_txt(
+        x509Name, field, MBSTRING_ASC,
+        reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
+    if (ret != 1)
+    {
+        log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
+                        entry("VALUE=%s", bytes.c_str()));
+        elog<InternalFailure>();
+    }
+}
+
+void Manager::createCSRObject(const Status& status)
+{
+    if (csrPtr)
+    {
+        csrPtr.reset(nullptr);
+    }
+    auto csrObjectPath = objectPath + '/' + "csr";
+    csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
+                                   certInstallPath.c_str(), status);
+}
+
+void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req)
+{
+    if (fs::exists(filePath))
+    {
+        log<level::INFO>("Removing the existing file",
+                         entry("FILENAME=%s", filePath.c_str()));
+        if (!fs::remove(filePath.c_str()))
+        {
+            log<level::ERR>("Unable to remove the file",
+                            entry("FILENAME=%s", filePath.c_str()));
+            elog<InternalFailure>();
+        }
+    }
+
+    FILE* fp = NULL;
+
+    if ((fp = std::fopen(filePath.c_str(), "w")) == NULL)
+    {
+        log<level::ERR>("Error opening the file to write the CSR",
+                        entry("FILENAME=%s", filePath.c_str()));
+        elog<InternalFailure>();
+    }
+
+    int rc = PEM_write_X509_REQ(fp, x509Req.get());
+    if (!rc)
+    {
+        log<level::ERR>("PEM write routine failed",
+                        entry("FILENAME=%s", filePath.c_str()));
+        std::fclose(fp);
+        elog<InternalFailure>();
+    }
+    std::fclose(fp);
+}
+
 } // namespace certs
 } // namespace phosphor
diff --git a/certs_manager.hpp b/certs_manager.hpp
index 2e93975..0c8f4e6 100644
--- a/certs_manager.hpp
+++ b/certs_manager.hpp
@@ -1,6 +1,12 @@
 #pragma once
-#include "certificate.hpp"
+#include "config.h"
 
+#include "certificate.hpp"
+#include "csr.hpp"
+
+#include <sdeventplus/source/child.hpp>
+#include <sdeventplus/source/event.hpp>
+#include <xyz/openbmc_project/Certs/CSR/Create/server.hpp>
 #include <xyz/openbmc_project/Certs/Install/server.hpp>
 #include <xyz/openbmc_project/Object/Delete/server.hpp>
 
@@ -8,9 +14,13 @@
 {
 namespace certs
 {
-using Create = sdbusplus::xyz::openbmc_project::Certs::server::Install;
+using Install = sdbusplus::xyz::openbmc_project::Certs::server::Install;
 using Delete = sdbusplus::xyz::openbmc_project::Object::server::Delete;
-using Ifaces = sdbusplus::server::object::object<Create, Delete>;
+using CSRCreate = sdbusplus::xyz::openbmc_project::Certs::CSR::server::Create;
+using Ifaces = sdbusplus::server::object::object<Install, CSRCreate, Delete>;
+
+using X509_REQ_Ptr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>;
+using EVP_PKEY_Ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
 
 class Manager : public Ifaces
 {
@@ -34,14 +44,15 @@
 
     /** @brief Constructor to put object onto bus at a dbus path.
      *  @param[in] bus - Bus to attach to.
+     *  @param[in] event - sd event handler.
      *  @param[in] path - Path to attach at.
      *  @param[in] type - Type of the certificate.
      *  @param[in] unit - Unit consumed by this certificate.
      *  @param[in] installPath - Certificate installation path.
      */
-    Manager(sdbusplus::bus::bus& bus, const char* path,
-            const CertificateType& type, UnitsToRestart&& unit,
-            CertInstallPath&& installPath);
+    Manager(sdbusplus::bus::bus& bus, sdeventplus::Event& event,
+            const char* path, const CertificateType& type,
+            UnitsToRestart&& unit, CertInstallPath&& installPath);
 
     /** @brief Implementation for Install
      *  Replace the existing certificate key file with another
@@ -56,10 +67,135 @@
      */
     void delete_() override;
 
+    /** @brief Generate Private key and CSR file
+     *  Generates the Private key file and CSR file based on the input
+     *  parameters. Validation of the parameters is callers responsibility.
+     *  At present supports only RSA algorithm type
+     *
+     *  @param[in] alternativeNames - Additional hostnames of the component that
+     *      is being secured.
+     *  @param[in] challengePassword - The challenge password to be applied to
+     *      the certificate for revocation requests.
+     *  @param[in] city - The city or locality of the organization making the
+     *      request. For Example Austin
+     *  @param[in] commonName - The fully qualified domain name of the component
+     *      that is being secured.
+     *  @param[in] contactPerson - The name of the user making the request.
+     *  @param[in] country - The country of the organization making the request.
+     *  @param[in] email - The email address of the contact within the
+     *      organization making the request.
+     *  @param[in] givenName - The given name of the user making the request.
+     *  @param[in] initials - The initials of the user making the request.
+     *  @param[in] keyBitLength - The length of the key in bits, if needed based
+     *      on the value of the KeyPairAlgorithm parameter.
+     *  @param[in] keyCurveId - The curve ID to be used with the key, if needed
+     *      based on the value of the KeyPairAlgorithm parameter.
+     *  @param[in] keyPairAlgorithm - The type of key pair for use with signing
+     *      algorithms. Valid built-in algorithm names for private key
+     *      generation are: RSA, DSA, DH and EC.
+     *  @param[in] keyUsage - Key usage extensions define the purpose of the
+     *      public key contained in a certificate. Valid Key usage extensions
+     *      and its usage description.
+     *      - ClientAuthentication: The public key is used for TLS WWW client
+     *      authentication.
+     *      - CodeSigning: The public key is used for the signing of executable
+     *          code
+     *      - CRLSigning: The public key is used for verifying signatures on
+     *          certificate revocation lists (CLRs).
+     *      - DataEncipherment: The public key is used for directly enciphering
+     *          raw user data without the use of an intermediate symmetric
+     *          cipher.
+     *      - DecipherOnly: The public key could be used for deciphering data
+     *          while performing key agreement.
+     *      - DigitalSignature: The public key is used for verifying digital
+     *          signatures, other than signatures on certificatesand CRLs.
+     *      - EmailProtection: The public key is used for email protection.
+     *      - EncipherOnly: Thepublic key could be used for enciphering data
+     *          while performing key agreement.
+     *      - KeyCertSign: The public key is used for verifying signatures on
+     *          public key certificates.
+     *      - KeyEncipherment: The public key is used for enciphering private or
+     *          secret keys.
+     *      - NonRepudiation: The public key is used to verify digital
+     *          signatures, other than signatures on certificates and CRLs, and
+     *          used to provide a non-repudiation service that protects against
+     *          the signing entity falsely denying some action.
+     *      - OCSPSigning: The public key is used for signing OCSP responses.
+     *      - ServerAuthentication: The public key is used for TLS WWW server
+     *          authentication.
+     *      - Timestamping: The public key is used for binding the hash of an
+     *          object to a time.
+     *  @param[in] organization - The legal name of the organization. This
+     *      should not be abbreviated and should include suffixes such as Inc,
+     *      Corp, or LLC.For example, IBM Corp.
+     *  @param[in] organizationalUnit - The name of the unit or division of the
+     *      organization making the request.
+     *  @param[in] state - The state or province where the organization is
+     *      located. This should not be abbreviated. For example, Texas.
+     *  @param[in] surname - The surname of the user making the request.
+     *  @param[in] unstructuredName - The unstructured name of the subject.
+     *
+     *  @return path[std::string] - The object path of the D-Bus object
+     *      representing CSR string. Note: For new CSR request will overwrite
+     * the existing CSR in the system.
+     */
+    std::string generateCSR(
+        std::vector<std::string> alternativeNames,
+        std::string challengePassword, std::string city, std::string commonName,
+        std::string contactPerson, std::string country, std::string email,
+        std::string givenName, std::string initials, int64_t keyBitLength,
+        std::string keyCurveId, std::string keyPairAlgorithm,
+        std::vector<std::string> keyUsage, std::string organization,
+        std::string organizationalUnit, std::string state, std::string surname,
+        std::string unstructuredName) override;
+
   private:
+    void generateCSRHelper(std::vector<std::string> alternativeNames,
+                           std::string challengePassword, std::string city,
+                           std::string commonName, std::string contactPerson,
+                           std::string country, std::string email,
+                           std::string givenName, std::string initials,
+                           int64_t keyBitLength, std::string keyCurveId,
+                           std::string keyPairAlgorithm,
+                           std::vector<std::string> keyUsage,
+                           std::string organization,
+                           std::string organizationalUnit, std::string state,
+                           std::string surname, std::string unstructuredName);
+
+    /** @brief Write private key data to file
+     *
+     *  @param[in] keyBitLength - KeyBit length.
+     *  @param[in] x509Req - pointer to X509 request.
+     *  @return pointer to private key
+     */
+    EVP_PKEY_Ptr writePrivateKey(int64_t keyBitLength, X509_REQ_Ptr& x509Req);
+
+    /** @brief Add the specified CSR field with the data
+     *  @param[in] x509Name - Structure used in setting certificate properties
+     *  @param[in] field - field name
+     *  @param[in] bytes - field value in bytes
+     */
+    void addEntry(X509_NAME* x509Name, const char* field,
+                  const std::string& bytes);
+
+    /** @brief Create CSR D-Bus object by reading the data in the CSR file
+     *  @param[in] statis - SUCCESSS/FAILURE In CSR generation.
+     */
+    void createCSRObject(const Status& status);
+
+    /** @brief Write generated CSR data to file
+     *
+     *  @param[in] filePath - CSR file path.
+     *  @param[in] x509Req - OpenSSL Request Pointer.
+     */
+    void writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req);
+
     /** @brief sdbusplus handler */
     sdbusplus::bus::bus& bus;
 
+    // sdevent Event handle
+    sdeventplus::Event& event;
+
     /** @brief object path */
     std::string objectPath;
 
@@ -74,7 +210,12 @@
 
     /** @brief pointer to certificate */
     std::unique_ptr<Certificate> certificatePtr = nullptr;
-};
 
+    /** @brief pointer to CSR */
+    std::unique_ptr<CSR> csrPtr = nullptr;
+
+    /** @brief SDEventPlus child pointer added to event loop */
+    std::unique_ptr<sdeventplus::source::Child> childPtr;
+};
 } // namespace certs
 } // namespace phosphor
diff --git a/configure.ac b/configure.ac
index 5bf1a40..b56b83a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -25,6 +25,7 @@
 )
 PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [phosphor-dbus-interfaces])
 PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus])
+PKG_CHECK_MODULES([SDEVENTPLUS], [sdeventplus])
 PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging])
 
 # Code coverage
@@ -123,6 +124,10 @@
 AS_IF([test "x$CSR_FILE_NAME" == "x"], [CSR_FILE_NAME="domain.csr"])
 AC_DEFINE_UNQUOTED([CSR_FILE_NAME], ["$CSR_FILE_NAME"], [The CSR file])
 
+AC_ARG_VAR(PRIV_KEY_FILE_NAME, [The private key file.])
+AS_IF([test "x$PRIV_KEY_FILE_NAME" == "x"], [PRIV_KEY_FILE_NAME="privkey.pem"])
+AC_DEFINE_UNQUOTED([PRIV_KEY_FILE_NAME], ["$PRIV_KEY_FILE_NAME"], [The private key file])
+
 # Create configured output
 AC_CONFIG_FILES([Makefile test/Makefile])
 AC_OUTPUT
diff --git a/mainapp.cpp b/mainapp.cpp
index 7ad0332..0238e5e 100644
--- a/mainapp.cpp
+++ b/mainapp.cpp
@@ -21,6 +21,7 @@
 
 #include <iostream>
 #include <locale>
+#include <sdeventplus/event.hpp>
 #include <string>
 
 static void ExitWithError(const char* err, char** argv)
@@ -65,15 +66,19 @@
 
     // unit is an optional parameter
     auto unit = std::move((options)["unit"]);
-
     auto bus = sdbusplus::bus::new_default();
-
     auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
 
     // Add sdbusplus ObjectManager
     sdbusplus::server::manager::manager objManager(bus, objPath.c_str());
 
-    phosphor::certs::Manager manager(bus, objPath.c_str(), type,
+    // Get default event loop
+    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);
+
+    phosphor::certs::Manager manager(bus, event, objPath.c_str(), type,
                                      std::move(unit), std::move(path));
 
     // Adjusting Interface name as per std convention
@@ -81,12 +86,6 @@
     capitalize(endpoint);
     auto busName = std::string(BUSNAME) + '.' + type + '.' + endpoint;
     bus.request_name(busName.c_str());
-
-    while (true)
-    {
-        // process dbus calls / signals discarding unhandled
-        bus.process_discard();
-        bus.wait();
-    }
+    event.loop();
     return 0;
 }
diff --git a/test/Makefile.am b/test/Makefile.am
index 1abc540..e498a16 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -3,6 +3,7 @@
 
 AM_CFLAGS = \
 	$(SDBUSPLUS_CFLAGS) \
+	$(SDEVENTPLUS_CFLAGS) \
 	$(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
 	$(PHOSPHOR_LOGGING_CFLAGS)
 
@@ -11,6 +12,7 @@
 AM_LDFLAGS = \
 	-lstdc++fs \
 	$(SDBUSPLUS_LIBS) \
+	$(SDEVENTPLUS_LIBS) \
 	$(PHOSPHOR_DBUS_INTERFACES_LIBS) \
 	$(PHOSPHOR_LOGGING_LIBS) \
 	$(OPENSSL_LIBS) \
@@ -29,3 +31,4 @@
 certs_manager_test_SOURCES = certs_manager_test.cpp
 certs_manager_test_LDADD = $(top_builddir)/certs_manager.o
 certs_manager_test_LDADD += $(top_builddir)/certificate.o
+certs_manager_test_LDADD += $(top_builddir)/csr.o
diff --git a/test/certs_manager_test.cpp b/test/certs_manager_test.cpp
index 9fd7110..b26c8ee 100644
--- a/test/certs_manager_test.cpp
+++ b/test/certs_manager_test.cpp
@@ -7,6 +7,7 @@
 #include <filesystem>
 #include <fstream>
 #include <iterator>
+#include <sdeventplus/event.hpp>
 #include <string>
 #include <xyz/openbmc_project/Certs/error.hpp>
 #include <xyz/openbmc_project/Common/error.hpp>
@@ -38,6 +39,8 @@
         }
         certDir = dirPtr;
         certificateFile = "cert.pem";
+        CSRFile = "domain.csr";
+        privateKeyFile = "privkey.pem";
         std::string cmd = "openssl req -x509 -sha256 -newkey rsa:2048 ";
         cmd += "-keyout cert.pem -out cert.pem -days 3650 ";
         cmd += "-subj "
@@ -53,6 +56,8 @@
     {
         fs::remove_all(certDir);
         fs::remove(certificateFile);
+        fs::remove(CSRFile);
+        fs::remove(privateKeyFile);
     }
 
     bool compareFiles(const std::string& file1, const std::string& file2)
@@ -80,7 +85,7 @@
 
   protected:
     sdbusplus::bus::bus bus;
-    std::string certificateFile;
+    std::string certificateFile, CSRFile, privateKeyFile;
 
     std::string certDir;
 };
@@ -88,7 +93,10 @@
 class MainApp
 {
   public:
-    MainApp(phosphor::certs::Manager* manager) : manager(manager)
+    MainApp(phosphor::certs::Manager* manager,
+            phosphor::certs::CSR* csr = nullptr) :
+        manager(manager),
+        csr(csr)
     {
     }
     void install(std::string& path)
@@ -99,7 +107,31 @@
     {
         manager->delete_();
     }
+
+    std::string generateCSR(std::vector<std::string> alternativeNames,
+                            std::string challengePassword, std::string city,
+                            std::string commonName, std::string contactPerson,
+                            std::string country, std::string email,
+                            std::string givenName, std::string initials,
+                            int64_t keyBitLength, std::string keyCurveId,
+                            std::string keyPairAlgorithm,
+                            std::vector<std::string> keyUsage,
+                            std::string organization,
+                            std::string organizationalUnit, std::string state,
+                            std::string surname, std::string unstructuredName)
+    {
+        return (manager->generateCSR(
+            alternativeNames, challengePassword, city, commonName,
+            contactPerson, country, email, givenName, initials, keyBitLength,
+            keyCurveId, keyPairAlgorithm, keyUsage, organization,
+            organizationalUnit, state, surname, unstructuredName));
+    }
+    std::string cSR()
+    {
+        return (csr->cSR());
+    }
     phosphor::certs::Manager* manager;
+    phosphor::certs::CSR* csr;
 };
 
 /** @brief Check if server install routine is invoked for server setup
@@ -272,8 +304,10 @@
     std::string installPath(certDir + "/" + certificateFile);
     std::string verifyPath(installPath);
     std::string verifyUnit(unit);
+    // Get default event loop
     auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
-    Manager manager(bus, objPath.c_str(), type, std::move(unit),
+    auto event = sdeventplus::Event::get_default();
+    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
                     std::move(installPath));
     MainApp mainApp(&manager);
     // delete certificate file and verify file is deleted
@@ -292,7 +326,8 @@
     std::string verifyPath(installPath);
     std::string verifyUnit(unit);
     auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
-    Manager manager(bus, objPath.c_str(), type, std::move(unit),
+    auto event = sdeventplus::Event::get_default();
+    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
                     std::move(installPath));
     MainApp mainApp(&manager);
     mainApp.install(certificateFile);
@@ -411,7 +446,8 @@
     std::string verifyPath(installPath);
     std::string verifyUnit(unit);
     auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
-    Manager manager(bus, objPath.c_str(), type, std::move(unit),
+    auto event = sdeventplus::Event::get_default();
+    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
                     std::move(installPath));
     MainApp mainApp(&manager);
     EXPECT_THROW(
@@ -441,9 +477,9 @@
     std::string type("client");
     std::string installPath(certDir + "/" + certificateFile);
     std::string verifyPath(installPath);
-    std::string verifyUnit(unit);
     auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
-    Manager manager(bus, objPath.c_str(), type, std::move(unit),
+    auto event = sdeventplus::Event::get_default();
+    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
                     std::move(installPath));
     MainApp mainApp(&manager);
     mainApp.install(certificateFile);
@@ -462,3 +498,110 @@
         },
         NotAllowed);
 }
+
+TEST_F(TestCertificates, TestGenerateCSR)
+{
+    std::string endpoint("https");
+    std::string unit("");
+    std::string type("Server");
+    std::string installPath(certDir + "/" + certificateFile);
+    std::string verifyPath(installPath);
+    std::string CSRPath(certDir + "/" + CSRFile);
+    std::string privateKeyPath(certDir + "/" + privateKeyFile);
+    std::vector<std::string> alternativeNames{"localhost1", "localhost2"};
+    std::string challengePassword("0penBmc");
+    std::string city("HYB");
+    std::string commonName("abc.com");
+    std::string contactPerson("Admin");
+    std::string country("IN");
+    std::string email("admin@in.ibm.com");
+    std::string givenName("givenName");
+    std::string initials("G");
+    int64_t keyBitLength(2048);
+    std::string keyCurveId("0");
+    std::string keyPairAlgorithm("RSA");
+    std::vector<std::string> keyUsage{"serverAuth", "clientAuth"};
+    std::string organization("IBM");
+    std::string organizationalUnit("ISDL");
+    std::string state("TS");
+    std::string surname("surname");
+    std::string unstructuredName("unstructuredName");
+    auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
+    auto event = sdeventplus::Event::get_default();
+    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
+                    std::move(installPath));
+    Status status;
+    CSR csr(bus, objPath.c_str(), CSRPath.c_str(), status);
+    MainApp mainApp(&manager, &csr);
+    mainApp.generateCSR(alternativeNames, challengePassword, city, commonName,
+                        contactPerson, country, email, givenName, initials,
+                        keyBitLength, keyCurveId, keyPairAlgorithm, keyUsage,
+                        organization, organizationalUnit, state, surname,
+                        unstructuredName);
+    std::string csrData("");
+    // generateCSR takes considerable time to create CSR and privateKey Files
+    EXPECT_FALSE(fs::exists(CSRPath));
+    EXPECT_FALSE(fs::exists(privateKeyPath));
+    EXPECT_THROW(
+        {
+            try
+            {
+                csrData = csr.cSR();
+            }
+            catch (const InternalFailure& e)
+            {
+                throw;
+            }
+        },
+        InternalFailure);
+    // wait for 10 sec to get CSR and privateKey Files generated
+    sleep(10);
+    EXPECT_TRUE(fs::exists(CSRPath));
+    EXPECT_TRUE(fs::exists(privateKeyPath));
+    csrData = csr.cSR();
+    ASSERT_NE("", csrData.c_str());
+}
+
+TEST_F(TestCertificates, TestGenerateCSRError)
+{
+    std::string endpoint("https");
+    std::string unit("");
+    std::string type("Server");
+    std::string installPath(certDir + "/" + certificateFile);
+    std::string verifyPath(installPath);
+    std::string CSRPath(certDir + "/" + CSRFile);
+    std::string privateKeyPath(certDir + "/" + privateKeyFile);
+    std::vector<std::string> alternativeNames{"localhost1", "localhost2"};
+    std::string challengePassword("0penBmc");
+    std::string city;
+    std::string commonName;
+    std::string contactPerson;
+    std::string country;
+    std::string email;
+    std::string givenName;
+    std::string initials;
+    int64_t keyBitLength(12);
+    std::string keyCurveId;
+    std::string keyPairAlgorithm;
+    std::vector<std::string> keyUsage{"serverAuth", "clientAuth"};
+    std::string organization;
+    std::string organizationalUnit;
+    std::string state;
+    std::string surname;
+    std::string unstructuredName;
+    auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
+    auto event = sdeventplus::Event::get_default();
+    Manager manager(bus, event, objPath.c_str(), type, std::move(unit),
+                    std::move(installPath));
+    Status status;
+    CSR csr(bus, objPath.c_str(), CSRPath.c_str(), status);
+    MainApp mainApp(&manager, &csr);
+    mainApp.generateCSR(alternativeNames, challengePassword, city, commonName,
+                        contactPerson, country, email, givenName, initials,
+                        keyBitLength, keyCurveId, keyPairAlgorithm, keyUsage,
+                        organization, organizationalUnit, state, surname,
+                        unstructuredName);
+    sleep(10);
+    EXPECT_FALSE(fs::exists(CSRPath));
+    EXPECT_FALSE(fs::exists(privateKeyPath));
+}