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
diff --git a/certs_manager.hpp b/certs_manager.hpp
index 1e6ca81..355840d 100644
--- a/certs_manager.hpp
+++ b/certs_manager.hpp
@@ -185,8 +185,10 @@
/** @brief Write private key data to file
*
* @param[in] pKey - pointer to private key
+ * @param[in] privKeyFileName - private key filename
*/
- void writePrivateKey(const EVP_PKEY_Ptr& pKey);
+ void writePrivateKey(const EVP_PKEY_Ptr& pKey,
+ const std::string& privKeyFileName);
/** @brief Add the specified CSR field with the data
* @param[in] x509Name - Structure used in setting certificate properties
@@ -213,6 +215,18 @@
*/
void createCertificate();
+ /** @brief Create RSA private key file
+ * Create RSA private key file by generating rsa key if not created
+ */
+ void createRSAPrivateKeyFile();
+
+ /** @brief Getting RSA private key
+ * Gettting RSA private key from generated file
+ * @param[in] keyBitLength - Key bit length
+ * @return Pointer to RSA key
+ */
+ EVP_PKEY_Ptr getRSAKeyPair(const int64_t keyBitLength);
+
/** @brief sdbusplus handler */
sdbusplus::bus::bus& bus;
@@ -242,6 +256,9 @@
/** @brief Watch on self signed certificates */
std::unique_ptr<Watch> certWatchPtr = nullptr;
+
+ /** @brif Parent path i.e certificate directory path */
+ fs::path certParentInstallPath;
};
} // namespace certs
} // namespace phosphor
diff --git a/configure.ac b/configure.ac
index b56b83a..81888b3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -128,6 +128,10 @@
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])
+AC_ARG_VAR(RSA_PRIV_KEY_FILE_NAME, [The rsa private key file.])
+AS_IF([test "x$RSA_PRIV_KEY_FILE_NAME" == "x"], [RSA_PRIV_KEY_FILE_NAME=".rsaprivkey.pem"])
+AC_DEFINE_UNQUOTED([RSA_PRIV_KEY_FILE_NAME], ["$RSA_PRIV_KEY_FILE_NAME"], [The rsa private key file])
+
# Create configured output
AC_CONFIG_FILES([Makefile test/Makefile])
AC_OUTPUT
diff --git a/test/certs_manager_test.cpp b/test/certs_manager_test.cpp
index 6e7f878..d4fd213 100644
--- a/test/certs_manager_test.cpp
+++ b/test/certs_manager_test.cpp
@@ -41,6 +41,7 @@
certificateFile = "cert.pem";
CSRFile = "domain.csr";
privateKeyFile = "privkey.pem";
+ rsaPrivateKeyFilePath = certDir + "/.rsaprivkey.pem";
std::string cmd = "openssl req -x509 -sha256 -newkey rsa:2048 ";
cmd += "-keyout cert.pem -out cert.pem -days 3650 ";
cmd += "-subj "
@@ -85,7 +86,7 @@
protected:
sdbusplus::bus::bus bus;
- std::string certificateFile, CSRFile, privateKeyFile;
+ std::string certificateFile, CSRFile, privateKeyFile, rsaPrivateKeyFilePath;
std::string certDir;
};
@@ -565,70 +566,6 @@
ASSERT_NE("", csrData.c_str());
}
-/** @brief Check default KeyBitLength is used if Key bit length is not given*/
-TEST_F(TestCertificates, TestGenerateCSRwithDefaultKeyBitLength)
-{
- 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("Password");
- 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 = 0;
- std::string keyCurveId("0");
- std::string keyPairAlgorithm("RSA");
- std::vector<std::string> keyUsage{"serverAuth", "clientAuth"};
- std::string organization("IBM");
- std::string organizationalUnit("orgUnit");
- 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());
-}
-
/** @brief Check if ECC key pair is generated when user is not given algorithm
* type. At present RSA and EC key pair algorithm are supported
*/
@@ -861,3 +798,161 @@
EXPECT_TRUE(fs::exists(CSRPath));
EXPECT_TRUE(fs::exists(privateKeyPath));
}
+
+/** @brief Check error is thrown if giving unsupported ket bit length to
+ * generate rsa key
+ */
+TEST_F(TestCertificates, TestRSAKeyWithUnsupportedKeyBitLength)
+{
+ 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("Password");
+ std::string city("BLR");
+ 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(4096);
+ std::string keyCurveId("secp521r1");
+ std::string keyPairAlgorithm("RSA");
+ std::vector<std::string> keyUsage{"serverAuth", "clientAuth"};
+ std::string organization("IBM");
+ std::string organizationalUnit("orgUnit");
+ 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);
+ EXPECT_FALSE(fs::exists(CSRPath));
+ EXPECT_FALSE(fs::exists(privateKeyPath));
+}
+
+/** @brief Check error is thrown if generated rsa key file is not present
+ */
+TEST_F(TestCertificates, TestRSAKeyFileNotPresentCase)
+{
+ 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("Password");
+ std::string city("BLR");
+ 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("secp521r1");
+ std::string keyPairAlgorithm("RSA");
+ std::vector<std::string> keyUsage{"serverAuth", "clientAuth"};
+ std::string organization("IBM");
+ std::string organizationalUnit("orgUnit");
+ 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));
+
+ // Removing generated RSA key file
+ fs::remove(rsaPrivateKeyFilePath);
+
+ 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);
+ EXPECT_FALSE(fs::exists(CSRPath));
+ EXPECT_FALSE(fs::exists(privateKeyPath));
+}
+
+/** @brief Check private key file is created from generated rsa key file is
+ * `present
+ */
+TEST_F(TestCertificates, TestRSAKeyFromRSAKeyFileIsWrittenIntoPrivateKeyFile)
+{
+ 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("Password");
+ std::string city("BLR");
+ 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("secp521r1");
+ std::string keyPairAlgorithm("RSA");
+ std::vector<std::string> keyUsage{"serverAuth", "clientAuth"};
+ std::string organization("IBM");
+ std::string organizationalUnit("orgUnit");
+ 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);
+ sleep(10);
+ EXPECT_TRUE(fs::exists(CSRPath));
+ EXPECT_TRUE(fs::exists(privateKeyPath));
+}
+
+/** @brief Check RSA key is generted during application startup*/
+TEST_F(TestCertificates, TestGenerateRSAPrivateKeyFile)
+{
+ std::string endpoint("https");
+ std::string unit("");
+ std::string type("Server");
+ std::string installPath(certDir + "/" + certificateFile);
+ auto objPath = std::string(OBJPATH) + '/' + 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));
+ EXPECT_TRUE(fs::exists(rsaPrivateKeyFilePath));
+}