Generate RSA Private Key file during application startup

Generating RSA private key file during application startup if rsa key
file is not found. Here, the rsa private key file is a hidden file
(.rsaprivkey.pem) and placed in certificate file install path which is
given during application startup.

This generated rsa private key file will be used to create private key
and csr files if certificate manager received the generateCSR request
with key pair algorithm as RSA. So, the every time rsa key generation
is avoided, because rsa key is generated with keybitlength as 2048
during application startup.

From this change, certificate manager will support only 2048 as key
bit length to generated rsa key pair. If user given other than 2048,
application will throw error.

Tested By:
 - Added below unit test case
    * To check rsa private key file is generated during application
      startup.
    * To validate unsupported key bit length.
    * To check rsa private key file is present or not.
    * To check rsa private key is picked from rsa private key file
      while receive the generateCSR request.
 - Manual test case
    * Restarted certificate manager application to check rsa private
      key file is generated.
      systemctl restart phosphor-certificate-manager@bmcweb.service

    * Invoked genearteCSR request by using curl command to check
      generated rsa private key file is used to create private key
      and csr file.
      curl -c cjar -b cjar -k -H "X-Auth-Token: $bmc_token" -X POST
      https://${bmc}/redfish/v1/CertificateService/Actions/
      CertificateService.GenerateCSR/ -d @generate_https.json

Change-Id: I876779f1ab36f52774c52041d68304a610ea261b
Signed-off-by: Ramesh Iyyar <rameshi1@in.ibm.com>
diff --git a/certs_manager.cpp b/certs_manager.cpp
index b189bb9..06ddf68 100644
--- a/certs_manager.cpp
+++ b/certs_manager.cpp
@@ -18,14 +18,26 @@
 
 using X509_REQ_Ptr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>;
 using BIGNUM_Ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
+using InvalidArgument =
+    sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
+using Argument = xyz::openbmc_project::Common::InvalidArgument;
+
+constexpr auto SUPPORTED_KEYBITLENGTH = 2048;
 
 Manager::Manager(sdbusplus::bus::bus& bus, sdeventplus::Event& event,
                  const char* path, const CertificateType& type,
                  UnitsToRestart&& unit, CertInstallPath&& installPath) :
     Ifaces(bus, path),
     bus(bus), event(event), objectPath(path), certType(type),
-    unitToRestart(std::move(unit)), certInstallPath(std::move(installPath))
+    unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)),
+    certParentInstallPath(fs::path(certInstallPath).parent_path())
 {
+    // Generating RSA private key file if certificate type is server/client
+    if (certType != AUTHORITY)
+    {
+        createRSAPrivateKeyFile();
+    }
+
     // restore any existing certificates
     if (fs::exists(certInstallPath))
     {
@@ -251,15 +263,11 @@
 
     // Used EC algorithm as default if user did not give algorithm type.
     if (keyPairAlgorithm == "RSA")
-        pKey = std::move(generateRSAKeyPair(keyBitLength));
+        pKey = getRSAKeyPair(keyBitLength);
     else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
-        pKey = std::move(generateECKeyPair(keyCurveId));
+        pKey = generateECKeyPair(keyCurveId);
     else
     {
-        using InvalidArgument =
-            sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
-        using Argument = xyz::openbmc_project::Common::InvalidArgument;
-
         log<level::ERR>("Given Key pair algorithm is not supported. Supporting "
                         "RSA and EC only");
         elog<InvalidArgument>(
@@ -275,7 +283,7 @@
     }
 
     // Write private key to file
-    writePrivateKey(pKey);
+    writePrivateKey(pKey, PRIV_KEY_FILE_NAME);
 
     // set sign key of x509 req
     ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
@@ -286,9 +294,8 @@
     }
 
     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);
+    fs::path csrFilePath = certParentInstallPath / CSR_FILE_NAME;
+    writeCSR(csrFilePath.string(), x509Req);
 }
 
 EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength)
@@ -396,12 +403,12 @@
     return pKey;
 }
 
-void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey)
+void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey,
+                              const std::string& privKeyFileName)
 {
     log<level::INFO>("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;
+    fs::path privKeyPath = certParentInstallPath / privKeyFileName;
 
     FILE* fp = std::fopen(privKeyPath.c_str(), "w");
     if (fp == NULL)
@@ -503,5 +510,61 @@
             Reason("Existing certificate file is corrupted"));
     }
 }
+
+void Manager::createRSAPrivateKeyFile()
+{
+    fs::path rsaPrivateKeyFileName =
+        certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
+
+    try
+    {
+        if (!fs::exists(rsaPrivateKeyFileName))
+        {
+            writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH),
+                            RSA_PRIV_KEY_FILE_NAME);
+        }
+    }
+    catch (const InternalFailure& e)
+    {
+        report<InternalFailure>();
+    }
+}
+
+EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength)
+{
+    if (keyBitLength != SUPPORTED_KEYBITLENGTH)
+    {
+        log<level::ERR>(
+            "Given Key bit length is not supported",
+            entry("GIVENKEYBITLENGTH=%d", keyBitLength),
+            entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH));
+        elog<InvalidArgument>(
+            Argument::ARGUMENT_NAME("KEYBITLENGTH"),
+            Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
+    }
+    fs::path rsaPrivateKeyFileName =
+        certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
+
+    FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
+    if (!privateKeyFile)
+    {
+        log<level::ERR>("Unable to open RSA private key file to read",
+                        entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()),
+                        entry("ERRORREASON=%s", strerror(errno)));
+        elog<InternalFailure>();
+    }
+
+    EVP_PKEY_Ptr privateKey(
+        PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
+        ::EVP_PKEY_free);
+    std::fclose(privateKeyFile);
+
+    if (!privateKey)
+    {
+        log<level::ERR>("Error occured during PEM_read_PrivateKey call");
+        elog<InternalFailure>();
+    }
+    return privateKey;
+}
 } // namespace certs
 } // namespace phosphor