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 CertSvcStoreCertList *createStoreListNode(const std::string &gname, const std::string &title,
102 CertStoreType storeType)
104 CertSvcStoreCertList *node = (CertSvcStoreCertList *)malloc(sizeof(CertSvcStoreCertList));
109 node->gname = strdup(gname.c_str());
110 node->title = strdup(title.c_str());
111 node->status = ENABLED;
112 node->storeType = storeType;
115 if (node->gname == NULL || node->title == NULL) {
125 void destroyStoreList(CertSvcStoreCertList **certList, size_t *length)
127 if (certList == NULL || length == NULL) {
131 CertSvcStoreCertList *list = *certList;
134 CertSvcStoreCertList *next = list->next;
144 void addStoreListNode(CertSvcStoreCertList **list, CertSvcStoreCertList *node)
150 int appendStoreListNode(CertSvcStoreCertList **certList, size_t *length,
151 const std::string &gname, const std::string &alias,
152 CertStoreType storeType)
154 if (certList == NULL || length == NULL)
155 return CERTSVC_SUCCESS;
157 CertSvcStoreCertList *node = createStoreListNode(gname, alias, storeType);
159 return CERTSVC_BAD_ALLOC;
162 addStoreListNode(certList, node);
165 return CERTSVC_SUCCESS;
168 std::string generateGname(void)
173 unsigned char d[SHA_DIGEST_LENGTH];
176 SYSCALL(generator = open("/dev/urandom", O_RDONLY));
179 return std::string();
181 SYSCALL(result = read(generator, &random, sizeof(random)));
184 SYSCALL(close(generator));
185 return std::string();
188 SYSCALL(result = close(generator));
191 return std::string();
194 SHA1_Update(&ctx, &random, sizeof(random));
196 result = asprintf(&gname,
197 "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
198 "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
199 d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9],
200 d[10], d[11], d[12], d[13], d[14], d[15], d[16], d[17], d[18], d[19]);
203 return std::string();
205 std::string ret(gname);
210 std::string getCommonName(CertType type, const std::string &cert)
212 BioUniquePtr bio(BIO_new(BIO_s_mem()), BIO_free_all);
214 if (bio.get() == NULL) {
215 LogError("Failed to allocate memory.");
216 return std::string();
219 auto readCount = BIO_write(bio.get(), (const void *)cert.data(), (int)cert.length());
222 LogError("Failed to load cert into bio.");
223 return std::string();
230 case P12_INTERMEDIATE:
231 x509 = PEM_read_bio_X509_AUX(bio.get(), NULL, 0, NULL);
235 x509 = PEM_read_bio_X509(bio.get(), NULL, 0, NULL);
240 LogError("Failed to create x509 structure.");
241 return std::string();
244 X509UniquePtr x509Ptr(x509, X509_free);
245 const char *subject_c = X509_NAME_oneline(x509->cert_info->subject, NULL, 0);
247 if (subject_c == NULL) {
248 LogError("Failed to parse x509 structure");
249 return std::string();
252 return std::string(subject_c);
256 * column / common name / associated gname / prikey gname /
257 * PEM_CRT : common name / gname / none /
258 * P12_END_USER : alias / gname / prikey gname /
259 * P12_TRUSTED : common name / end cert gname / none /
260 * P12_INTERMEDIATE : common name / end cert gname / none /
263 int installPKEY(CertStoreType storeType,
264 const std::string &key,
265 const std::string &gname)
267 return vcore_client_install_certificate_to_store(
278 int installEndCert(CertStoreType storeType,
279 const std::string &cert,
280 const std::string &alias,
281 const std::string &gname,
282 const std::string &prikeyGname)
284 return vcore_client_install_certificate_to_store(
295 int installChainCert(CertStoreType storeType,
296 const std::string &cert,
297 const std::string &gname,
298 const std::string &endCertGname,
301 std::string commonName = getCommonName(type, cert);
302 return vcore_client_install_certificate_to_store(
307 endCertGname.c_str(),
312 int installCert(CertStoreType storeType,
313 const std::string &cert,
314 const std::string &gname,
315 const std::string &alias)
317 return vcore_client_install_certificate_to_store(
328 std::string readFromFile(const std::string &path)
332 if ((fp = fopen(path.c_str(), "rb")) == NULL) {
333 LogError("Fail to open file for reading : " << path);
334 return std::string();
337 FileUniquePtr filePtr(fp, fclose);
338 fseek(fp, 0L, SEEK_END);
341 if (len <= 0 || len == INT_MAX) {
342 LogError("Fail to get proper certificate.");
343 return std::string();
347 char *content = (char *)malloc(sizeof(char) * (len + 1));
349 if (content == NULL) {
350 LogError("Fail to allocate memory");
351 return std::string();
354 memset(content, 0x00, len + 1);
355 size_t readLen = fread(content, sizeof(char), (size_t)len, fp);
357 if (readLen != (size_t)len) {
358 LogError("Fail to read file : " << path);
360 return std::string();
364 std::string ret(content);
369 std::string parseCRT(const std::string &cert)
374 from = cert.find(START_CERT);
375 to = cert.find(END_CERT);
376 tailLen = END_CERT.length();
378 if (from == std::string::npos || to == std::string::npos || from > to) {
379 from = cert.find(START_TRUSTED);
380 to = cert.find(END_TRUSTED);
381 tailLen = END_TRUSTED.length();
384 if (from == std::string::npos || to == std::string::npos || from > to)
385 return std::string();
387 return std::string(cert, from, to - from + tailLen);
390 #define _CERT_SVC_VERIFY_PKCS12
391 int verify_cert_details(X509 *cert, STACK_OF(X509) *certv)
393 int result = CERTSVC_SUCCESS;
394 char *pSubject = NULL;
395 char *pIssuerName = NULL;
396 X509_STORE_CTX *cert_ctx = NULL;
397 X509_STORE *cert_store = NULL;
399 #ifdef _CERT_SVC_VERIFY_PKCS12
402 pSubject = X509_NAME_oneline(cert->cert_info->subject, NULL, 0);
405 LogError("Failed to get subject name");
406 result = CERTSVC_FAIL;
410 pIssuerName = X509_NAME_oneline(cert->cert_info->issuer, NULL, 0);
413 LogError("Failed to get issuer name");
414 result = CERTSVC_FAIL;
418 if (strcmp((const char *)pSubject, (const char *)pIssuerName) == 0) {
420 EVP_PKEY *pKey = NULL;
421 pKey = X509_get_pubkey(cert);
424 LogError("Failed to get public key");
425 result = CERTSVC_FAIL;
429 if (X509_verify(cert, pKey) <= 0) {
430 LogError("P12 verification failed");
432 result = CERTSVC_FAIL;
436 LogDebug("P12 verification Success");
439 cert_store = X509_STORE_new();
442 LogError("Memory allocation failed");
443 result = CERTSVC_FAIL;
447 res = X509_STORE_load_locations(cert_store, NULL, TZ_SYS_CA_CERTS);
450 LogError("P12 load certificate store failed");
451 X509_STORE_free(cert_store);
452 result = CERTSVC_FAIL;
456 res = X509_STORE_set_default_paths(cert_store);
459 LogError("P12 load certificate store path failed");
460 X509_STORE_free(cert_store);
461 result = CERTSVC_FAIL;
465 /* initialise store and store context */
466 cert_ctx = X509_STORE_CTX_new();
468 if (cert_ctx == NULL) {
469 LogError("Memory allocation failed");
470 result = CERTSVC_FAIL;
474 /* construct store context */
475 if (!X509_STORE_CTX_init(cert_ctx, cert_store, cert, NULL)) {
476 LogError("Memory allocation failed");
477 result = CERTSVC_FAIL;
481 #ifdef P12_VERIFICATION_NEEDED
482 res = X509_verify_cert(cert_ctx);
485 LogError("P12 verification failed");
486 result = CERTSVC_FAIL;
490 LogDebug("P12 verification Success");
493 } else if (certv != NULL) {
495 cert_store = X509_STORE_new();
498 LogError("Memory allocation failed");
499 result = CERTSVC_FAIL;
503 res = X509_STORE_load_locations(cert_store, NULL, TZ_SYS_CA_CERTS);
506 LogError("P12 load certificate store failed");
507 result = CERTSVC_FAIL;
511 res = X509_STORE_set_default_paths(cert_store);
514 LogError("P12 load certificate path failed");
515 result = CERTSVC_FAIL;
519 /* initialise store and store context */
520 cert_ctx = X509_STORE_CTX_new();
522 if (cert_ctx == NULL) {
523 LogError("Memory allocation failed");
524 result = CERTSVC_FAIL;
528 /* construct store context */
529 if (!X509_STORE_CTX_init(cert_ctx, cert_store, cert, NULL)) {
530 LogError("Memory allocation failed");
531 result = CERTSVC_FAIL;
535 X509_STORE_CTX_trusted_stack(cert_ctx, certv);
536 #ifdef P12_VERIFICATION_NEEDED
537 res = X509_verify_cert(cert_ctx);
540 LogError("P12 verification failed");
541 result = CERTSVC_FAIL;
545 LogDebug("P12 verification Success");
549 #endif //_CERT_SVC_VERIFY_PKCS12
552 if (cert_store != NULL)
553 X509_STORE_free(cert_store);
556 X509_STORE_CTX_free(cert_ctx);
563 enum class OsslType : int {
569 std::string osslToPEM(OsslType type, void *data)
571 std::vector<char> buf(4096);
572 BioUniquePtr bio(BIO_new(BIO_s_mem()), BIO_free_all);
574 if (bio.get() == NULL)
575 return std::string();
579 PEM_write_bio_PrivateKey(bio.get(), static_cast<EVP_PKEY *>(data), NULL, NULL, 0, NULL, NULL);
583 PEM_write_bio_X509(bio.get(), static_cast<X509 *>(data));
586 case OsslType::X509AUX:
587 PEM_write_bio_X509_AUX(bio.get(), static_cast<X509 *>(data));
594 int size = BIO_read(bio.get(), buf.data(), 4096);
597 return std::string();
600 return std::string(buf.data());
603 int extractPkcs12(const std::string &path,
604 const std::string &password,
605 PKEYUniquePtr &keyPtr,
606 X509UniquePtr &certPtr,
607 X509StackUniquePtr &certvPtr)
611 if ((stream = fopen(path.c_str(), "rb")) == NULL) {
612 LogError("Unable to open the file for reading : " << path);
613 return CERTSVC_IO_ERROR;
616 PKCS12 *container = d2i_PKCS12_fp(stream, NULL);
619 if (container == NULL) {
620 LogError("Failed to parse the input file passed.");
624 EVP_PKEY *key = NULL;
626 STACK_OF(X509) *certv = NULL;
627 int result = PKCS12_parse(container, password.c_str(), &key, &cert, &certv);
628 PKCS12_free(container);
631 unsigned long e = ERR_get_error();
633 ERR_error_string_n(e, buf, 1023);
634 LogError("Failed to parse the file passed. openssl err: " << buf);
640 certvPtr.reset(certv);
641 return CERTSVC_SUCCESS;
644 void rollbackStore(CertStoreType storeTypes, const std::string &endCertName)
646 for (CertStoreType storeType = VPN_STORE; storeType < SYSTEM_STORE;
647 storeType = nextStore(storeType)) {
648 if (!hasStore(storeTypes, storeType))
651 char **certChainName = NULL;
653 int result = vcore_client_load_certificates_from_store(storeType, endCertName.c_str(),
654 &certChainName, &ncerts);
656 if (result != CERTSVC_SUCCESS) {
657 LogError("Unable to load certificates from store. result : " << result);
661 for (size_t i = 0; i < ncerts; i++) {
662 if (certChainName[i] == NULL)
665 vcore_client_delete_certificate_from_store(storeType, certChainName[i]);
666 free(certChainName[i]);
669 vcore_client_delete_certificate_from_store(storeType, endCertName.c_str());
673 int insertToStore(CertStoreType storeTypes,
674 const std::string &alias,
675 const std::string &prikeyName,
676 const std::string &prikeyBuffer,
677 const std::string &endCertName,
678 const std::string &endCertBuffer,
679 const std::vector<std::string> &certChainName,
680 const std::vector<std::string> &certChainBuffer,
681 CertSvcStoreCertList **certList,
684 size_t ncerts = certChainName.size();
686 for (CertStoreType storeType = VPN_STORE; storeType < SYSTEM_STORE;
687 storeType = nextStore(storeType)) {
688 if (!hasStore(storeTypes, storeType))
691 LogDebug("Processing store type : " << storeType);
692 int result = installPKEY(storeType, prikeyBuffer, prikeyName);
694 if (result != CERTSVC_SUCCESS) {
695 LogError("Failed to store the private key contents. result : " << result);
699 result = installEndCert(storeType, endCertBuffer, alias, endCertName, prikeyName);
701 if (result != CERTSVC_SUCCESS) {
702 LogError("Failed to install the end user certificate. result : " << result);
706 int res = appendStoreListNode(certList, length, endCertName, alias, storeType);
707 if (res != CERTSVC_SUCCESS) {
708 LogError("Failed to append store list node.");
712 for (size_t i = 0; i < ncerts; i++) {
714 result = installChainCert(storeType, certChainBuffer[i], certChainName[i], endCertName,
717 result = installChainCert(storeType, certChainBuffer[i], certChainName[i], endCertName,
720 if (result != CERTSVC_SUCCESS) {
721 LogError("Failed to install the ca certificates. result : " << result);
725 int res = appendStoreListNode(certList, length, certChainName[i], alias, storeType);
726 if (res != CERTSVC_SUCCESS) {
727 LogError("Failed to append store list node.");
733 LogDebug("Success to insert extracted pkcs12 data to db");
734 return CERTSVC_SUCCESS;
737 int insertToStorePEM(CertStoreType storeTypes, const std::string &path, const std::string &gname,
738 const std::string &alias, CertSvcStoreCertList **certList, size_t *length)
740 std::string content = readFromFile(path);
742 if (content.empty()) {
743 LogError("Failed to read the file : " << path);
744 return CERTSVC_IO_ERROR;
747 std::string parsed = parseCRT(content);
749 if (parsed.empty()) {
750 LogError("Failed to parse CRT : " << path);
754 for (CertStoreType storeType = VPN_STORE; storeType < SYSTEM_STORE;
755 storeType = nextStore(storeType)) {
756 if (!hasStore(storeTypes, storeType))
759 int result = installCert(storeType, parsed, gname, alias);
761 if (result != CERTSVC_SUCCESS) {
762 LogError("Failed to install PEM/CRT to db store : " << storeType << " result : " << result);
763 rollbackStore(storeTypes, gname);
767 int res = appendStoreListNode(certList, length, gname, alias, storeType);
768 if (res != CERTSVC_SUCCESS) {
769 rollbackStore(storeTypes, gname);
770 LogError("Failed to append store list node.");
774 LogDebug("Success to install PEM/CRT to db store : " << storeType);
777 LogDebug("Success to install PEM/CRT to db stores : " << storeTypes);
778 return CERTSVC_SUCCESS;
781 } // namespace anonymous
784 int pkcs12_import_from_file_to_store(CertStoreType storeTypes,
786 const char *_password,
788 CertSvcStoreCertList **certList,
793 if (_alias == NULL || _path == NULL || strlen(_path) < 4) {
794 LogError("Invalid input parameter.");
795 return CERTSVC_WRONG_ARGUMENT;
798 std::string path(_path);
799 std::string alias(_alias);
800 std::string password;
802 if (_password != NULL)
803 password = std::string(_password);
805 LogDebug("pkcs12_import_from_file_to_store start. path[" << path << "] password[" << password <<
806 "] alias[" << alias << "]");
808 if (storeTypes & SYSTEM_STORE) {
809 LogError("User should not install any form of certificates in SYSTEM_STORE.");
810 return CERTSVC_INVALID_STORE_TYPE;
814 * Installs CRT and PEM files.
815 * We will passing NULL for private_key_gname and associated_gname parameter
816 * in installFilePEM(). Which means that there is no private key involved
817 * in the certificate which we are installing and there are no other
818 * certificates related with the current certificate which is installed
820 std::string suffix = path.substr(path.length() - 4, 4);
822 if (strcasecmp(suffix.c_str(), ".pem") == 0 || strcasecmp(suffix.c_str(), ".crt") == 0) {
823 std::string gnamePEM = generateGname();
824 result = insertToStorePEM(storeTypes, path, gnamePEM, alias, certList, length);
826 if (result != CERTSVC_SUCCESS) {
827 destroyStoreList(certList, length);
828 LogError("Failed to install PEM/CRT file to store. gname : " << gnamePEM << " result : " << result);
834 LogDebug("Convert ossl type to string start");
835 /* 0. extract pkcs12 data from file */
836 PKEYUniquePtr key(nullptr, EVP_PKEY_free);
837 X509UniquePtr cert(nullptr, X509_free);
838 X509StackUniquePtr certv(nullptr, X509_stack_free);
839 result = extractPkcs12(path, password, key, cert, certv);
841 if (result != CERTSVC_SUCCESS) {
842 LogError("Failed to extract pkcs12 file. result : " << result);
846 LogDebug("extract pkcs12 to unique ptr success");
847 result = verify_cert_details(cert.get(), certv.get());
849 if (result != CERTSVC_SUCCESS) {
850 LogError("Failed to verify p12 certificate. result : " << result);
854 /* 1. handling private key */
855 std::string prikeyName = generateGname();
856 std::string prikeyBuffer = osslToPEM(OsslType::PKEY, key.get());
858 if (prikeyName.empty() || prikeyBuffer.empty()) {
859 LogError("Failed to transform pkey to PEM. result : " << result);
863 LogDebug("Convert pkey to string success");
864 /* 2. handling end user certificate */
865 std::string endCertName = generateGname();
866 std::string endCertBuffer = osslToPEM(OsslType::X509, cert.get());
868 if (endCertName.empty() || endCertBuffer.empty()) {
869 LogError("Failed to transform x509 to PEM. result : " << result);
873 LogDebug("Convert end cert to string success");
874 /* 3. handling certificate chain */
875 std::vector<std::string> certChainName;
876 std::vector<std::string> certChainBuffer;
877 int ncerts = certv ? sk_X509_num(certv.get()) : 0;
879 for (int i = 0; i < ncerts; i++) {
880 std::string tempName = generateGname();
881 std::string tempBuffer = osslToPEM(OsslType::X509AUX, sk_X509_value(certv.get(), i));
883 if (tempName.empty() || tempBuffer.empty()) {
884 LogError("Failed to transform x509 AUX to PEM");
888 certChainName.push_back(std::move(tempName));
889 certChainBuffer.push_back(std::move(tempBuffer));
892 LogDebug("Convert cert chain to string success");
893 /* 4. insert extracted pkcs12 data to db */
894 result = insertToStore(storeTypes,
905 if (result != CERTSVC_SUCCESS) {
906 destroyStoreList(certList, length);
907 rollbackStore(storeTypes, endCertName);
910 LogDebug("Success to import pkcs12 to store");
914 int pkcs12_has_password(const char *filepath, int *passworded)
916 if (filepath == NULL || passworded == NULL)
917 return CERTSVC_WRONG_ARGUMENT;
921 if ((stream = fopen(filepath, "rb")) == NULL)
922 return CERTSVC_IO_ERROR;
924 PKCS12 *container = d2i_PKCS12_fp(stream, NULL);
927 if (container == NULL)
930 EVP_PKEY *pkey = NULL;
932 int result = PKCS12_parse(container, NULL, &pkey, &cert, NULL);
933 PKCS12_free(container);
942 unsigned long e = ERR_get_error();
943 if (ERR_GET_REASON(e) == PKCS12_R_MAC_VERIFY_FAILURE) {
944 LogInfo("verify failed without password. file(" << filepath << ") is password-protected.");
945 *passworded = CERTSVC_TRUE;
948 ERR_error_string_n(e, buf, 1023);
949 LogError("Error on PKCS12_pasre file(" << filepath << "): " << buf);
953 *passworded = CERTSVC_FALSE;
956 return CERTSVC_SUCCESS;