2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * @author Jacek Migacz (j.migacz@samsung.com)
19 * @author Kyungwook Tak (k.tak@samsung.com)
21 * @brief PKCS#12 container manipulation routines.
28 #include <sys/types.h>
35 #include <openssl/err.h>
36 #include <openssl/pkcs12.h>
37 #include <openssl/sha.h>
38 #include <openssl/x509.h>
39 #include <openssl/pem.h>
41 #include "dpl/log/log.h"
42 #include "cert-svc/cerror.h"
44 #include "vcore/Certificate.h"
45 #include "vcore/Client.h"
46 #include "vcore/pkcs12.h"
48 #define SYSCALL(call) while (((call) == -1) && (errno == EINTR))
52 static const std::string START_CERT = "-----BEGIN CERTIFICATE-----";
53 static const std::string END_CERT = "-----END CERTIFICATE-----";
54 static const std::string START_TRUSTED = "-----BEGIN TRUSTED CERTIFICATE-----";
55 static const std::string END_TRUSTED = "-----END TRUSTED CERTIFICATE-----";
56 static const std::string START_KEY = "-----BEGIN PRIVATE KEY-----";
57 static const std::string END_KEY = "-----END PRIVATE KEY-----";
59 using ValidationCore::CertificatePtr;
60 using ValidationCore::Certificate;
62 using FileUniquePtr = std::unique_ptr<FILE, std::function<int(FILE *)>>;
63 using BioUniquePtr = std::unique_ptr<BIO, std::function<void(BIO *)>>;
64 using PKEYUniquePtr = std::unique_ptr<EVP_PKEY, std::function<void(EVP_PKEY *)>>;
65 using X509UniquePtr = std::unique_ptr<X509, std::function<void(X509 *)>>;
66 using X509StackUniquePtr = std::unique_ptr<STACK_OF(X509), std::function<void(STACK_OF(X509) *)>>;
68 void X509_stack_free(STACK_OF(X509) *stack)
73 inline bool hasStore(CertStoreType types, CertStoreType type)
75 return (types & type) != 0;
78 inline CertStoreType nextStore(CertStoreType type)
101 std::string generateGname(void)
106 unsigned char d[SHA_DIGEST_LENGTH];
109 SYSCALL(generator = open("/dev/urandom", O_RDONLY));
112 return std::string();
114 SYSCALL(result = read(generator, &random, sizeof(random)));
117 SYSCALL(close(generator));
118 return std::string();
121 SYSCALL(result = close(generator));
124 return std::string();
127 SHA1_Update(&ctx, &random, sizeof(random));
129 result = asprintf(&gname,
130 "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
131 "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
132 d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9],
133 d[10], d[11], d[12], d[13], d[14], d[15], d[16], d[17], d[18], d[19]);
136 return std::string();
138 std::string ret(gname);
143 std::string getCommonName(CertType type, const std::string &cert)
145 BioUniquePtr bio(BIO_new(BIO_s_mem()), BIO_free_all);
147 if (bio.get() == NULL) {
148 LogError("Failed to allocate memory.");
149 return std::string();
152 auto readCount = BIO_write(bio.get(), (const void *)cert.data(), (int)cert.length());
155 LogError("Failed to load cert into bio.");
156 return std::string();
163 case P12_INTERMEDIATE:
164 x509 = PEM_read_bio_X509_AUX(bio.get(), NULL, 0, NULL);
168 x509 = PEM_read_bio_X509(bio.get(), NULL, 0, NULL);
173 LogError("Failed to create x509 structure.");
174 return std::string();
177 X509UniquePtr x509Ptr(x509, X509_free);
178 const char *subject_c = X509_NAME_oneline(x509->cert_info->subject, NULL, 0);
180 if (subject_c == NULL) {
181 LogError("Failed to parse x509 structure");
182 return std::string();
185 return std::string(subject_c);
189 * column / common name / associated gname / prikey gname /
190 * PEM_CRT : common name / gname / none /
191 * P12_END_USER : alias / gname / prikey gname /
192 * P12_TRUSTED : common name / end cert gname / none /
193 * P12_INTERMEDIATE : common name / end cert gname / none /
196 int installPKEY(CertStoreType storeType,
197 const std::string &key,
198 const std::string &gname)
200 return vcore_client_install_certificate_to_store(
211 int installEndCert(CertStoreType storeType,
212 const std::string &cert,
213 const std::string &alias,
214 const std::string &gname,
215 const std::string &prikeyGname)
217 return vcore_client_install_certificate_to_store(
228 int installChainCert(CertStoreType storeType,
229 const std::string &cert,
230 const std::string &gname,
231 const std::string &endCertGname,
234 std::string commonName = getCommonName(type, cert);
235 return vcore_client_install_certificate_to_store(
240 endCertGname.c_str(),
245 int installCert(CertStoreType storeType,
246 const std::string &cert,
247 const std::string &gname)
249 std::string commonName = getCommonName(PEM_CRT, cert);
250 return vcore_client_install_certificate_to_store(
261 std::string readFromFile(const std::string &path)
265 if ((fp = fopen(path.c_str(), "rb")) == NULL) {
266 LogError("Fail to open file for reading : " << path);
267 return std::string();
270 FileUniquePtr filePtr(fp, fclose);
271 fseek(fp, 0L, SEEK_END);
274 if (len <= 0 || len == INT_MAX) {
275 LogError("Fail to get proper certificate.");
276 return std::string();
280 char *content = (char *)malloc(sizeof(char) * (len + 1));
282 if (content == NULL) {
283 LogError("Fail to allocate memory");
284 return std::string();
287 memset(content, 0x00, len + 1);
288 size_t readLen = fread(content, sizeof(char), (size_t)len, fp);
290 if (readLen != (size_t)len) {
291 LogError("Fail to read file : " << path);
293 return std::string();
297 std::string ret(content);
302 std::string parseCRT(const std::string &cert)
307 from = cert.find(START_CERT);
308 to = cert.find(END_CERT);
309 tailLen = END_CERT.length();
311 if (from == std::string::npos || to == std::string::npos || from > to) {
312 from = cert.find(START_TRUSTED);
313 to = cert.find(END_TRUSTED);
314 tailLen = END_TRUSTED.length();
317 if (from == std::string::npos || to == std::string::npos || from > to)
318 return std::string();
320 return std::string(cert, from, to - from + tailLen);
323 #define _CERT_SVC_VERIFY_PKCS12
324 int verify_cert_details(X509 *cert, STACK_OF(X509) *certv)
326 int result = CERTSVC_SUCCESS;
327 char *pSubject = NULL;
328 char *pIssuerName = NULL;
329 X509_STORE_CTX *cert_ctx = NULL;
330 X509_STORE *cert_store = NULL;
332 #ifdef _CERT_SVC_VERIFY_PKCS12
335 pSubject = X509_NAME_oneline(cert->cert_info->subject, NULL, 0);
338 LogError("Failed to get subject name");
339 result = CERTSVC_FAIL;
343 pIssuerName = X509_NAME_oneline(cert->cert_info->issuer, NULL, 0);
346 LogError("Failed to get issuer name");
347 result = CERTSVC_FAIL;
351 if (strcmp((const char *)pSubject, (const char *)pIssuerName) == 0) {
353 EVP_PKEY *pKey = NULL;
354 pKey = X509_get_pubkey(cert);
357 LogError("Failed to get public key");
358 result = CERTSVC_FAIL;
362 if (X509_verify(cert, pKey) <= 0) {
363 LogError("P12 verification failed");
365 result = CERTSVC_FAIL;
369 LogDebug("P12 verification Success");
372 cert_store = X509_STORE_new();
375 LogError("Memory allocation failed");
376 result = CERTSVC_FAIL;
380 res = X509_STORE_load_locations(cert_store, NULL, TZ_SYS_CA_CERTS);
383 LogError("P12 load certificate store failed");
384 X509_STORE_free(cert_store);
385 result = CERTSVC_FAIL;
389 res = X509_STORE_set_default_paths(cert_store);
392 LogError("P12 load certificate store path failed");
393 X509_STORE_free(cert_store);
394 result = CERTSVC_FAIL;
398 /* initialise store and store context */
399 cert_ctx = X509_STORE_CTX_new();
401 if (cert_ctx == NULL) {
402 LogError("Memory allocation failed");
403 result = CERTSVC_FAIL;
407 /* construct store context */
408 if (!X509_STORE_CTX_init(cert_ctx, cert_store, cert, NULL)) {
409 LogError("Memory allocation failed");
410 result = CERTSVC_FAIL;
414 #ifdef P12_VERIFICATION_NEEDED
415 res = X509_verify_cert(cert_ctx);
418 LogError("P12 verification failed");
419 result = CERTSVC_FAIL;
423 LogDebug("P12 verification Success");
426 } else if (certv != NULL) {
428 cert_store = X509_STORE_new();
431 LogError("Memory allocation failed");
432 result = CERTSVC_FAIL;
436 res = X509_STORE_load_locations(cert_store, NULL, TZ_SYS_CA_CERTS);
439 LogError("P12 load certificate store failed");
440 result = CERTSVC_FAIL;
444 res = X509_STORE_set_default_paths(cert_store);
447 LogError("P12 load certificate path failed");
448 result = CERTSVC_FAIL;
452 /* initialise store and store context */
453 cert_ctx = X509_STORE_CTX_new();
455 if (cert_ctx == NULL) {
456 LogError("Memory allocation failed");
457 result = CERTSVC_FAIL;
461 /* construct store context */
462 if (!X509_STORE_CTX_init(cert_ctx, cert_store, cert, NULL)) {
463 LogError("Memory allocation failed");
464 result = CERTSVC_FAIL;
468 X509_STORE_CTX_trusted_stack(cert_ctx, certv);
469 #ifdef P12_VERIFICATION_NEEDED
470 res = X509_verify_cert(cert_ctx);
473 LogError("P12 verification failed");
474 result = CERTSVC_FAIL;
478 LogDebug("P12 verification Success");
482 #endif //_CERT_SVC_VERIFY_PKCS12
485 if (cert_store != NULL)
486 X509_STORE_free(cert_store);
489 X509_STORE_CTX_free(cert_ctx);
496 enum class OsslType : int {
502 std::string osslToPEM(OsslType type, void *data)
504 std::vector<char> buf(4096);
505 BioUniquePtr bio(BIO_new(BIO_s_mem()), BIO_free_all);
507 if (bio.get() == NULL)
508 return std::string();
512 PEM_write_bio_PrivateKey(bio.get(), static_cast<EVP_PKEY *>(data), NULL, NULL, 0, NULL, NULL);
516 PEM_write_bio_X509(bio.get(), static_cast<X509 *>(data));
519 case OsslType::X509AUX:
520 PEM_write_bio_X509_AUX(bio.get(), static_cast<X509 *>(data));
527 int size = BIO_read(bio.get(), buf.data(), 4096);
530 return std::string();
533 return std::string(buf.data());
536 int extractPkcs12(const std::string &path,
537 const std::string &password,
538 PKEYUniquePtr &keyPtr,
539 X509UniquePtr &certPtr,
540 X509StackUniquePtr &certvPtr)
544 if ((stream = fopen(path.c_str(), "rb")) == NULL) {
545 LogError("Unable to open the file for reading : " << path);
546 return CERTSVC_IO_ERROR;
549 PKCS12 *container = d2i_PKCS12_fp(stream, NULL);
552 if (container == NULL) {
553 LogError("Failed to parse the input file passed.");
557 EVP_PKEY *key = NULL;
559 STACK_OF(X509) *certv = NULL;
560 int result = PKCS12_parse(container, password.c_str(), &key, &cert, &certv);
561 PKCS12_free(container);
564 unsigned long e = ERR_get_error();
566 ERR_error_string_n(e, buf, 1023);
567 LogError("Failed to parse the file passed. openssl err: " << buf);
573 certvPtr.reset(certv);
574 return CERTSVC_SUCCESS;
577 void rollbackStore(CertStoreType storeTypes, const std::string &endCertName)
579 for (CertStoreType storeType = VPN_STORE; storeType < SYSTEM_STORE;
580 storeType = nextStore(storeType)) {
581 if (!hasStore(storeTypes, storeType))
584 char **certChainName = NULL;
586 int result = vcore_client_load_certificates_from_store(storeType, endCertName.c_str(),
587 &certChainName, &ncerts);
589 if (result != CERTSVC_SUCCESS) {
590 LogError("Unable to load certificates from store. result : " << result);
594 for (size_t i = 0; i < ncerts; i++) {
595 if (certChainName[i] == NULL)
598 vcore_client_delete_certificate_from_store(storeType, certChainName[i]);
599 free(certChainName[i]);
602 vcore_client_delete_certificate_from_store(storeType, endCertName.c_str());
606 int insertToStore(CertStoreType storeTypes,
607 const std::string &alias,
608 const std::string &prikeyName,
609 const std::string &prikeyBuffer,
610 const std::string &endCertName,
611 const std::string &endCertBuffer,
612 const std::vector<std::string> &certChainName,
613 const std::vector<std::string> &certChainBuffer)
615 size_t ncerts = certChainName.size();
617 for (CertStoreType storeType = VPN_STORE; storeType < SYSTEM_STORE;
618 storeType = nextStore(storeType)) {
619 if (!hasStore(storeTypes, storeType))
622 LogDebug("Processing store type : " << storeType);
623 int result = installPKEY(storeType, prikeyBuffer, prikeyName);
625 if (result != CERTSVC_SUCCESS) {
626 LogError("Failed to store the private key contents. result : " << result);
630 result = installEndCert(storeType, endCertBuffer, alias, endCertName, prikeyName);
632 if (result != CERTSVC_SUCCESS) {
633 LogError("Failed to install the end user certificate. result : " << result);
637 for (size_t i = 0; i < ncerts; i++) {
639 result = installChainCert(storeType, certChainBuffer[i], certChainName[i], endCertName,
642 result = installChainCert(storeType, certChainBuffer[i], certChainName[i], endCertName,
645 if (result != CERTSVC_SUCCESS) {
646 LogError("Failed to install the ca certificates. result : " << result);
652 LogDebug("Success to insert extracted pkcs12 data to db");
653 return CERTSVC_SUCCESS;
656 int insertToStorePEM(CertStoreType storeTypes, const std::string &path, const std::string &gname)
658 std::string content = readFromFile(path);
660 if (content.empty()) {
661 LogError("Failed to read the file : " << path);
662 return CERTSVC_IO_ERROR;
665 std::string parsed = parseCRT(content);
667 if (parsed.empty()) {
668 LogError("Failed to parse CRT : " << path);
672 for (CertStoreType storeType = VPN_STORE; storeType < SYSTEM_STORE;
673 storeType = nextStore(storeType)) {
674 if (!hasStore(storeTypes, storeType))
677 int result = installCert(storeType, parsed, gname);
679 if (result != CERTSVC_SUCCESS) {
680 LogError("Failed to install PEM/CRT to db store : " << storeType << " result : " << result);
681 rollbackStore(storeTypes, gname);
685 LogDebug("Success to install PEM/CRT to db store : " << storeType);
688 LogDebug("Success to install PEM/CRT to db stores : " << storeTypes);
689 return CERTSVC_SUCCESS;
692 } // namespace anonymous
695 int pkcs12_import_from_file_to_store(CertStoreType storeTypes,
697 const char *_password,
702 if (_alias == NULL || _path == NULL || strlen(_path) < 4) {
703 LogError("Invalid input parameter.");
704 return CERTSVC_WRONG_ARGUMENT;
707 std::string path(_path);
708 std::string alias(_alias);
709 std::string password;
711 if (_password != NULL)
712 password = std::string(_password);
714 LogDebug("pkcs12_import_from_file_to_store start. path[" << path << "] password[" << password <<
715 "] alias[" << alias << "]");
717 if (storeTypes & SYSTEM_STORE) {
718 LogError("User should not install any form of certificates in SYSTEM_STORE.");
719 return CERTSVC_INVALID_STORE_TYPE;
723 * Installs CRT and PEM files.
724 * We will passing NULL for private_key_gname and associated_gname parameter
725 * in installFilePEM(). Which means that there is no private key involved
726 * in the certificate which we are installing and there are no other
727 * certificates related with the current certificate which is installed
729 std::string suffix = path.substr(path.length() - 4, 4);
731 if (strcasecmp(suffix.c_str(), ".pem") == 0 || strcasecmp(suffix.c_str(), ".crt") == 0) {
732 std::string gnamePEM = generateGname();
733 result = insertToStorePEM(storeTypes, path, gnamePEM);
735 if (result != CERTSVC_SUCCESS)
736 LogError("Failed to install PEM/CRT file to store. gname : " << gnamePEM << " result : " << result);
741 LogDebug("Convert ossl type to string start");
742 /* 0. extract pkcs12 data from file */
743 PKEYUniquePtr key(nullptr, EVP_PKEY_free);
744 X509UniquePtr cert(nullptr, X509_free);
745 X509StackUniquePtr certv(nullptr, X509_stack_free);
746 result = extractPkcs12(path, password, key, cert, certv);
748 if (result != CERTSVC_SUCCESS) {
749 LogError("Failed to extract pkcs12 file. result : " << result);
753 LogDebug("extract pkcs12 to unique ptr success");
754 result = verify_cert_details(cert.get(), certv.get());
756 if (result != CERTSVC_SUCCESS) {
757 LogError("Failed to verify p12 certificate. result : " << result);
761 /* 1. handling private key */
762 std::string prikeyName = generateGname();
763 std::string prikeyBuffer = osslToPEM(OsslType::PKEY, key.get());
765 if (prikeyName.empty() || prikeyBuffer.empty()) {
766 LogError("Failed to transform pkey to PEM. result : " << result);
770 LogDebug("Convert pkey to string success");
771 /* 2. handling end user certificate */
772 std::string endCertName = generateGname();
773 std::string endCertBuffer = osslToPEM(OsslType::X509, cert.get());
775 if (endCertName.empty() || endCertBuffer.empty()) {
776 LogError("Failed to transform x509 to PEM. result : " << result);
780 LogDebug("Convert end cert to string success");
781 /* 3. handling certificate chain */
782 std::vector<std::string> certChainName;
783 std::vector<std::string> certChainBuffer;
784 int ncerts = certv ? sk_X509_num(certv.get()) : 0;
786 for (int i = 0; i < ncerts; i++) {
787 std::string tempName = generateGname();
788 std::string tempBuffer = osslToPEM(OsslType::X509AUX, sk_X509_value(certv.get(), i));
790 if (tempName.empty() || tempBuffer.empty()) {
791 LogError("Failed to transform x509 AUX to PEM");
795 certChainName.push_back(std::move(tempName));
796 certChainBuffer.push_back(std::move(tempBuffer));
799 LogDebug("Convert cert chain to string success");
800 /* 4. insert extracted pkcs12 data to db */
801 result = insertToStore(storeTypes,
810 if (result != CERTSVC_SUCCESS)
811 rollbackStore(storeTypes, endCertName);
813 LogDebug("Success to import pkcs12 to store");
817 int pkcs12_has_password(const char *filepath, int *passworded)
819 if (filepath == NULL || passworded == NULL)
820 return CERTSVC_WRONG_ARGUMENT;
824 if ((stream = fopen(filepath, "rb")) == NULL)
825 return CERTSVC_IO_ERROR;
827 PKCS12 *container = d2i_PKCS12_fp(stream, NULL);
830 if (container == NULL)
833 EVP_PKEY *pkey = NULL;
835 int result = PKCS12_parse(container, NULL, &pkey, &cert, NULL);
836 PKCS12_free(container);
845 unsigned long e = ERR_get_error();
846 if (ERR_GET_REASON(e) == PKCS12_R_MAC_VERIFY_FAILURE) {
847 LogInfo("verify failed without password. file(" << filepath << ") is password-protected.");
848 *passworded = CERTSVC_TRUE;
851 ERR_error_string_n(e, buf, 1023);
852 LogError("Error on PKCS12_pasre file(" << filepath << "): " << buf);
856 *passworded = CERTSVC_FALSE;
859 return CERTSVC_SUCCESS;