Append private key from the system to CA signed certificate

A CSR request is intiated which creates a private key file
and CSR file in the system.

CSR file is passed onto the CA to sign the certificate.

If the CA signed certificate file does not have private key
use the private key generated as part of the CSR request
from the system.

After sucessfull validation append the private key with the
CA signed file and store in the system.

Tested:
1) Verifid when uploading a certificate file without private
key system is using the private key file from the system.

Change-Id: I9a5313af64f87fdb1d9189558c95ab75ce317a73
Signed-off-by: Marri Devender Rao <devenrao@in.ibm.com>
diff --git a/certificate.cpp b/certificate.cpp
index af8635e..b36e5f4 100644
--- a/certificate.cpp
+++ b/certificate.cpp
@@ -1,3 +1,5 @@
+#include "config.h"
+
 #include "certificate.hpp"
 
 #include <openssl/bio.h>
@@ -88,7 +90,18 @@
     typeFuncMap[SERVER] = installHelper;
     typeFuncMap[CLIENT] = installHelper;
     typeFuncMap[AUTHORITY] = [](auto filePath) {};
+
+    auto appendPrivateKey = [this](const std::string& filePath) {
+        checkAndAppendPrivateKey(filePath);
+    };
+
+    appendKeyMap[SERVER] = appendPrivateKey;
+    appendKeyMap[CLIENT] = appendPrivateKey;
+    appendKeyMap[AUTHORITY] = [](const std::string& filePath) {};
+
+    // install the certificate
     install(uploadPath, isSkipUnitReload);
+
     this->emit_object_added();
 }
 
@@ -237,14 +250,23 @@
         elog<InvalidCertificate>(Reason("Certificate validation failed"));
     }
 
-    // Invoke type specific compare keys function.
-    auto iter = typeFuncMap.find(certType);
-    if (iter == typeFuncMap.end())
+    // Invoke type specific append private key function.
+    auto appendIter = appendKeyMap.find(certType);
+    if (appendIter == appendKeyMap.end())
     {
         log<level::ERR>("Unsupported Type", entry("TYPE=%s", certType.c_str()));
         elog<InternalFailure>();
     }
-    iter->second(filePath);
+    appendIter->second(filePath);
+
+    // Invoke type specific compare keys function.
+    auto compIter = typeFuncMap.find(certType);
+    if (compIter == typeFuncMap.end())
+    {
+        log<level::ERR>("Unsupported Type", entry("TYPE=%s", certType.c_str()));
+        elog<InternalFailure>();
+    }
+    compIter->second(filePath);
 
     // Copy the certificate to the installation path
     // During bootup will be parsing existing file so no need to
@@ -406,6 +428,61 @@
     }
     return cert;
 }
+
+void Certificate::checkAndAppendPrivateKey(const std::string& filePath)
+{
+    BIO_MEM_Ptr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
+    if (!keyBio)
+    {
+        log<level::ERR>("Error occured during BIO_s_file call",
+                        entry("FILE=%s", filePath.c_str()));
+        elog<InternalFailure>();
+    }
+    BIO_read_filename(keyBio.get(), filePath.c_str());
+
+    EVP_PKEY_Ptr priKey(
+        PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
+        ::EVP_PKEY_free);
+    if (!priKey)
+    {
+        log<level::INFO>("Private key not present in file",
+                         entry("FILE=%s", filePath.c_str()));
+        fs::path privateKeyFile = fs::path(certInstallPath).parent_path();
+        privateKeyFile = privateKeyFile / PRIV_KEY_FILE_NAME;
+        if (!fs::exists(privateKeyFile))
+        {
+            log<level::ERR>("Private key file is not found",
+                            entry("FILE=%s", privateKeyFile.c_str()));
+            elog<InternalFailure>();
+        }
+
+        std::ifstream privKeyFileStream;
+        std::ofstream certFileStream;
+        privKeyFileStream.exceptions(std::ifstream::failbit |
+                                     std::ifstream::badbit |
+                                     std::ifstream::eofbit);
+        certFileStream.exceptions(std::ofstream::failbit |
+                                  std::ofstream::badbit |
+                                  std::ofstream::eofbit);
+        try
+        {
+            privKeyFileStream.open(privateKeyFile);
+            certFileStream.open(filePath, std::ios::app);
+            certFileStream << privKeyFileStream.rdbuf() << std::flush;
+            privKeyFileStream.close();
+            certFileStream.close();
+        }
+        catch (const std::exception& e)
+        {
+            log<level::ERR>("Failed to append private key",
+                            entry("ERR=%s", e.what()),
+                            entry("SRC=%s", privateKeyFile.c_str()),
+                            entry("DST=%s", filePath.c_str()));
+            elog<InternalFailure>();
+        }
+    }
+}
+
 bool Certificate::compareKeys(const std::string& filePath)
 {
     log<level::INFO>("Certificate compareKeys",