1 #include "config.h"
2 
3 #include "certificate.hpp"
4 
5 #include "certs_manager.hpp"
6 
7 #include <openssl/bio.h>
8 #include <openssl/crypto.h>
9 #include <openssl/err.h>
10 #include <openssl/evp.h>
11 #include <openssl/pem.h>
12 #include <openssl/ssl.h>
13 #include <openssl/x509v3.h>
14 
15 #include <fstream>
16 #include <phosphor-logging/elog-errors.hpp>
17 #include <xyz/openbmc_project/Certs/error.hpp>
18 #include <xyz/openbmc_project/Common/error.hpp>
19 
20 namespace phosphor
21 {
22 namespace certs
23 {
24 // RAII support for openSSL functions.
25 using BIO_MEM_Ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
26 using X509_STORE_CTX_Ptr =
27     std::unique_ptr<X509_STORE_CTX, decltype(&::X509_STORE_CTX_free)>;
28 using X509_LOOKUP_Ptr =
29     std::unique_ptr<X509_LOOKUP, decltype(&::X509_LOOKUP_free)>;
30 using ASN1_TIME_ptr = std::unique_ptr<ASN1_TIME, decltype(&ASN1_STRING_free)>;
31 using EVP_PKEY_Ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
32 using BUF_MEM_Ptr = std::unique_ptr<BUF_MEM, decltype(&::BUF_MEM_free)>;
33 using InternalFailure =
34     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
35 using InvalidCertificate =
36     sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
37 using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON;
38 
39 // Trust chain related errors.`
40 #define TRUST_CHAIN_ERR(errnum)                                                \
41     ((errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) ||                     \
42      (errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) ||                       \
43      (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) ||               \
44      (errnum == X509_V_ERR_CERT_UNTRUSTED) ||                                  \
45      (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE))
46 
47 // Refer to schema 2018.3
48 // http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
49 // supported KeyUsage types in redfish
50 // Refer to
51 // https://github.com/openssl/openssl/blob/master/include/openssl/x509v3.h for
52 // key usage bit fields
53 std::map<uint8_t, std::string> keyUsageToRfStr = {
54     {KU_DIGITAL_SIGNATURE, "DigitalSignature"},
55     {KU_NON_REPUDIATION, "NonRepudiation"},
56     {KU_KEY_ENCIPHERMENT, "KeyEncipherment"},
57     {KU_DATA_ENCIPHERMENT, "DataEncipherment"},
58     {KU_KEY_AGREEMENT, "KeyAgreement"},
59     {KU_KEY_CERT_SIGN, "KeyCertSign"},
60     {KU_CRL_SIGN, "CRLSigning"},
61     {KU_ENCIPHER_ONLY, "EncipherOnly"},
62     {KU_DECIPHER_ONLY, "DecipherOnly"}};
63 
64 // Refer to schema 2018.3
65 // http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
66 // supported Extended KeyUsage types in redfish
67 std::map<uint8_t, std::string> extendedKeyUsageToRfStr = {
68     {NID_server_auth, "ServerAuthentication"},
69     {NID_client_auth, "ClientAuthentication"},
70     {NID_email_protect, "EmailProtection"},
71     {NID_OCSP_sign, "OCSPSigning"},
72     {NID_ad_timeStamping, "Timestamping"},
73     {NID_code_sign, "CodeSigning"}};
74 
75 std::string Certificate::generateCertId(const std::string& certPath)
76 {
77     const X509_Ptr cert = loadCert(certPath);
78     unsigned long subjectNameHash = X509_subject_name_hash(cert.get());
79     unsigned long issuerSerialHash = X509_issuer_and_serial_hash(cert.get());
80     static constexpr auto CERT_ID_LENGTH = 17;
81     char idBuff[CERT_ID_LENGTH];
82 
83     snprintf(idBuff, CERT_ID_LENGTH, "%08lx%08lx", subjectNameHash,
84              issuerSerialHash);
85 
86     return std::string(idBuff);
87 }
88 
89 std::string
90     Certificate::generateUniqueFilePath(const std::string& directoryPath)
91 {
92     char* filePath = tempnam(directoryPath.c_str(), NULL);
93     if (filePath == NULL)
94     {
95         log<level::ERR>(
96             "Error occured while creating random certificate file path",
97             entry("DIR=%s", directoryPath.c_str()));
98         elog<InternalFailure>();
99     }
100     std::string filePathStr(filePath);
101     free(filePath);
102     return filePathStr;
103 }
104 
105 std::string Certificate::generateAuthCertFileX509Path(
106     const std::string& certSrcFilePath, const std::string& certDstDirPath)
107 {
108     const X509_Ptr cert = loadCert(certSrcFilePath);
109     unsigned long hash = X509_subject_name_hash(cert.get());
110     static constexpr auto CERT_HASH_LENGTH = 9;
111     char hashBuf[CERT_HASH_LENGTH];
112 
113     snprintf(hashBuf, CERT_HASH_LENGTH, "%08lx", hash);
114 
115     const std::string certHash(hashBuf);
116     for (int i = 0; i < AUTHORITY_CERTIFICATES_LIMIT; ++i)
117     {
118         const std::string certDstFileX509Path =
119             certDstDirPath + "/" + certHash + "." + std::to_string(i);
120         if (!fs::exists(certDstFileX509Path))
121         {
122             return certDstFileX509Path;
123         }
124     }
125 
126     log<level::ERR>("Authority certificate x509 file path already used",
127                     entry("DIR=%s", certDstDirPath.c_str()));
128     elog<InternalFailure>();
129 }
130 
131 std::string
132     Certificate::generateAuthCertFilePath(const std::string& certSrcFilePath)
133 {
134     // If there is a certificate file path (which means certificate replacement
135     // is doing) use it (do not create new one)
136     if (!certFilePath.empty())
137     {
138         return certFilePath;
139     }
140     // If source certificate file is located in the certificates directory use
141     // it (do not create new one)
142     else if (fs::path(certSrcFilePath).parent_path().string() ==
143              certInstallPath)
144     {
145         return certSrcFilePath;
146     }
147     // Otherwise generate new file name/path
148     else
149     {
150         return generateUniqueFilePath(certInstallPath);
151     }
152 }
153 
154 std::string
155     Certificate::generateCertFilePath(const std::string& certSrcFilePath)
156 {
157     if (certType == phosphor::certs::AUTHORITY)
158     {
159         return generateAuthCertFilePath(certSrcFilePath);
160     }
161     else
162     {
163         return certInstallPath;
164     }
165 }
166 
167 Certificate::Certificate(sdbusplus::bus::bus& bus, const std::string& objPath,
168                          const CertificateType& type,
169                          const CertInstallPath& installPath,
170                          const CertUploadPath& uploadPath,
171                          const CertWatchPtr& certWatchPtr, Manager& parent) :
172     CertIfaces(bus, objPath.c_str(), true),
173     bus(bus), objectPath(objPath), certType(type), certInstallPath(installPath),
174     certWatchPtr(certWatchPtr), manager(parent)
175 {
176     auto installHelper = [this](const auto& filePath) {
177         if (!compareKeys(filePath))
178         {
179             elog<InvalidCertificate>(
180                 Reason("Private key does not match the Certificate"));
181         };
182     };
183     typeFuncMap[SERVER] = installHelper;
184     typeFuncMap[CLIENT] = installHelper;
185     typeFuncMap[AUTHORITY] = [](auto filePath) {};
186 
187     auto appendPrivateKey = [this](const std::string& filePath) {
188         checkAndAppendPrivateKey(filePath);
189     };
190 
191     appendKeyMap[SERVER] = appendPrivateKey;
192     appendKeyMap[CLIENT] = appendPrivateKey;
193     appendKeyMap[AUTHORITY] = [](const std::string& filePath) {};
194 
195     // Generate certificate file path
196     certFilePath = generateCertFilePath(uploadPath);
197 
198     // install the certificate
199     install(uploadPath);
200 
201     this->emit_object_added();
202 }
203 
204 Certificate::~Certificate()
205 {
206     if (!fs::remove(certFilePath))
207     {
208         log<level::INFO>("Certificate file not found!",
209                          entry("PATH=%s", certFilePath.c_str()));
210     }
211 }
212 
213 void Certificate::replace(const std::string filePath)
214 {
215     manager.replaceCertificate(this, filePath);
216 }
217 
218 void Certificate::install(const std::string& certSrcFilePath)
219 {
220     log<level::INFO>("Certificate install ",
221                      entry("FILEPATH=%s", certSrcFilePath.c_str()));
222     auto errCode = X509_V_OK;
223 
224     // stop watch for user initiated certificate install
225     if (certWatchPtr)
226     {
227         certWatchPtr->stopWatch();
228     }
229 
230     // Verify the certificate file
231     fs::path file(certSrcFilePath);
232     if (!fs::exists(file))
233     {
234         log<level::ERR>("File is Missing",
235                         entry("FILE=%s", certSrcFilePath.c_str()));
236         elog<InternalFailure>();
237     }
238 
239     try
240     {
241         if (fs::file_size(certSrcFilePath) == 0)
242         {
243             // file is empty
244             log<level::ERR>("File is empty",
245                             entry("FILE=%s", certSrcFilePath.c_str()));
246             elog<InvalidCertificate>(Reason("File is empty"));
247         }
248     }
249     catch (const fs::filesystem_error& e)
250     {
251         // Log Error message
252         log<level::ERR>(e.what(), entry("FILE=%s", certSrcFilePath.c_str()));
253         elog<InternalFailure>();
254     }
255 
256     // Defining store object as RAW to avoid double free.
257     // X509_LOOKUP_free free up store object.
258     // Create an empty X509_STORE structure for certificate validation.
259     auto x509Store = X509_STORE_new();
260     if (!x509Store)
261     {
262         log<level::ERR>("Error occured during X509_STORE_new call");
263         elog<InternalFailure>();
264     }
265 
266     OpenSSL_add_all_algorithms();
267 
268     // ADD Certificate Lookup method.
269     X509_LOOKUP_Ptr lookup(X509_STORE_add_lookup(x509Store, X509_LOOKUP_file()),
270                            ::X509_LOOKUP_free);
271     if (!lookup)
272     {
273         // Normally lookup cleanup function interanlly does X509Store cleanup
274         // Free up the X509Store.
275         X509_STORE_free(x509Store);
276         log<level::ERR>("Error occured during X509_STORE_add_lookup call");
277         elog<InternalFailure>();
278     }
279     // Load Certificate file.
280     errCode = X509_LOOKUP_load_file(lookup.get(), certSrcFilePath.c_str(),
281                                     X509_FILETYPE_PEM);
282     if (errCode != 1)
283     {
284         log<level::ERR>("Error occured during X509_LOOKUP_load_file call",
285                         entry("FILE=%s", certSrcFilePath.c_str()));
286         elog<InvalidCertificate>(Reason("Invalid certificate file format"));
287     }
288 
289     // Load Certificate file into the X509 structre.
290     X509_Ptr cert = loadCert(certSrcFilePath);
291     X509_STORE_CTX_Ptr storeCtx(X509_STORE_CTX_new(), ::X509_STORE_CTX_free);
292     if (!storeCtx)
293     {
294         log<level::ERR>("Error occured during X509_STORE_CTX_new call",
295                         entry("FILE=%s", certSrcFilePath.c_str()));
296         elog<InternalFailure>();
297     }
298 
299     errCode = X509_STORE_CTX_init(storeCtx.get(), x509Store, cert.get(), NULL);
300     if (errCode != 1)
301     {
302         log<level::ERR>("Error occured during X509_STORE_CTX_init call",
303                         entry("FILE=%s", certSrcFilePath.c_str()));
304         elog<InternalFailure>();
305     }
306 
307     // Set time to current time.
308     auto locTime = time(nullptr);
309 
310     X509_STORE_CTX_set_time(storeCtx.get(), X509_V_FLAG_USE_CHECK_TIME,
311                             locTime);
312 
313     errCode = X509_verify_cert(storeCtx.get());
314     if (errCode == 1)
315     {
316         errCode = X509_V_OK;
317     }
318     else if (errCode == 0)
319     {
320         errCode = X509_STORE_CTX_get_error(storeCtx.get());
321         log<level::INFO>(
322             "Error occured during X509_verify_cert call, checking for known "
323             "error",
324             entry("FILE=%s", certSrcFilePath.c_str()),
325             entry("ERRCODE=%d", errCode),
326             entry("ERROR_STR=%s", X509_verify_cert_error_string(errCode)));
327     }
328     else
329     {
330         log<level::ERR>("Error occured during X509_verify_cert call",
331                         entry("FILE=%s", certSrcFilePath.c_str()));
332         elog<InternalFailure>();
333     }
334 
335     // Allow certificate upload, for "certificate is not yet valid" and
336     // trust chain related errors.
337     if (!((errCode == X509_V_OK) ||
338           (errCode == X509_V_ERR_CERT_NOT_YET_VALID) ||
339           TRUST_CHAIN_ERR(errCode)))
340     {
341         if (errCode == X509_V_ERR_CERT_HAS_EXPIRED)
342         {
343             log<level::ERR>("Expired certificate ");
344             elog<InvalidCertificate>(Reason("Expired Certificate"));
345         }
346         // Loging general error here.
347         log<level::ERR>(
348             "Certificate validation failed", entry("ERRCODE=%d", errCode),
349             entry("ERROR_STR=%s", X509_verify_cert_error_string(errCode)));
350         elog<InvalidCertificate>(Reason("Certificate validation failed"));
351     }
352 
353     validateCertificateExpiryDate(cert);
354 
355     // Verify that the certificate can be used in a TLS context
356     const SSL_METHOD* method = TLS_method();
357     std::unique_ptr<SSL_CTX, decltype(&::SSL_CTX_free)> ctx(SSL_CTX_new(method),
358                                                             SSL_CTX_free);
359     if (SSL_CTX_use_certificate(ctx.get(), cert.get()) != 1)
360     {
361         log<level::ERR>("Certificate is not usable",
362                         entry("ERRCODE=%x", ERR_get_error()));
363         elog<InvalidCertificate>(Reason("Certificate is not usable"));
364     }
365 
366     // Invoke type specific append private key function.
367     auto appendIter = appendKeyMap.find(certType);
368     if (appendIter == appendKeyMap.end())
369     {
370         log<level::ERR>("Unsupported Type", entry("TYPE=%s", certType.c_str()));
371         elog<InternalFailure>();
372     }
373     appendIter->second(certSrcFilePath);
374 
375     // Invoke type specific compare keys function.
376     auto compIter = typeFuncMap.find(certType);
377     if (compIter == typeFuncMap.end())
378     {
379         log<level::ERR>("Unsupported Type", entry("TYPE=%s", certType.c_str()));
380         elog<InternalFailure>();
381     }
382     compIter->second(certSrcFilePath);
383 
384     // Copy the certificate to the installation path
385     // During bootup will be parsing existing file so no need to
386     // copy it.
387     if (certSrcFilePath != certFilePath)
388     {
389         std::ifstream inputCertFileStream;
390         std::ofstream outputCertFileStream;
391         inputCertFileStream.exceptions(std::ifstream::failbit |
392                                        std::ifstream::badbit |
393                                        std::ifstream::eofbit);
394         outputCertFileStream.exceptions(std::ofstream::failbit |
395                                         std::ofstream::badbit |
396                                         std::ofstream::eofbit);
397 
398         try
399         {
400             inputCertFileStream.open(certSrcFilePath);
401             outputCertFileStream.open(certFilePath, std::ios::out);
402             outputCertFileStream << inputCertFileStream.rdbuf() << std::flush;
403             inputCertFileStream.close();
404             outputCertFileStream.close();
405         }
406         catch (const std::exception& e)
407         {
408             log<level::ERR>("Failed to copy certificate",
409                             entry("ERR=%s", e.what()),
410                             entry("SRC=%s", certSrcFilePath.c_str()),
411                             entry("DST=%s", certFilePath.c_str()));
412             elog<InternalFailure>();
413         }
414     }
415 
416     storageUpdate();
417 
418     // Keep certificate ID
419     certId = generateCertId(certFilePath);
420 
421     // Parse the certificate file and populate properties
422     populateProperties(certFilePath);
423 
424     // restart watch
425     if (certWatchPtr)
426     {
427         certWatchPtr->startWatch();
428     }
429 }
430 
431 void Certificate::validateCertificateExpiryDate(const X509_Ptr& cert)
432 {
433     int days = 0;
434     int secs = 0;
435 
436     ASN1_TIME_ptr epoch(ASN1_TIME_new(), ASN1_STRING_free);
437     // Set time to 12:00am GMT, Jan 1 1970
438     ASN1_TIME_set_string(epoch.get(), "700101120000Z");
439 
440     ASN1_TIME* notAfter = X509_get_notAfter(cert.get());
441     ASN1_TIME_diff(&days, &secs, epoch.get(), notAfter);
442 
443     static const int dayToSeconds = 24 * 60 * 60;
444 
445     // TODO #issue15 - allow only upto year 2038 which time_t supports for now
446     // time_t is defined as int32 so any expiry date greater than 2038 will
447     // cause the time_t variable overflow resulting in -ve number.
448     if (days > (INT_MAX - secs) / dayToSeconds)
449     {
450         log<level::ERR>("Certificate expiry date is beyond year 2038",
451                         entry("DAYS=%d", days));
452         elog<InvalidCertificate>(Reason("Expiry date should be below 2038"));
453     }
454 }
455 
456 void Certificate::populateProperties()
457 {
458     populateProperties(certInstallPath);
459 }
460 
461 std::string Certificate::getCertId() const
462 {
463     return certId;
464 }
465 
466 bool Certificate::isSame(const std::string& certPath)
467 {
468     return getCertId() == generateCertId(certPath);
469 }
470 
471 void Certificate::storageUpdate()
472 {
473     if (certType == phosphor::certs::AUTHORITY)
474     {
475         // Create symbolic link in the certificate directory
476         std::string certFileX509Path;
477         try
478         {
479             if (!certFilePath.empty() &&
480                 fs::is_regular_file(fs::path(certFilePath)))
481             {
482                 certFileX509Path =
483                     generateAuthCertFileX509Path(certFilePath, certInstallPath);
484                 fs::create_symlink(fs::path(certFilePath),
485                                    fs::path(certFileX509Path));
486             }
487         }
488         catch (const std::exception& e)
489         {
490             log<level::ERR>("Failed to create symlink for certificate",
491                             entry("ERR=%s", e.what()),
492                             entry("FILE=%s", certFilePath.c_str()),
493                             entry("SYMLINK=%s", certFileX509Path.c_str()));
494             elog<InternalFailure>();
495         }
496     }
497 }
498 
499 void Certificate::populateProperties(const std::string& certPath)
500 {
501     X509_Ptr cert = loadCert(certPath);
502     // Update properties if no error thrown
503     BIO_MEM_Ptr certBio(BIO_new(BIO_s_mem()), BIO_free);
504     PEM_write_bio_X509(certBio.get(), cert.get());
505     BUF_MEM_Ptr certBuf(BUF_MEM_new(), BUF_MEM_free);
506     BUF_MEM* buf = certBuf.get();
507     BIO_get_mem_ptr(certBio.get(), &buf);
508     std::string certStr(buf->data, buf->length);
509     CertificateIface::certificateString(certStr);
510 
511     static const int maxKeySize = 4096;
512     char subBuffer[maxKeySize] = {0};
513     BIO_MEM_Ptr subBio(BIO_new(BIO_s_mem()), BIO_free);
514     // This pointer cannot be freed independantly.
515     X509_NAME* sub = X509_get_subject_name(cert.get());
516     X509_NAME_print_ex(subBio.get(), sub, 0, XN_FLAG_SEP_COMMA_PLUS);
517     BIO_read(subBio.get(), subBuffer, maxKeySize);
518     CertificateIface::subject(subBuffer);
519 
520     char issuerBuffer[maxKeySize] = {0};
521     BIO_MEM_Ptr issuerBio(BIO_new(BIO_s_mem()), BIO_free);
522     // This pointer cannot be freed independantly.
523     X509_NAME* issuer_name = X509_get_issuer_name(cert.get());
524     X509_NAME_print_ex(issuerBio.get(), issuer_name, 0, XN_FLAG_SEP_COMMA_PLUS);
525     BIO_read(issuerBio.get(), issuerBuffer, maxKeySize);
526     CertificateIface::issuer(issuerBuffer);
527 
528     std::vector<std::string> keyUsageList;
529     ASN1_BIT_STRING* usage;
530 
531     // Go through each usage in the bit string and convert to
532     // corresponding string value
533     if ((usage = static_cast<ASN1_BIT_STRING*>(
534              X509_get_ext_d2i(cert.get(), NID_key_usage, NULL, NULL))))
535     {
536         for (auto i = 0; i < usage->length; ++i)
537         {
538             for (auto& x : keyUsageToRfStr)
539             {
540                 if (x.first & usage->data[i])
541                 {
542                     keyUsageList.push_back(x.second);
543                     break;
544                 }
545             }
546         }
547     }
548 
549     EXTENDED_KEY_USAGE* extUsage;
550     if ((extUsage = static_cast<EXTENDED_KEY_USAGE*>(
551              X509_get_ext_d2i(cert.get(), NID_ext_key_usage, NULL, NULL))))
552     {
553         for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
554         {
555             keyUsageList.push_back(extendedKeyUsageToRfStr[OBJ_obj2nid(
556                 sk_ASN1_OBJECT_value(extUsage, i))]);
557         }
558     }
559     CertificateIface::keyUsage(keyUsageList);
560 
561     int days = 0;
562     int secs = 0;
563 
564     ASN1_TIME_ptr epoch(ASN1_TIME_new(), ASN1_STRING_free);
565     // Set time to 12:00am GMT, Jan 1 1970
566     ASN1_TIME_set_string(epoch.get(), "700101120000Z");
567 
568     static const uint32_t dayToSeconds = 24 * 60 * 60;
569     ASN1_TIME* notAfter = X509_get_notAfter(cert.get());
570     ASN1_TIME_diff(&days, &secs, epoch.get(), notAfter);
571     CertificateIface::validNotAfter((days * dayToSeconds) + secs);
572 
573     ASN1_TIME* notBefore = X509_get_notBefore(cert.get());
574     ASN1_TIME_diff(&days, &secs, epoch.get(), notBefore);
575     CertificateIface::validNotBefore((days * dayToSeconds) + secs);
576 }
577 
578 X509_Ptr Certificate::loadCert(const std::string& filePath)
579 {
580     // Read Certificate file
581     X509_Ptr cert(X509_new(), ::X509_free);
582     if (!cert)
583     {
584         log<level::ERR>("Error occured during X509_new call",
585                         entry("FILE=%s", filePath.c_str()),
586                         entry("ERRCODE=%lu", ERR_get_error()));
587         elog<InternalFailure>();
588     }
589 
590     BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
591     if (!bioCert)
592     {
593         log<level::ERR>("Error occured during BIO_new_file call",
594                         entry("FILE=%s", filePath.c_str()));
595         elog<InternalFailure>();
596     }
597 
598     X509* x509 = cert.get();
599     if (!PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr))
600     {
601         log<level::ERR>("Error occured during PEM_read_bio_X509 call",
602                         entry("FILE=%s", filePath.c_str()));
603         elog<InternalFailure>();
604     }
605     return cert;
606 }
607 
608 void Certificate::checkAndAppendPrivateKey(const std::string& filePath)
609 {
610     BIO_MEM_Ptr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
611     if (!keyBio)
612     {
613         log<level::ERR>("Error occured during BIO_s_file call",
614                         entry("FILE=%s", filePath.c_str()));
615         elog<InternalFailure>();
616     }
617     BIO_read_filename(keyBio.get(), filePath.c_str());
618 
619     EVP_PKEY_Ptr priKey(
620         PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
621         ::EVP_PKEY_free);
622     if (!priKey)
623     {
624         log<level::INFO>("Private key not present in file",
625                          entry("FILE=%s", filePath.c_str()));
626         fs::path privateKeyFile = fs::path(certInstallPath).parent_path();
627         privateKeyFile = privateKeyFile / PRIV_KEY_FILE_NAME;
628         if (!fs::exists(privateKeyFile))
629         {
630             log<level::ERR>("Private key file is not found",
631                             entry("FILE=%s", privateKeyFile.c_str()));
632             elog<InternalFailure>();
633         }
634 
635         std::ifstream privKeyFileStream;
636         std::ofstream certFileStream;
637         privKeyFileStream.exceptions(std::ifstream::failbit |
638                                      std::ifstream::badbit |
639                                      std::ifstream::eofbit);
640         certFileStream.exceptions(std::ofstream::failbit |
641                                   std::ofstream::badbit |
642                                   std::ofstream::eofbit);
643         try
644         {
645             privKeyFileStream.open(privateKeyFile);
646             certFileStream.open(filePath, std::ios::app);
647             certFileStream << std::endl; // insert line break
648             certFileStream << privKeyFileStream.rdbuf() << std::flush;
649             privKeyFileStream.close();
650             certFileStream.close();
651         }
652         catch (const std::exception& e)
653         {
654             log<level::ERR>("Failed to append private key",
655                             entry("ERR=%s", e.what()),
656                             entry("SRC=%s", privateKeyFile.c_str()),
657                             entry("DST=%s", filePath.c_str()));
658             elog<InternalFailure>();
659         }
660     }
661 }
662 
663 bool Certificate::compareKeys(const std::string& filePath)
664 {
665     log<level::INFO>("Certificate compareKeys",
666                      entry("FILEPATH=%s", filePath.c_str()));
667     X509_Ptr cert(X509_new(), ::X509_free);
668     if (!cert)
669     {
670         log<level::ERR>("Error occured during X509_new call",
671                         entry("FILE=%s", filePath.c_str()),
672                         entry("ERRCODE=%lu", ERR_get_error()));
673         elog<InternalFailure>();
674     }
675 
676     BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
677     if (!bioCert)
678     {
679         log<level::ERR>("Error occured during BIO_new_file call",
680                         entry("FILE=%s", filePath.c_str()));
681         elog<InternalFailure>();
682     }
683 
684     X509* x509 = cert.get();
685     PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr);
686 
687     EVP_PKEY_Ptr pubKey(X509_get_pubkey(cert.get()), ::EVP_PKEY_free);
688     if (!pubKey)
689     {
690         log<level::ERR>("Error occurred during X509_get_pubkey",
691                         entry("FILE=%s", filePath.c_str()),
692                         entry("ERRCODE=%lu", ERR_get_error()));
693         elog<InvalidCertificate>(Reason("Failed to get public key info"));
694     }
695 
696     BIO_MEM_Ptr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
697     if (!keyBio)
698     {
699         log<level::ERR>("Error occured during BIO_s_file call",
700                         entry("FILE=%s", filePath.c_str()));
701         elog<InternalFailure>();
702     }
703     BIO_read_filename(keyBio.get(), filePath.c_str());
704 
705     EVP_PKEY_Ptr priKey(
706         PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
707         ::EVP_PKEY_free);
708     if (!priKey)
709     {
710         log<level::ERR>("Error occurred during PEM_read_bio_PrivateKey",
711                         entry("FILE=%s", filePath.c_str()),
712                         entry("ERRCODE=%lu", ERR_get_error()));
713         elog<InvalidCertificate>(Reason("Failed to get private key info"));
714     }
715 
716     int32_t rc = EVP_PKEY_cmp(priKey.get(), pubKey.get());
717     if (rc != 1)
718     {
719         log<level::ERR>("Private key is not matching with Certificate",
720                         entry("FILE=%s", filePath.c_str()),
721                         entry("ERRCODE=%d", rc));
722         return false;
723     }
724     return true;
725 }
726 
727 void Certificate::delete_()
728 {
729     manager.deleteCertificate(this);
730 }
731 } // namespace certs
732 } // namespace phosphor
733