2 * Copyright (c) 2015 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>
34 #include <openssl/err.h>
35 #include <openssl/pkcs12.h>
36 #include <openssl/sha.h>
37 #include <openssl/x509.h>
38 #include <openssl/pem.h>
40 #include "dpl/log/log.h"
41 #include "cert-svc/cerror.h"
43 #include "vcore/Certificate.h"
44 #include "vcore/Client.h"
45 #include "vcore/pkcs12.h"
47 #define SYSCALL(call) while (((call) == -1) && (errno == EINTR))
51 static const std::string START_CERT = "-----BEGIN CERTIFICATE-----";
52 static const std::string END_CERT = "-----END CERTIFICATE-----";
53 static const std::string START_TRUSTED = "-----BEGIN TRUSTED CERTIFICATE-----";
54 static const std::string END_TRUSTED = "-----END TRUSTED CERTIFICATE-----";
55 static const std::string START_KEY = "-----BEGIN PRIVATE KEY-----";
56 static const std::string END_KEY = "-----END PRIVATE KEY-----";
58 using ValidationCore::CertificatePtr;
59 using ValidationCore::Certificate;
61 using FileUniquePtr = std::unique_ptr<FILE, std::function<int(FILE *)>>;
62 using BioUniquePtr = std::unique_ptr<BIO, std::function<void(BIO *)>>;
63 using PKEYUniquePtr = std::unique_ptr<EVP_PKEY, std::function<void(EVP_PKEY *)>>;
64 using X509UniquePtr = std::unique_ptr<X509, std::function<void(X509 *)>>;
65 using X509StackUniquePtr = std::unique_ptr<STACK_OF(X509), std::function<void(STACK_OF(X509) *)>>;
67 void X509_stack_free(STACK_OF(X509) *stack)
72 inline bool hasStore(CertStoreType types, CertStoreType type)
74 return (types & type) != 0;
77 inline CertStoreType nextStore(CertStoreType type)
100 std::string generateGname(void)
105 unsigned char d[SHA_DIGEST_LENGTH];
108 SYSCALL(generator = open("/dev/urandom", O_RDONLY));
111 return std::string();
113 SYSCALL(result = read(generator, &random, sizeof(random)));
116 SYSCALL(close(generator));
117 return std::string();
120 SYSCALL(result = close(generator));
123 return std::string();
126 SHA1_Update(&ctx, &random, sizeof(random));
128 result = asprintf(&gname,
129 "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
130 "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
131 d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9],
132 d[10], d[11], d[12], d[13], d[14], d[15], d[16], d[17], d[18], d[19]);
135 return std::string();
137 std::string ret(gname);
142 std::string getCommonName(CertType type, const std::string &cert)
144 BioUniquePtr bio(BIO_new(BIO_s_mem()), BIO_free_all);
146 if (bio.get() == NULL) {
147 LogError("Failed to allocate memory.");
148 return std::string();
151 auto readCount = BIO_write(bio.get(), (const void *)cert.data(), (int)cert.length());
154 LogError("Failed to load cert into bio.");
155 return std::string();
162 case P12_INTERMEDIATE:
163 x509 = PEM_read_bio_X509_AUX(bio.get(), NULL, 0, NULL);
167 x509 = PEM_read_bio_X509(bio.get(), NULL, 0, NULL);
172 LogError("Failed to create x509 structure.");
173 return std::string();
176 X509UniquePtr x509Ptr(x509, X509_free);
177 const char *subject_c = X509_NAME_oneline(x509->cert_info->subject, NULL, 0);
179 if (subject_c == NULL) {
180 LogError("Failed to parse x509 structure");
181 return std::string();
184 return std::string(subject_c);
188 * column / common name / associated gname / prikey gname /
189 * PEM_CRT : common name / gname / none /
190 * P12_END_USER : alias / gname / prikey gname /
191 * P12_TRUSTED : common name / end cert gname / none /
192 * P12_INTERMEDIATE : common name / end cert gname / none /
195 int installPKEY(CertStoreType storeType,
196 const std::string &key,
197 const std::string &gname)
199 return vcore_client_install_certificate_to_store(
210 int installEndCert(CertStoreType storeType,
211 const std::string &cert,
212 const std::string &alias,
213 const std::string &gname,
214 const std::string &prikeyGname)
216 return vcore_client_install_certificate_to_store(
227 int installChainCert(CertStoreType storeType,
228 const std::string &cert,
229 const std::string &gname,
230 const std::string &endCertGname,
233 std::string commonName = getCommonName(type, cert);
234 return vcore_client_install_certificate_to_store(
239 endCertGname.c_str(),
244 int installCert(CertStoreType storeType,
245 const std::string &cert,
246 const std::string &gname)
248 std::string commonName = getCommonName(PEM_CRT, cert);
249 return vcore_client_install_certificate_to_store(
260 std::string readFromFile(const std::string &path)
264 if ((fp = fopen(path.c_str(), "rb")) == NULL) {
265 LogError("Fail to open file for reading : " << path);
266 return std::string();
269 FileUniquePtr filePtr(fp, fclose);
270 fseek(fp, 0L, SEEK_END);
274 LogError("Fail to get certificate length.");
275 return std::string();
279 char *content = (char *)malloc(sizeof(char) * (len + 1));
281 if (content == NULL) {
282 LogError("Fail to allocate memory");
283 return std::string();
286 memset(content, 0x00, len + 1);
287 size_t readLen = fread(content, sizeof(char), (size_t)len, fp);
289 if (readLen != (size_t)len) {
290 LogError("Fail to read file : " << path);
292 return std::string();
296 std::string ret(content);
301 std::string parseCRT(const std::string &cert)
306 from = cert.find(START_CERT);
307 to = cert.find(END_CERT);
308 tailLen = END_CERT.length();
310 if (from == std::string::npos || to == std::string::npos || from > to) {
311 from = cert.find(START_TRUSTED);
312 to = cert.find(END_TRUSTED);
313 tailLen = END_TRUSTED.length();
316 if (from == std::string::npos || to == std::string::npos || from > to)
317 return std::string();
319 return std::string(cert, from, to - from + tailLen);
322 #define _CERT_SVC_VERIFY_PKCS12
323 int verify_cert_details(X509 *cert, STACK_OF(X509) *certv)
325 int result = CERTSVC_SUCCESS;
326 char *pSubject = NULL;
327 char *pIssuerName = NULL;
328 X509_STORE_CTX *cert_ctx = NULL;
329 X509_STORE *cert_store = NULL;
331 #ifdef _CERT_SVC_VERIFY_PKCS12
334 pSubject = X509_NAME_oneline(cert->cert_info->subject, NULL, 0);
337 LogError("Failed to get subject name");
338 result = CERTSVC_FAIL;
342 pIssuerName = X509_NAME_oneline(cert->cert_info->issuer, NULL, 0);
345 LogError("Failed to get issuer name");
346 result = CERTSVC_FAIL;
350 if (strcmp((const char *)pSubject, (const char *)pIssuerName) == 0) {
352 EVP_PKEY *pKey = NULL;
353 pKey = X509_get_pubkey(cert);
356 LogError("Failed to get public key");
357 result = CERTSVC_FAIL;
361 if (X509_verify(cert, pKey) <= 0) {
362 LogError("P12 verification failed");
364 result = CERTSVC_FAIL;
368 LogDebug("P12 verification Success");
371 cert_store = X509_STORE_new();
374 LogError("Memory allocation failed");
375 result = CERTSVC_FAIL;
379 res = X509_STORE_load_locations(cert_store, NULL, TZ_SYS_CA_CERTS);
382 LogError("P12 load certificate store failed");
383 X509_STORE_free(cert_store);
384 result = CERTSVC_FAIL;
388 res = X509_STORE_set_default_paths(cert_store);
391 LogError("P12 load certificate store path failed");
392 X509_STORE_free(cert_store);
393 result = CERTSVC_FAIL;
397 /* initialise store and store context */
398 cert_ctx = X509_STORE_CTX_new();
400 if (cert_ctx == NULL) {
401 LogError("Memory allocation failed");
402 result = CERTSVC_FAIL;
406 /* construct store context */
407 if (!X509_STORE_CTX_init(cert_ctx, cert_store, cert, NULL)) {
408 LogError("Memory allocation failed");
409 result = CERTSVC_FAIL;
413 #ifdef P12_VERIFICATION_NEEDED
414 res = X509_verify_cert(cert_ctx);
417 LogError("P12 verification failed");
418 result = CERTSVC_FAIL;
422 LogDebug("P12 verification Success");
425 } else if (certv != NULL) {
427 cert_store = X509_STORE_new();
430 LogError("Memory allocation failed");
431 result = CERTSVC_FAIL;
435 res = X509_STORE_load_locations(cert_store, NULL, TZ_SYS_CA_CERTS);
438 LogError("P12 load certificate store failed");
439 result = CERTSVC_FAIL;
443 res = X509_STORE_set_default_paths(cert_store);
446 LogError("P12 load certificate path failed");
447 result = CERTSVC_FAIL;
451 /* initialise store and store context */
452 cert_ctx = X509_STORE_CTX_new();
454 if (cert_ctx == NULL) {
455 LogError("Memory allocation failed");
456 result = CERTSVC_FAIL;
460 /* construct store context */
461 if (!X509_STORE_CTX_init(cert_ctx, cert_store, cert, NULL)) {
462 LogError("Memory allocation failed");
463 result = CERTSVC_FAIL;
467 X509_STORE_CTX_trusted_stack(cert_ctx, certv);
468 #ifdef P12_VERIFICATION_NEEDED
469 res = X509_verify_cert(cert_ctx);
472 LogError("P12 verification failed");
473 result = CERTSVC_FAIL;
477 LogDebug("P12 verification Success");
481 #endif //_CERT_SVC_VERIFY_PKCS12
484 if (cert_store != NULL)
485 X509_STORE_free(cert_store);
488 X509_STORE_CTX_free(cert_ctx);
495 enum class OsslType : int {
501 std::string osslToPEM(OsslType type, void *data)
503 std::vector<char> buf(4096);
504 BioUniquePtr bio(BIO_new(BIO_s_mem()), BIO_free_all);
506 if (bio.get() == NULL)
507 return std::string();
511 PEM_write_bio_PrivateKey(bio.get(), static_cast<EVP_PKEY *>(data), NULL, NULL, 0, NULL, NULL);
515 PEM_write_bio_X509(bio.get(), static_cast<X509 *>(data));
518 case OsslType::X509AUX:
519 PEM_write_bio_X509_AUX(bio.get(), static_cast<X509 *>(data));
526 int size = BIO_read(bio.get(), buf.data(), 4096);
529 return std::string();
532 return std::string(buf.data());
535 int extractPkcs12(const std::string &path,
536 const std::string &password,
537 PKEYUniquePtr &keyPtr,
538 X509UniquePtr &certPtr,
539 X509StackUniquePtr &certvPtr)
543 if ((stream = fopen(path.c_str(), "rb")) == NULL) {
544 LogError("Unable to open the file for reading : " << path);
545 return CERTSVC_IO_ERROR;
548 PKCS12 *container = d2i_PKCS12_fp(stream, NULL);
551 if (container == NULL) {
552 LogError("Failed to parse the input file passed.");
556 EVP_PKEY *key = NULL;
558 STACK_OF(X509) *certv = NULL;
559 int result = PKCS12_parse(container, password.c_str(), &key, &cert, &certv);
560 PKCS12_free(container);
563 unsigned long e = ERR_get_error();
565 ERR_error_string_n(e, buf, 1023);
566 LogError("Failed to parse the file passed. openssl err: " << buf);
572 certvPtr.reset(certv);
573 return CERTSVC_SUCCESS;
576 void rollbackStore(CertStoreType storeTypes, const std::string &endCertName)
578 for (CertStoreType storeType = VPN_STORE; storeType < SYSTEM_STORE;
579 storeType = nextStore(storeType)) {
580 if (!hasStore(storeTypes, storeType))
583 char **certChainName = NULL;
585 int result = vcore_client_load_certificates_from_store(storeType, endCertName.c_str(),
586 &certChainName, &ncerts);
588 if (result != CERTSVC_SUCCESS) {
589 LogError("Unable to load certificates from store. result : " << result);
593 for (size_t i = 0; i < ncerts; i++) {
594 if (certChainName[i] == NULL)
597 vcore_client_delete_certificate_from_store(storeType, certChainName[i]);
598 free(certChainName[i]);
601 vcore_client_delete_certificate_from_store(storeType, endCertName.c_str());
605 int insertToStore(CertStoreType storeTypes,
606 const std::string &alias,
607 const std::string &prikeyName,
608 const std::string &prikeyBuffer,
609 const std::string &endCertName,
610 const std::string &endCertBuffer,
611 const std::vector<std::string> &certChainName,
612 const std::vector<std::string> &certChainBuffer)
614 size_t ncerts = certChainName.size();
616 for (CertStoreType storeType = VPN_STORE; storeType < SYSTEM_STORE;
617 storeType = nextStore(storeType)) {
618 if (!hasStore(storeTypes, storeType))
621 LogDebug("Processing store type : " << storeType);
622 int result = installPKEY(storeType, prikeyBuffer, prikeyName);
624 if (result != CERTSVC_SUCCESS) {
625 LogError("Failed to store the private key contents. result : " << result);
629 result = installEndCert(storeType, endCertBuffer, alias, endCertName, prikeyName);
631 if (result != CERTSVC_SUCCESS) {
632 LogError("Failed to install the end user certificate. result : " << result);
636 for (size_t i = 0; i < ncerts; i++) {
638 result = installChainCert(storeType, certChainBuffer[i], certChainName[i], endCertName,
641 result = installChainCert(storeType, certChainBuffer[i], certChainName[i], endCertName,
644 if (result != CERTSVC_SUCCESS) {
645 LogError("Failed to install the ca certificates. result : " << result);
651 LogDebug("Success to insert extracted pkcs12 data to db");
652 return CERTSVC_SUCCESS;
655 int insertToStorePEM(CertStoreType storeTypes, const std::string &path, const std::string &gname)
657 std::string content = readFromFile(path);
659 if (content.empty()) {
660 LogError("Failed to read the file : " << path);
661 return CERTSVC_IO_ERROR;
664 std::string parsed = parseCRT(content);
666 if (parsed.empty()) {
667 LogError("Failed to parse CRT : " << path);
671 for (CertStoreType storeType = VPN_STORE; storeType < SYSTEM_STORE;
672 storeType = nextStore(storeType)) {
673 if (!hasStore(storeTypes, storeType))
676 int result = installCert(storeType, parsed, gname);
678 if (result != CERTSVC_SUCCESS) {
679 LogError("Failed to install PEM/CRT to db store : " << storeType << " result : " << result);
680 rollbackStore(storeTypes, gname);
684 LogDebug("Success to install PEM/CRT to db store : " << storeType);
687 LogDebug("Success to install PEM/CRT to db stores : " << storeTypes);
688 return CERTSVC_SUCCESS;
691 } // namespace anonymous
694 int pkcs12_import_from_file_to_store(CertStoreType storeTypes,
696 const char *_password,
701 if (_alias == NULL || _path == NULL || strlen(_path) < 4) {
702 LogError("Invalid input parameter.");
703 return CERTSVC_WRONG_ARGUMENT;
706 std::string path(_path);
707 std::string alias(_alias);
708 std::string password;
710 if (_password != NULL)
711 password = std::string(_password);
713 LogDebug("pkcs12_import_from_file_to_store start. path[" << path << "] password[" << password <<
714 "] alias[" << alias << "]");
716 if (storeTypes & SYSTEM_STORE) {
717 LogError("User should not install any form of certificates in SYSTEM_STORE.");
718 return CERTSVC_INVALID_STORE_TYPE;
722 * Installs CRT and PEM files.
723 * We will passing NULL for private_key_gname and associated_gname parameter
724 * in installFilePEM(). Which means that there is no private key involved
725 * in the certificate which we are installing and there are no other
726 * certificates related with the current certificate which is installed
728 std::string suffix = path.substr(path.length() - 4, 4);
730 if (strcasecmp(suffix.c_str(), ".pem") == 0 || strcasecmp(suffix.c_str(), ".crt") == 0) {
731 std::string gnamePEM = generateGname();
732 result = insertToStorePEM(storeTypes, path, gnamePEM);
734 if (result != CERTSVC_SUCCESS)
735 LogError("Failed to install PEM/CRT file to store. gname : " << gnamePEM << " result : " << result);
740 LogDebug("Convert ossl type to string start");
741 /* 0. extract pkcs12 data from file */
742 PKEYUniquePtr key(nullptr, EVP_PKEY_free);
743 X509UniquePtr cert(nullptr, X509_free);
744 X509StackUniquePtr certv(nullptr, X509_stack_free);
745 result = extractPkcs12(path, password, key, cert, certv);
747 if (result != CERTSVC_SUCCESS) {
748 LogError("Failed to extract pkcs12 file. result : " << result);
752 LogDebug("extract pkcs12 to unique ptr success");
753 result = verify_cert_details(cert.get(), certv.get());
755 if (result != CERTSVC_SUCCESS) {
756 LogError("Failed to verify p12 certificate. result : " << result);
760 /* 1. handling private key */
761 std::string prikeyName = generateGname();
762 std::string prikeyBuffer = osslToPEM(OsslType::PKEY, key.get());
764 if (prikeyName.empty() || prikeyBuffer.empty()) {
765 LogError("Failed to transform pkey to PEM. result : " << result);
769 LogDebug("Convert pkey to string success");
770 /* 2. handling end user certificate */
771 std::string endCertName = generateGname();
772 std::string endCertBuffer = osslToPEM(OsslType::X509, cert.get());
774 if (endCertName.empty() || endCertBuffer.empty()) {
775 LogError("Failed to transform x509 to PEM. result : " << result);
779 LogDebug("Convert end cert to string success");
780 /* 3. handling certificate chain */
781 std::vector<std::string> certChainName;
782 std::vector<std::string> certChainBuffer;
783 int ncerts = certv ? sk_X509_num(certv.get()) : 0;
785 for (int i = 0; i < ncerts; i++) {
786 std::string tempName = generateGname();
787 std::string tempBuffer = osslToPEM(OsslType::X509AUX, sk_X509_value(certv.get(), i));
789 if (tempName.empty() || tempBuffer.empty()) {
790 LogError("Failed to transform x509 AUX to PEM");
794 certChainName.push_back(std::move(tempName));
795 certChainBuffer.push_back(std::move(tempBuffer));
798 LogDebug("Convert cert chain to string success");
799 /* 4. insert extracted pkcs12 data to db */
800 result = insertToStore(storeTypes,
809 if (result != CERTSVC_SUCCESS)
810 rollbackStore(storeTypes, endCertName);
812 LogDebug("Success to import pkcs12 to store");
816 int pkcs12_has_password(const char *filepath, int *passworded)
818 if (filepath == NULL || passworded == NULL)
819 return CERTSVC_WRONG_ARGUMENT;
823 if ((stream = fopen(filepath, "rb")) == NULL)
824 return CERTSVC_IO_ERROR;
826 PKCS12 *container = d2i_PKCS12_fp(stream, NULL);
829 if (container == NULL)
832 EVP_PKEY *pkey = NULL;
834 int result = PKCS12_parse(container, NULL, &pkey, &cert, NULL);
835 PKCS12_free(container);
844 unsigned long e = ERR_get_error();
845 if (ERR_GET_REASON(e) == PKCS12_R_MAC_VERIFY_FAILURE) {
846 LogInfo("verify failed without password. file(" << filepath << ") is password-protected.");
847 *passworded = CERTSVC_TRUE;
850 ERR_error_string_n(e, buf, 1023);
851 LogError("Error on PKCS12_pasre file(" << filepath << "): " << buf);
855 *passworded = CERTSVC_FALSE;
858 return CERTSVC_SUCCESS;