1cfbc8dc8SJayanth Othayoth #include "certs_manager.hpp" 2cfbc8dc8SJayanth Othayoth 3f4682712SMarri Devender Rao #include <openssl/pem.h> 4f4682712SMarri Devender Rao #include <unistd.h> 5f4682712SMarri Devender Rao 66ceec40bSMarri Devender Rao #include <phosphor-logging/elog-errors.hpp> 713bf74e4SMarri Devender Rao #include <xyz/openbmc_project/Certs/error.hpp> 8cfbc8dc8SJayanth Othayoth #include <xyz/openbmc_project/Common/error.hpp> 9cfbc8dc8SJayanth Othayoth namespace phosphor 10cfbc8dc8SJayanth Othayoth { 11cfbc8dc8SJayanth Othayoth namespace certs 12cfbc8dc8SJayanth Othayoth { 131396511dSMarri Devender Rao using InternalFailure = 141396511dSMarri Devender Rao sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 15ffad1ef1SMarri Devender Rao using InvalidCertificate = 16ffad1ef1SMarri Devender Rao sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate; 17ffad1ef1SMarri Devender Rao using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON; 18cfbc8dc8SJayanth Othayoth 19f4682712SMarri Devender Rao using X509_REQ_Ptr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>; 20f4682712SMarri Devender Rao using BIGNUM_Ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>; 21c6e58c7eSRamesh Iyyar using InvalidArgument = 22c6e58c7eSRamesh Iyyar sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument; 23c6e58c7eSRamesh Iyyar using Argument = xyz::openbmc_project::Common::InvalidArgument; 24c6e58c7eSRamesh Iyyar 25c6e58c7eSRamesh Iyyar constexpr auto SUPPORTED_KEYBITLENGTH = 2048; 26f4682712SMarri Devender Rao 27f4682712SMarri Devender Rao Manager::Manager(sdbusplus::bus::bus& bus, sdeventplus::Event& event, 28f4682712SMarri Devender Rao const char* path, const CertificateType& type, 29f4682712SMarri Devender Rao UnitsToRestart&& unit, CertInstallPath&& installPath) : 306ceec40bSMarri Devender Rao Ifaces(bus, path), 31f4682712SMarri Devender Rao bus(bus), event(event), objectPath(path), certType(type), 32c6e58c7eSRamesh Iyyar unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)), 33c6e58c7eSRamesh Iyyar certParentInstallPath(fs::path(certInstallPath).parent_path()) 34cfbc8dc8SJayanth Othayoth { 35b57d75e2SMarri Devender Rao // create parent certificate path if not existing 36b57d75e2SMarri Devender Rao try 37b57d75e2SMarri Devender Rao { 38b57d75e2SMarri Devender Rao if (!fs::exists(certParentInstallPath)) 39b57d75e2SMarri Devender Rao { 40b57d75e2SMarri Devender Rao fs::create_directories(certParentInstallPath); 41b57d75e2SMarri Devender Rao } 42b57d75e2SMarri Devender Rao } 43b57d75e2SMarri Devender Rao catch (fs::filesystem_error& e) 44b57d75e2SMarri Devender Rao { 45b57d75e2SMarri Devender Rao log<level::ERR>("Failed to create directory", entry("ERR=%s", e.what()), 46b57d75e2SMarri Devender Rao entry("DIRECTORY=%s", certParentInstallPath.c_str())); 47b57d75e2SMarri Devender Rao report<InternalFailure>(); 48b57d75e2SMarri Devender Rao } 49b57d75e2SMarri Devender Rao 50c6e58c7eSRamesh Iyyar // Generating RSA private key file if certificate type is server/client 51c6e58c7eSRamesh Iyyar if (certType != AUTHORITY) 52c6e58c7eSRamesh Iyyar { 53c6e58c7eSRamesh Iyyar createRSAPrivateKeyFile(); 54c6e58c7eSRamesh Iyyar } 55c6e58c7eSRamesh Iyyar 56ffad1ef1SMarri Devender Rao // restore any existing certificates 57bf7c588cSMarri Devender Rao if (fs::exists(certInstallPath)) 58bf7c588cSMarri Devender Rao { 59ffad1ef1SMarri Devender Rao createCertificate(); 60ffad1ef1SMarri Devender Rao } 61ffad1ef1SMarri Devender Rao 62ffad1ef1SMarri Devender Rao // watch is not required for authority certificates 63ffad1ef1SMarri Devender Rao if (certType != AUTHORITY) 64ffad1ef1SMarri Devender Rao { 65ffad1ef1SMarri Devender Rao // watch for certificate file create/replace 66ffad1ef1SMarri Devender Rao certWatchPtr = std::make_unique< 67ffad1ef1SMarri Devender Rao Watch>(event, certInstallPath, [this]() { 68bf7c588cSMarri Devender Rao try 69bf7c588cSMarri Devender Rao { 70ffad1ef1SMarri Devender Rao // if certificate file existing update it 71ffad1ef1SMarri Devender Rao if (certificatePtr != nullptr) 72ffad1ef1SMarri Devender Rao { 73ffad1ef1SMarri Devender Rao log<level::INFO>( 74ffad1ef1SMarri Devender Rao "Inotify callback to update certificate properties"); 75ffad1ef1SMarri Devender Rao certificatePtr->populateProperties(); 76ffad1ef1SMarri Devender Rao } 77ffad1ef1SMarri Devender Rao else 78ffad1ef1SMarri Devender Rao { 79ffad1ef1SMarri Devender Rao log<level::INFO>( 80ffad1ef1SMarri Devender Rao "Inotify callback to create certificate object"); 81ffad1ef1SMarri Devender Rao createCertificate(); 82ffad1ef1SMarri Devender Rao } 83bf7c588cSMarri Devender Rao } 84bf7c588cSMarri Devender Rao catch (const InternalFailure& e) 85bf7c588cSMarri Devender Rao { 86ffad1ef1SMarri Devender Rao commit<InternalFailure>(); 87bf7c588cSMarri Devender Rao } 88bf7c588cSMarri Devender Rao catch (const InvalidCertificate& e) 89bf7c588cSMarri Devender Rao { 90ffad1ef1SMarri Devender Rao commit<InvalidCertificate>(); 91bf7c588cSMarri Devender Rao } 92ffad1ef1SMarri Devender Rao }); 93bf7c588cSMarri Devender Rao } 94dd74bd20SJayanth Othayoth } 95589159f2SJayanth Othayoth 96*06a69d7bSZbigniew Kurzynski std::string Manager::install(const std::string filePath) 97cfbc8dc8SJayanth Othayoth { 981396511dSMarri Devender Rao using NotAllowed = 991396511dSMarri Devender Rao sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed; 1001396511dSMarri Devender Rao using Reason = xyz::openbmc_project::Common::NotAllowed::REASON; 1011396511dSMarri Devender Rao // TODO: Issue#3 At present supporting only one certificate to be 1021396511dSMarri Devender Rao // uploaded this need to be revisited to support multiple 1031396511dSMarri Devender Rao // certificates 1041396511dSMarri Devender Rao if (certificatePtr != nullptr) 1051396511dSMarri Devender Rao { 1061396511dSMarri Devender Rao elog<NotAllowed>(Reason("Certificate already exist")); 1071396511dSMarri Devender Rao } 108ffad1ef1SMarri Devender Rao 1091396511dSMarri Devender Rao auto certObjectPath = objectPath + '/' + '1'; 1108f80c35bSMarri Devender Rao certificatePtr = std::make_unique<Certificate>( 1118f80c35bSMarri Devender Rao bus, certObjectPath, certType, unitToRestart, certInstallPath, filePath, 112ffad1ef1SMarri Devender Rao false, certWatchPtr); 113*06a69d7bSZbigniew Kurzynski return certObjectPath; 114589159f2SJayanth Othayoth } 115ae70b3daSDeepak Kodihalli 116ae70b3daSDeepak Kodihalli void Manager::delete_() 117ae70b3daSDeepak Kodihalli { 1186ceec40bSMarri Devender Rao // TODO: #Issue 4 when a certificate is deleted system auto generates 1196ceec40bSMarri Devender Rao // certificate file. At present we are not supporting creation of 1206ceec40bSMarri Devender Rao // certificate object for the auto-generated certificate file as 1216ceec40bSMarri Devender Rao // deletion if only applicable for REST server and Bmcweb does not allow 1226ceec40bSMarri Devender Rao // deletion of certificates 1236ceec40bSMarri Devender Rao if (certificatePtr != nullptr) 124ae70b3daSDeepak Kodihalli { 1256ceec40bSMarri Devender Rao certificatePtr.reset(nullptr); 126ae70b3daSDeepak Kodihalli } 127ae70b3daSDeepak Kodihalli } 128f4682712SMarri Devender Rao 129f4682712SMarri Devender Rao std::string Manager::generateCSR( 130f4682712SMarri Devender Rao std::vector<std::string> alternativeNames, std::string challengePassword, 131f4682712SMarri Devender Rao std::string city, std::string commonName, std::string contactPerson, 132f4682712SMarri Devender Rao std::string country, std::string email, std::string givenName, 133f4682712SMarri Devender Rao std::string initials, int64_t keyBitLength, std::string keyCurveId, 134f4682712SMarri Devender Rao std::string keyPairAlgorithm, std::vector<std::string> keyUsage, 135f4682712SMarri Devender Rao std::string organization, std::string organizationalUnit, std::string state, 136f4682712SMarri Devender Rao std::string surname, std::string unstructuredName) 137f4682712SMarri Devender Rao { 138f4682712SMarri Devender Rao // We support only one CSR. 139f4682712SMarri Devender Rao csrPtr.reset(nullptr); 140f4682712SMarri Devender Rao auto pid = fork(); 141f4682712SMarri Devender Rao if (pid == -1) 142f4682712SMarri Devender Rao { 143f4682712SMarri Devender Rao log<level::ERR>("Error occurred during forking process"); 144f4682712SMarri Devender Rao report<InternalFailure>(); 145f4682712SMarri Devender Rao } 146f4682712SMarri Devender Rao else if (pid == 0) 147f4682712SMarri Devender Rao { 148f4682712SMarri Devender Rao try 149f4682712SMarri Devender Rao { 150f4682712SMarri Devender Rao generateCSRHelper(alternativeNames, challengePassword, city, 151f4682712SMarri Devender Rao commonName, contactPerson, country, email, 152f4682712SMarri Devender Rao givenName, initials, keyBitLength, keyCurveId, 153f4682712SMarri Devender Rao keyPairAlgorithm, keyUsage, organization, 154f4682712SMarri Devender Rao organizationalUnit, state, surname, 155f4682712SMarri Devender Rao unstructuredName); 156f4682712SMarri Devender Rao exit(EXIT_SUCCESS); 157f4682712SMarri Devender Rao } 158f4682712SMarri Devender Rao catch (const InternalFailure& e) 159f4682712SMarri Devender Rao { 160f4682712SMarri Devender Rao // commit the error reported in child process and exit 161f4682712SMarri Devender Rao // Callback method from SDEvent Loop looks for exit status 162f4682712SMarri Devender Rao exit(EXIT_FAILURE); 163f4682712SMarri Devender Rao commit<InternalFailure>(); 164f4682712SMarri Devender Rao } 165f4682712SMarri Devender Rao } 166f4682712SMarri Devender Rao else 167f4682712SMarri Devender Rao { 168f4682712SMarri Devender Rao using namespace sdeventplus::source; 169f4682712SMarri Devender Rao Child::Callback callback = [this](Child& eventSource, 170f4682712SMarri Devender Rao const siginfo_t* si) { 171f4682712SMarri Devender Rao eventSource.set_enabled(Enabled::On); 172f4682712SMarri Devender Rao if (si->si_status != 0) 173f4682712SMarri Devender Rao { 174f4682712SMarri Devender Rao this->createCSRObject(Status::FAILURE); 175f4682712SMarri Devender Rao } 176f4682712SMarri Devender Rao else 177f4682712SMarri Devender Rao { 178f4682712SMarri Devender Rao this->createCSRObject(Status::SUCCESS); 179f4682712SMarri Devender Rao } 180f4682712SMarri Devender Rao }; 181f4682712SMarri Devender Rao try 182f4682712SMarri Devender Rao { 183f4682712SMarri Devender Rao sigset_t ss; 184f4682712SMarri Devender Rao if (sigemptyset(&ss) < 0) 185f4682712SMarri Devender Rao { 186f4682712SMarri Devender Rao log<level::ERR>("Unable to initialize signal set"); 187f4682712SMarri Devender Rao elog<InternalFailure>(); 188f4682712SMarri Devender Rao } 189f4682712SMarri Devender Rao if (sigaddset(&ss, SIGCHLD) < 0) 190f4682712SMarri Devender Rao { 191f4682712SMarri Devender Rao log<level::ERR>("Unable to add signal to signal set"); 192f4682712SMarri Devender Rao elog<InternalFailure>(); 193f4682712SMarri Devender Rao } 194f4682712SMarri Devender Rao 195f4682712SMarri Devender Rao // Block SIGCHLD first, so that the event loop can handle it 196f4682712SMarri Devender Rao if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0) 197f4682712SMarri Devender Rao { 198f4682712SMarri Devender Rao log<level::ERR>("Unable to block signal"); 199f4682712SMarri Devender Rao elog<InternalFailure>(); 200f4682712SMarri Devender Rao } 201f4682712SMarri Devender Rao if (childPtr) 202f4682712SMarri Devender Rao { 203f4682712SMarri Devender Rao childPtr.reset(); 204f4682712SMarri Devender Rao } 205f4682712SMarri Devender Rao childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED, 206f4682712SMarri Devender Rao std::move(callback)); 207f4682712SMarri Devender Rao } 208f4682712SMarri Devender Rao catch (const InternalFailure& e) 209f4682712SMarri Devender Rao { 210f4682712SMarri Devender Rao commit<InternalFailure>(); 211f4682712SMarri Devender Rao } 212f4682712SMarri Devender Rao } 213f4682712SMarri Devender Rao auto csrObjectPath = objectPath + '/' + "csr"; 214f4682712SMarri Devender Rao return csrObjectPath; 215f4682712SMarri Devender Rao } 216f4682712SMarri Devender Rao 217ffad1ef1SMarri Devender Rao CertificatePtr& Manager::getCertificate() 218ffad1ef1SMarri Devender Rao { 219ffad1ef1SMarri Devender Rao return certificatePtr; 220ffad1ef1SMarri Devender Rao } 221ffad1ef1SMarri Devender Rao 222f4682712SMarri Devender Rao void Manager::generateCSRHelper( 223f4682712SMarri Devender Rao std::vector<std::string> alternativeNames, std::string challengePassword, 224f4682712SMarri Devender Rao std::string city, std::string commonName, std::string contactPerson, 225f4682712SMarri Devender Rao std::string country, std::string email, std::string givenName, 226f4682712SMarri Devender Rao std::string initials, int64_t keyBitLength, std::string keyCurveId, 227f4682712SMarri Devender Rao std::string keyPairAlgorithm, std::vector<std::string> keyUsage, 228f4682712SMarri Devender Rao std::string organization, std::string organizationalUnit, std::string state, 229f4682712SMarri Devender Rao std::string surname, std::string unstructuredName) 230f4682712SMarri Devender Rao { 231f4682712SMarri Devender Rao int ret = 0; 232f4682712SMarri Devender Rao 233f4682712SMarri Devender Rao // set version of x509 req 234f4682712SMarri Devender Rao int nVersion = 1; 235f4682712SMarri Devender Rao // TODO: Issue#6 need to make version number configurable 236f4682712SMarri Devender Rao X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free); 237f4682712SMarri Devender Rao ret = X509_REQ_set_version(x509Req.get(), nVersion); 238f4682712SMarri Devender Rao if (ret == 0) 239f4682712SMarri Devender Rao { 240f4682712SMarri Devender Rao log<level::ERR>("Error occured during X509_REQ_set_version call"); 241f4682712SMarri Devender Rao elog<InternalFailure>(); 242f4682712SMarri Devender Rao } 243f4682712SMarri Devender Rao 244f4682712SMarri Devender Rao // set subject of x509 req 245f4682712SMarri Devender Rao X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get()); 246f4682712SMarri Devender Rao 247f4682712SMarri Devender Rao if (!alternativeNames.empty()) 248f4682712SMarri Devender Rao { 249f4682712SMarri Devender Rao for (auto& name : alternativeNames) 250f4682712SMarri Devender Rao { 251f4682712SMarri Devender Rao addEntry(x509Name, "subjectAltName", name); 252f4682712SMarri Devender Rao } 253f4682712SMarri Devender Rao } 254f4682712SMarri Devender Rao addEntry(x509Name, "challengePassword", challengePassword); 255f4682712SMarri Devender Rao addEntry(x509Name, "L", city); 256f4682712SMarri Devender Rao addEntry(x509Name, "CN", commonName); 257f4682712SMarri Devender Rao addEntry(x509Name, "name", contactPerson); 258f4682712SMarri Devender Rao addEntry(x509Name, "C", country); 259f4682712SMarri Devender Rao addEntry(x509Name, "emailAddress", email); 260f4682712SMarri Devender Rao addEntry(x509Name, "GN", givenName); 261f4682712SMarri Devender Rao addEntry(x509Name, "initials", initials); 262f4682712SMarri Devender Rao addEntry(x509Name, "algorithm", keyPairAlgorithm); 263f4682712SMarri Devender Rao if (!keyUsage.empty()) 264f4682712SMarri Devender Rao { 265f4682712SMarri Devender Rao for (auto& usage : keyUsage) 266f4682712SMarri Devender Rao { 2677641105dSMarri Devender Rao if (isExtendedKeyUsage(usage)) 2687641105dSMarri Devender Rao { 2697641105dSMarri Devender Rao addEntry(x509Name, "extendedKeyUsage", usage); 2707641105dSMarri Devender Rao } 2717641105dSMarri Devender Rao else 2727641105dSMarri Devender Rao { 273f4682712SMarri Devender Rao addEntry(x509Name, "keyUsage", usage); 274f4682712SMarri Devender Rao } 275f4682712SMarri Devender Rao } 2767641105dSMarri Devender Rao } 277f4682712SMarri Devender Rao addEntry(x509Name, "O", organization); 278f4682712SMarri Devender Rao addEntry(x509Name, "ST", state); 279f4682712SMarri Devender Rao addEntry(x509Name, "SN", surname); 280f4682712SMarri Devender Rao addEntry(x509Name, "unstructuredName", unstructuredName); 281f4682712SMarri Devender Rao 2828a09b52aSRamesh Iyyar EVP_PKEY_Ptr pKey(nullptr, ::EVP_PKEY_free); 2838a09b52aSRamesh Iyyar 2848a09b52aSRamesh Iyyar log<level::INFO>("Given Key pair algorithm", 2858a09b52aSRamesh Iyyar entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str())); 2868a09b52aSRamesh Iyyar 2878a09b52aSRamesh Iyyar // Used EC algorithm as default if user did not give algorithm type. 2888a09b52aSRamesh Iyyar if (keyPairAlgorithm == "RSA") 289c6e58c7eSRamesh Iyyar pKey = getRSAKeyPair(keyBitLength); 2908a09b52aSRamesh Iyyar else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty())) 291c6e58c7eSRamesh Iyyar pKey = generateECKeyPair(keyCurveId); 2928a09b52aSRamesh Iyyar else 2938a09b52aSRamesh Iyyar { 2948a09b52aSRamesh Iyyar log<level::ERR>("Given Key pair algorithm is not supported. Supporting " 2958a09b52aSRamesh Iyyar "RSA and EC only"); 2968a09b52aSRamesh Iyyar elog<InvalidArgument>( 2978a09b52aSRamesh Iyyar Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"), 2988a09b52aSRamesh Iyyar Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str())); 2998a09b52aSRamesh Iyyar } 3008a09b52aSRamesh Iyyar 3018a09b52aSRamesh Iyyar ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get()); 3028a09b52aSRamesh Iyyar if (ret == 0) 3038a09b52aSRamesh Iyyar { 3048a09b52aSRamesh Iyyar log<level::ERR>("Error occured while setting Public key"); 3058a09b52aSRamesh Iyyar elog<InternalFailure>(); 3068a09b52aSRamesh Iyyar } 3078a09b52aSRamesh Iyyar 3088a09b52aSRamesh Iyyar // Write private key to file 309c6e58c7eSRamesh Iyyar writePrivateKey(pKey, PRIV_KEY_FILE_NAME); 310f4682712SMarri Devender Rao 311f4682712SMarri Devender Rao // set sign key of x509 req 312f4682712SMarri Devender Rao ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256()); 3138a09b52aSRamesh Iyyar if (ret == 0) 314f4682712SMarri Devender Rao { 315f4682712SMarri Devender Rao log<level::ERR>("Error occured while signing key of x509"); 316f4682712SMarri Devender Rao elog<InternalFailure>(); 317f4682712SMarri Devender Rao } 3188a09b52aSRamesh Iyyar 319f4682712SMarri Devender Rao log<level::INFO>("Writing CSR to file"); 320c6e58c7eSRamesh Iyyar fs::path csrFilePath = certParentInstallPath / CSR_FILE_NAME; 321c6e58c7eSRamesh Iyyar writeCSR(csrFilePath.string(), x509Req); 322f4682712SMarri Devender Rao } 323f4682712SMarri Devender Rao 3247641105dSMarri Devender Rao bool Manager::isExtendedKeyUsage(const std::string& usage) 3257641105dSMarri Devender Rao { 3267641105dSMarri Devender Rao const static std::array<const char*, 6> usageList = { 3277641105dSMarri Devender Rao "ServerAuthentication", "ClientAuthentication", "OCSPSigning", 3287641105dSMarri Devender Rao "Timestamping", "CodeSigning", "EmailProtection"}; 3297641105dSMarri Devender Rao auto it = std::find_if( 3307641105dSMarri Devender Rao usageList.begin(), usageList.end(), 3317641105dSMarri Devender Rao [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); }); 3327641105dSMarri Devender Rao return it != usageList.end(); 3337641105dSMarri Devender Rao } 3348a09b52aSRamesh Iyyar EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength) 335f4682712SMarri Devender Rao { 336f4682712SMarri Devender Rao int ret = 0; 337f4682712SMarri Devender Rao // generate rsa key 338f4682712SMarri Devender Rao BIGNUM_Ptr bne(BN_new(), ::BN_free); 339f4682712SMarri Devender Rao ret = BN_set_word(bne.get(), RSA_F4); 340f4682712SMarri Devender Rao if (ret == 0) 341f4682712SMarri Devender Rao { 342f4682712SMarri Devender Rao log<level::ERR>("Error occured during BN_set_word call"); 343f4682712SMarri Devender Rao elog<InternalFailure>(); 344f4682712SMarri Devender Rao } 345f4682712SMarri Devender Rao 3468a09b52aSRamesh Iyyar int64_t keyBitLen = keyBitLength; 347f4682712SMarri Devender Rao // set keybit length to default value if not set 3488a09b52aSRamesh Iyyar if (keyBitLen <= 0) 349f4682712SMarri Devender Rao { 3508a09b52aSRamesh Iyyar constexpr auto DEFAULT_KEYBITLENGTH = 2048; 3518a09b52aSRamesh Iyyar log<level::INFO>( 3528a09b52aSRamesh Iyyar "KeyBitLength is not given.Hence, using default KeyBitLength", 3538a09b52aSRamesh Iyyar entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH)); 3548a09b52aSRamesh Iyyar keyBitLen = DEFAULT_KEYBITLENGTH; 355f4682712SMarri Devender Rao } 356f4682712SMarri Devender Rao RSA* rsa = RSA_new(); 3578a09b52aSRamesh Iyyar ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), NULL); 358f4682712SMarri Devender Rao if (ret != 1) 359f4682712SMarri Devender Rao { 360f4682712SMarri Devender Rao free(rsa); 361f4682712SMarri Devender Rao log<level::ERR>("Error occured during RSA_generate_key_ex call", 3628a09b52aSRamesh Iyyar entry("KEYBITLENGTH=%PRIu64", keyBitLen)); 363f4682712SMarri Devender Rao elog<InternalFailure>(); 364f4682712SMarri Devender Rao } 365f4682712SMarri Devender Rao 366f4682712SMarri Devender Rao // set public key of x509 req 367f4682712SMarri Devender Rao EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free); 3688a09b52aSRamesh Iyyar ret = EVP_PKEY_assign_RSA(pKey.get(), rsa); 369f4682712SMarri Devender Rao if (ret == 0) 370f4682712SMarri Devender Rao { 3718a09b52aSRamesh Iyyar free(rsa); 3728a09b52aSRamesh Iyyar log<level::ERR>("Error occured during assign rsa key into EVP"); 373f4682712SMarri Devender Rao elog<InternalFailure>(); 374f4682712SMarri Devender Rao } 375f4682712SMarri Devender Rao 3768a09b52aSRamesh Iyyar return pKey; 3778a09b52aSRamesh Iyyar } 3788a09b52aSRamesh Iyyar 3798a09b52aSRamesh Iyyar EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId) 3808a09b52aSRamesh Iyyar { 3818a09b52aSRamesh Iyyar std::string curId(curveId); 3828a09b52aSRamesh Iyyar 3838a09b52aSRamesh Iyyar if (curId.empty()) 3848a09b52aSRamesh Iyyar { 3858a09b52aSRamesh Iyyar // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349 3868a09b52aSRamesh Iyyar constexpr auto DEFAULT_KEYCURVEID = "secp224r1"; 3878a09b52aSRamesh Iyyar log<level::INFO>( 3888a09b52aSRamesh Iyyar "KeyCurveId is not given. Hence using default curve id", 3898a09b52aSRamesh Iyyar entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID)); 3908a09b52aSRamesh Iyyar curId = DEFAULT_KEYCURVEID; 3918a09b52aSRamesh Iyyar } 3928a09b52aSRamesh Iyyar 3938a09b52aSRamesh Iyyar int ecGrp = OBJ_txt2nid(curId.c_str()); 3948a09b52aSRamesh Iyyar 3958a09b52aSRamesh Iyyar if (ecGrp == NID_undef) 3968a09b52aSRamesh Iyyar { 3978a09b52aSRamesh Iyyar log<level::ERR>( 3988a09b52aSRamesh Iyyar "Error occured during convert the curve id string format into NID", 3998a09b52aSRamesh Iyyar entry("KEYCURVEID=%s", curId.c_str())); 4008a09b52aSRamesh Iyyar elog<InternalFailure>(); 4018a09b52aSRamesh Iyyar } 4028a09b52aSRamesh Iyyar 4038a09b52aSRamesh Iyyar EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp); 4048a09b52aSRamesh Iyyar 4058a09b52aSRamesh Iyyar if (ecKey == NULL) 4068a09b52aSRamesh Iyyar { 4078a09b52aSRamesh Iyyar log<level::ERR>( 4088a09b52aSRamesh Iyyar "Error occured during create the EC_Key object from NID", 4098a09b52aSRamesh Iyyar entry("ECGROUP=%d", ecGrp)); 4108a09b52aSRamesh Iyyar elog<InternalFailure>(); 4118a09b52aSRamesh Iyyar } 4128a09b52aSRamesh Iyyar 4138a09b52aSRamesh Iyyar // If you want to save a key and later load it with 4148a09b52aSRamesh Iyyar // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE 4158a09b52aSRamesh Iyyar // flag on the key. 4168a09b52aSRamesh Iyyar EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE); 4178a09b52aSRamesh Iyyar 4188a09b52aSRamesh Iyyar int ret = EC_KEY_generate_key(ecKey); 4198a09b52aSRamesh Iyyar 4208a09b52aSRamesh Iyyar if (ret == 0) 4218a09b52aSRamesh Iyyar { 4228a09b52aSRamesh Iyyar EC_KEY_free(ecKey); 4238a09b52aSRamesh Iyyar log<level::ERR>("Error occured during generate EC key"); 4248a09b52aSRamesh Iyyar elog<InternalFailure>(); 4258a09b52aSRamesh Iyyar } 4268a09b52aSRamesh Iyyar 4278a09b52aSRamesh Iyyar EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free); 4288a09b52aSRamesh Iyyar ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey); 4298a09b52aSRamesh Iyyar if (ret == 0) 4308a09b52aSRamesh Iyyar { 4318a09b52aSRamesh Iyyar EC_KEY_free(ecKey); 4328a09b52aSRamesh Iyyar log<level::ERR>("Error occured during assign EC Key into EVP"); 4338a09b52aSRamesh Iyyar elog<InternalFailure>(); 4348a09b52aSRamesh Iyyar } 4358a09b52aSRamesh Iyyar 4368a09b52aSRamesh Iyyar return pKey; 4378a09b52aSRamesh Iyyar } 4388a09b52aSRamesh Iyyar 439c6e58c7eSRamesh Iyyar void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey, 440c6e58c7eSRamesh Iyyar const std::string& privKeyFileName) 4418a09b52aSRamesh Iyyar { 4428a09b52aSRamesh Iyyar log<level::INFO>("Writing private key to file"); 443f4682712SMarri Devender Rao // write private key to file 444c6e58c7eSRamesh Iyyar fs::path privKeyPath = certParentInstallPath / privKeyFileName; 445f4682712SMarri Devender Rao 446f4682712SMarri Devender Rao FILE* fp = std::fopen(privKeyPath.c_str(), "w"); 447f4682712SMarri Devender Rao if (fp == NULL) 448f4682712SMarri Devender Rao { 449f4682712SMarri Devender Rao log<level::ERR>("Error occured creating private key file"); 450f4682712SMarri Devender Rao elog<InternalFailure>(); 451f4682712SMarri Devender Rao } 4528a09b52aSRamesh Iyyar int ret = PEM_write_PrivateKey(fp, pKey.get(), NULL, NULL, 0, 0, NULL); 453f4682712SMarri Devender Rao std::fclose(fp); 454f4682712SMarri Devender Rao if (ret == 0) 455f4682712SMarri Devender Rao { 456f4682712SMarri Devender Rao log<level::ERR>("Error occured while writing private key to file"); 457f4682712SMarri Devender Rao elog<InternalFailure>(); 458f4682712SMarri Devender Rao } 459f4682712SMarri Devender Rao } 460f4682712SMarri Devender Rao 461f4682712SMarri Devender Rao void Manager::addEntry(X509_NAME* x509Name, const char* field, 462f4682712SMarri Devender Rao const std::string& bytes) 463f4682712SMarri Devender Rao { 464f4682712SMarri Devender Rao if (bytes.empty()) 465f4682712SMarri Devender Rao { 466f4682712SMarri Devender Rao return; 467f4682712SMarri Devender Rao } 468f4682712SMarri Devender Rao int ret = X509_NAME_add_entry_by_txt( 469f4682712SMarri Devender Rao x509Name, field, MBSTRING_ASC, 470f4682712SMarri Devender Rao reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0); 471f4682712SMarri Devender Rao if (ret != 1) 472f4682712SMarri Devender Rao { 473f4682712SMarri Devender Rao log<level::ERR>("Unable to set entry", entry("FIELD=%s", field), 474f4682712SMarri Devender Rao entry("VALUE=%s", bytes.c_str())); 475f4682712SMarri Devender Rao elog<InternalFailure>(); 476f4682712SMarri Devender Rao } 477f4682712SMarri Devender Rao } 478f4682712SMarri Devender Rao 479f4682712SMarri Devender Rao void Manager::createCSRObject(const Status& status) 480f4682712SMarri Devender Rao { 481f4682712SMarri Devender Rao if (csrPtr) 482f4682712SMarri Devender Rao { 483f4682712SMarri Devender Rao csrPtr.reset(nullptr); 484f4682712SMarri Devender Rao } 485f4682712SMarri Devender Rao auto csrObjectPath = objectPath + '/' + "csr"; 486f4682712SMarri Devender Rao csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(), 487f4682712SMarri Devender Rao certInstallPath.c_str(), status); 488f4682712SMarri Devender Rao } 489f4682712SMarri Devender Rao 490f4682712SMarri Devender Rao void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req) 491f4682712SMarri Devender Rao { 492f4682712SMarri Devender Rao if (fs::exists(filePath)) 493f4682712SMarri Devender Rao { 494f4682712SMarri Devender Rao log<level::INFO>("Removing the existing file", 495f4682712SMarri Devender Rao entry("FILENAME=%s", filePath.c_str())); 496f4682712SMarri Devender Rao if (!fs::remove(filePath.c_str())) 497f4682712SMarri Devender Rao { 498f4682712SMarri Devender Rao log<level::ERR>("Unable to remove the file", 499f4682712SMarri Devender Rao entry("FILENAME=%s", filePath.c_str())); 500f4682712SMarri Devender Rao elog<InternalFailure>(); 501f4682712SMarri Devender Rao } 502f4682712SMarri Devender Rao } 503f4682712SMarri Devender Rao 504f4682712SMarri Devender Rao FILE* fp = NULL; 505f4682712SMarri Devender Rao 506f4682712SMarri Devender Rao if ((fp = std::fopen(filePath.c_str(), "w")) == NULL) 507f4682712SMarri Devender Rao { 508f4682712SMarri Devender Rao log<level::ERR>("Error opening the file to write the CSR", 509f4682712SMarri Devender Rao entry("FILENAME=%s", filePath.c_str())); 510f4682712SMarri Devender Rao elog<InternalFailure>(); 511f4682712SMarri Devender Rao } 512f4682712SMarri Devender Rao 513f4682712SMarri Devender Rao int rc = PEM_write_X509_REQ(fp, x509Req.get()); 514f4682712SMarri Devender Rao if (!rc) 515f4682712SMarri Devender Rao { 516f4682712SMarri Devender Rao log<level::ERR>("PEM write routine failed", 517f4682712SMarri Devender Rao entry("FILENAME=%s", filePath.c_str())); 518f4682712SMarri Devender Rao std::fclose(fp); 519f4682712SMarri Devender Rao elog<InternalFailure>(); 520f4682712SMarri Devender Rao } 521f4682712SMarri Devender Rao std::fclose(fp); 522f4682712SMarri Devender Rao } 523f4682712SMarri Devender Rao 524ffad1ef1SMarri Devender Rao void Manager::createCertificate() 525ffad1ef1SMarri Devender Rao { 526ffad1ef1SMarri Devender Rao try 527ffad1ef1SMarri Devender Rao { 528ffad1ef1SMarri Devender Rao // TODO: Issue#3 At present supporting only one certificate to be 529ffad1ef1SMarri Devender Rao // uploaded this need to be revisited to support multiple 530ffad1ef1SMarri Devender Rao // certificates 531ffad1ef1SMarri Devender Rao auto certObjectPath = objectPath + '/' + '1'; 532ffad1ef1SMarri Devender Rao certificatePtr = std::make_unique<Certificate>( 533ffad1ef1SMarri Devender Rao bus, certObjectPath, certType, unitToRestart, certInstallPath, 534ffad1ef1SMarri Devender Rao certInstallPath, true, certWatchPtr); 535ffad1ef1SMarri Devender Rao } 536ffad1ef1SMarri Devender Rao catch (const InternalFailure& e) 537ffad1ef1SMarri Devender Rao { 538ffad1ef1SMarri Devender Rao report<InternalFailure>(); 539ffad1ef1SMarri Devender Rao } 540ffad1ef1SMarri Devender Rao catch (const InvalidCertificate& e) 541ffad1ef1SMarri Devender Rao { 542ffad1ef1SMarri Devender Rao report<InvalidCertificate>( 543ffad1ef1SMarri Devender Rao Reason("Existing certificate file is corrupted")); 544ffad1ef1SMarri Devender Rao } 545ffad1ef1SMarri Devender Rao } 546c6e58c7eSRamesh Iyyar 547c6e58c7eSRamesh Iyyar void Manager::createRSAPrivateKeyFile() 548c6e58c7eSRamesh Iyyar { 549c6e58c7eSRamesh Iyyar fs::path rsaPrivateKeyFileName = 550c6e58c7eSRamesh Iyyar certParentInstallPath / RSA_PRIV_KEY_FILE_NAME; 551c6e58c7eSRamesh Iyyar 552c6e58c7eSRamesh Iyyar try 553c6e58c7eSRamesh Iyyar { 554c6e58c7eSRamesh Iyyar if (!fs::exists(rsaPrivateKeyFileName)) 555c6e58c7eSRamesh Iyyar { 556c6e58c7eSRamesh Iyyar writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH), 557c6e58c7eSRamesh Iyyar RSA_PRIV_KEY_FILE_NAME); 558c6e58c7eSRamesh Iyyar } 559c6e58c7eSRamesh Iyyar } 560c6e58c7eSRamesh Iyyar catch (const InternalFailure& e) 561c6e58c7eSRamesh Iyyar { 562c6e58c7eSRamesh Iyyar report<InternalFailure>(); 563c6e58c7eSRamesh Iyyar } 564c6e58c7eSRamesh Iyyar } 565c6e58c7eSRamesh Iyyar 566c6e58c7eSRamesh Iyyar EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength) 567c6e58c7eSRamesh Iyyar { 568c6e58c7eSRamesh Iyyar if (keyBitLength != SUPPORTED_KEYBITLENGTH) 569c6e58c7eSRamesh Iyyar { 570c6e58c7eSRamesh Iyyar log<level::ERR>( 571c6e58c7eSRamesh Iyyar "Given Key bit length is not supported", 572c6e58c7eSRamesh Iyyar entry("GIVENKEYBITLENGTH=%d", keyBitLength), 573c6e58c7eSRamesh Iyyar entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH)); 574c6e58c7eSRamesh Iyyar elog<InvalidArgument>( 575c6e58c7eSRamesh Iyyar Argument::ARGUMENT_NAME("KEYBITLENGTH"), 576c6e58c7eSRamesh Iyyar Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str())); 577c6e58c7eSRamesh Iyyar } 578c6e58c7eSRamesh Iyyar fs::path rsaPrivateKeyFileName = 579c6e58c7eSRamesh Iyyar certParentInstallPath / RSA_PRIV_KEY_FILE_NAME; 580c6e58c7eSRamesh Iyyar 581c6e58c7eSRamesh Iyyar FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r"); 582c6e58c7eSRamesh Iyyar if (!privateKeyFile) 583c6e58c7eSRamesh Iyyar { 584c6e58c7eSRamesh Iyyar log<level::ERR>("Unable to open RSA private key file to read", 585c6e58c7eSRamesh Iyyar entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()), 586c6e58c7eSRamesh Iyyar entry("ERRORREASON=%s", strerror(errno))); 587c6e58c7eSRamesh Iyyar elog<InternalFailure>(); 588c6e58c7eSRamesh Iyyar } 589c6e58c7eSRamesh Iyyar 590c6e58c7eSRamesh Iyyar EVP_PKEY_Ptr privateKey( 591c6e58c7eSRamesh Iyyar PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr), 592c6e58c7eSRamesh Iyyar ::EVP_PKEY_free); 593c6e58c7eSRamesh Iyyar std::fclose(privateKeyFile); 594c6e58c7eSRamesh Iyyar 595c6e58c7eSRamesh Iyyar if (!privateKey) 596c6e58c7eSRamesh Iyyar { 597c6e58c7eSRamesh Iyyar log<level::ERR>("Error occured during PEM_read_PrivateKey call"); 598c6e58c7eSRamesh Iyyar elog<InternalFailure>(); 599c6e58c7eSRamesh Iyyar } 600c6e58c7eSRamesh Iyyar return privateKey; 601c6e58c7eSRamesh Iyyar } 602cfbc8dc8SJayanth Othayoth } // namespace certs 603cfbc8dc8SJayanth Othayoth } // namespace phosphor 604