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