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)
80 case NONE_STORE: return VPN_STORE;
81 case VPN_STORE: return WIFI_STORE;
82 case WIFI_STORE: return EMAIL_STORE;
83 case EMAIL_STORE: return SYSTEM_STORE;
84 case SYSTEM_STORE: return NONE_STORE;
85 default: return NONE_STORE;
89 std::string generateGname(void)
94 unsigned char d[SHA_DIGEST_LENGTH];
98 SYSCALL(generator = open("/dev/urandom", O_RDONLY));
100 return std::string();
101 SYSCALL(result = read(generator, &random, sizeof(random)));
103 SYSCALL(close(generator));
104 return std::string();
106 SYSCALL(result = close(generator));
108 return std::string();
111 SHA1_Update(&ctx, &random, sizeof(random));
114 result = asprintf(&gname,
115 "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
116 "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
117 d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9],
118 d[10], d[11], d[12], d[13], d[14], d[15], d[16], d[17], d[18], d[19]);
121 return std::string();
123 std::string ret(gname);
130 std::string getCommonName(CertType type, const std::string &cert)
132 BioUniquePtr bio(BIO_new(BIO_s_mem()), BIO_free_all);
133 if (bio.get() == NULL) {
134 LogError("Failed to allocate memory.");
135 return std::string();
138 auto readCount = BIO_write(bio.get(), (const void *)cert.data(), (int)cert.length());
140 LogError("Failed to load cert into bio.");
141 return std::string();
147 case P12_INTERMEDIATE:
148 x509 = PEM_read_bio_X509_AUX(bio.get(), NULL, 0, NULL);
152 x509 = PEM_read_bio_X509(bio.get(), NULL, 0, NULL);
157 LogError("Failed to create x509 structure.");
158 return std::string();
161 X509UniquePtr x509Ptr(x509, X509_free);
163 const char *subject_c = X509_NAME_oneline(x509->cert_info->subject, NULL, 0);
164 if (subject_c == NULL) {
165 LogError("Failed to parse x509 structure");
166 return std::string();
169 return std::string(subject_c);
173 * column / common name / associated gname / prikey gname /
174 * PEM_CRT : common name / gname / none /
175 * P12_END_USER : alias / gname / prikey gname /
176 * P12_TRUSTED : common name / end cert gname / none /
177 * P12_INTERMEDIATE : common name / end cert gname / none /
180 int installPKEY(CertStoreType storeType,
181 const std::string &key,
182 const std::string &gname)
184 return vcore_client_install_certificate_to_store(
195 int installEndCert(CertStoreType storeType,
196 const std::string &cert,
197 const std::string &alias,
198 const std::string &gname,
199 const std::string &prikeyGname)
201 return vcore_client_install_certificate_to_store(
212 int installChainCert(CertStoreType storeType,
213 const std::string &cert,
214 const std::string &gname,
215 const std::string &endCertGname,
218 std::string commonName = getCommonName(type, cert);
220 return vcore_client_install_certificate_to_store(
225 endCertGname.c_str(),
230 int installCert(CertStoreType storeType,
231 const std::string &cert,
232 const std::string &gname)
234 std::string commonName = getCommonName(PEM_CRT, cert);
236 return vcore_client_install_certificate_to_store(
247 std::string readFromFile(const std::string &path)
250 if ((fp = fopen(path.c_str(), "rb")) == NULL) {
251 LogError("Fail to open file for reading : " << path);
252 return std::string();
255 FileUniquePtr filePtr(fp, fclose);
257 fseek(fp, 0L, SEEK_END);
260 LogError("Fail to get certificate length.");
261 return std::string();
266 char *content = (char *)malloc(sizeof(char) * (len + 1));
267 if (content == NULL) {
268 LogError("Fail to allocate memory");
269 return std::string();
272 memset(content, 0x00, len + 1);
273 size_t readLen = fread(content, sizeof(char), (size_t)len, fp);
274 if (readLen != (size_t)len) {
275 LogError("Fail to read file : " << path);
277 return std::string();
282 std::string ret(content);
289 std::string parseCRT(const std::string &cert)
295 from = cert.find(START_CERT);
296 to = cert.find(END_CERT);
297 tailLen = END_CERT.length();
299 if (from == std::string::npos || to == std::string::npos || from > to) {
300 from = cert.find(START_TRUSTED);
301 to = cert.find(END_TRUSTED);
302 tailLen = END_TRUSTED.length();
305 if (from == std::string::npos || to == std::string::npos || from > to)
306 return std::string();
308 return std::string(cert, from, to - from + tailLen);
311 #define _CERT_SVC_VERIFY_PKCS12
312 int verify_cert_details(X509 *cert, STACK_OF(X509) *certv)
314 int result = CERTSVC_SUCCESS;
315 char* pSubject = NULL;
316 char* pIssuerName = NULL;
317 X509_STORE_CTX *cert_ctx = NULL;
318 X509_STORE *cert_store = NULL;
321 #ifdef _CERT_SVC_VERIFY_PKCS12
323 pSubject = X509_NAME_oneline(cert->cert_info->subject, NULL, 0);
325 LogError("Failed to get subject name");
326 result = CERTSVC_FAIL;
330 pIssuerName = X509_NAME_oneline(cert->cert_info->issuer, NULL, 0);
332 LogError("Failed to get issuer name");
333 result = CERTSVC_FAIL;
337 if (strcmp((const char*)pSubject, (const char*)pIssuerName) == 0) {
339 EVP_PKEY *pKey = NULL;
340 pKey = X509_get_pubkey(cert);
342 LogError("Failed to get public key");
343 result = CERTSVC_FAIL;
347 if (X509_verify(cert, pKey) <= 0) {
348 LogError("P12 verification failed");
350 result = CERTSVC_FAIL;
353 LogDebug("P12 verification Success");
356 cert_store = X509_STORE_new();
358 LogError("Memory allocation failed");
359 result = CERTSVC_FAIL;
363 res = X509_STORE_load_locations(cert_store, NULL, "/opt/etc/ssl/certs/");
365 LogError("P12 load certificate store failed");
366 X509_STORE_free(cert_store);
367 result = CERTSVC_FAIL;
371 res = X509_STORE_set_default_paths(cert_store);
373 LogError("P12 load certificate store path failed");
374 X509_STORE_free(cert_store);
375 result = CERTSVC_FAIL;
379 /* initialise store and store context */
380 cert_ctx = X509_STORE_CTX_new();
381 if (cert_ctx == NULL) {
382 LogError("Memory allocation failed");
383 result = CERTSVC_FAIL;
387 /* construct store context */
388 if (!X509_STORE_CTX_init(cert_ctx, cert_store, cert, NULL)) {
389 LogError("Memory allocation failed");
390 result = CERTSVC_FAIL;
394 #ifdef P12_VERIFICATION_NEEDED
395 res = X509_verify_cert(cert_ctx);
397 LogError("P12 verification failed");
398 result = CERTSVC_FAIL;
401 LogDebug("P12 verification Success");
404 } else if (certv != NULL) {
406 cert_store = X509_STORE_new();
408 LogError("Memory allocation failed");
409 result = CERTSVC_FAIL;
413 res = X509_STORE_load_locations(cert_store, NULL, SYSTEM_CERT_DIR);
415 LogError("P12 load certificate store failed");
416 result = CERTSVC_FAIL;
420 res = X509_STORE_set_default_paths(cert_store);
422 LogError("P12 load certificate path failed");
423 result = CERTSVC_FAIL;
427 /* initialise store and store context */
428 cert_ctx = X509_STORE_CTX_new();
429 if (cert_ctx == NULL) {
430 LogError("Memory allocation failed");
431 result = CERTSVC_FAIL;
435 /* construct store context */
436 if (!X509_STORE_CTX_init(cert_ctx, cert_store, cert, NULL)) {
437 LogError("Memory allocation failed");
438 result = CERTSVC_FAIL;
442 X509_STORE_CTX_trusted_stack(cert_ctx, certv);
443 #ifdef P12_VERIFICATION_NEEDED
444 res = X509_verify_cert(cert_ctx);
446 LogError("P12 verification failed");
447 result = CERTSVC_FAIL;
450 LogDebug("P12 verification Success");
453 #endif //_CERT_SVC_VERIFY_PKCS12
456 if (cert_store != NULL)
457 X509_STORE_free(cert_store);
459 X509_STORE_CTX_free(cert_ctx);
467 enum class OsslType : int {
473 std::string osslToPEM(OsslType type, void *data)
475 std::vector<char> buf(4096);
476 BioUniquePtr bio(BIO_new(BIO_s_mem()), BIO_free_all);
477 if (bio.get() == NULL)
478 return std::string();
482 PEM_write_bio_PrivateKey(bio.get(), static_cast<EVP_PKEY *>(data), NULL, NULL, 0, NULL, NULL);
486 PEM_write_bio_X509(bio.get(), static_cast<X509 *>(data));
489 case OsslType::X509AUX:
490 PEM_write_bio_X509_AUX(bio.get(), static_cast<X509 *>(data));
497 int size = BIO_read(bio.get(), buf.data(), 4096);
499 return std::string();
503 return std::string(buf.data());
506 int extractPkcs12(const std::string &path,
507 const std::string &password,
508 PKEYUniquePtr &keyPtr,
509 X509UniquePtr &certPtr,
510 X509StackUniquePtr &certvPtr)
513 if ((stream = fopen(path.c_str(), "rb")) == NULL) {
514 LogError("Unable to open the file for reading : " << path);
515 return CERTSVC_IO_ERROR;
518 PKCS12 *container = d2i_PKCS12_fp(stream, NULL);
520 if (container == NULL) {
521 LogError("Failed to parse the input file passed.");
525 EVP_PKEY *key = NULL;
527 STACK_OF(X509) *certv = NULL;
528 int result = PKCS12_parse(container, password.c_str(), &key, &cert, &certv);
529 PKCS12_free(container);
531 LogError("Failed to parse the file passed. openssl err : " << ERR_get_error());
537 certvPtr.reset(certv);
539 return CERTSVC_SUCCESS;
542 void rollbackStore(CertStoreType storeTypes, const std::string &endCertName)
544 for (CertStoreType storeType = VPN_STORE; storeType < SYSTEM_STORE; storeType = nextStore(storeType)) {
545 if (!hasStore(storeTypes, storeType))
548 char **certChainName = NULL;
551 int result = vcore_client_load_certificates_from_store(storeType, endCertName.c_str(), &certChainName, &ncerts);
552 if (result != CERTSVC_SUCCESS) {
553 LogError("Unable to load certificates from store. result : " << result);
557 for (size_t i = 0; i < ncerts; i++) {
558 if (certChainName[i] == NULL)
561 vcore_client_delete_certificate_from_store(storeType, certChainName[i]);
562 free(certChainName[i]);
565 vcore_client_delete_certificate_from_store(storeType, endCertName.c_str());
569 int insertToStore(CertStoreType storeTypes,
570 const std::string &alias,
571 const std::string &prikeyName,
572 const std::string &prikeyBuffer,
573 const std::string &endCertName,
574 const std::string &endCertBuffer,
575 const std::vector<std::string> &certChainName,
576 const std::vector<std::string> &certChainBuffer)
578 size_t ncerts = certChainName.size();
580 for (CertStoreType storeType = VPN_STORE; storeType < SYSTEM_STORE; storeType = nextStore(storeType)) {
581 if (!hasStore(storeTypes, storeType))
584 LogDebug("Processing store type : " << storeType);
586 int result = installPKEY(storeType, prikeyBuffer, prikeyName);
587 if (result != CERTSVC_SUCCESS) {
588 LogError("Failed to store the private key contents. result : " << result);
592 result = installEndCert(storeType, endCertBuffer, alias, endCertName, prikeyName);
593 if (result != CERTSVC_SUCCESS) {
594 LogError("Failed to install the end user certificate. result : " << result);
598 for (size_t i = 0; i < ncerts; i++) {
600 result = installChainCert(storeType, certChainBuffer[i], certChainName[i], endCertName, P12_INTERMEDIATE);
602 result = installChainCert(storeType, certChainBuffer[i], certChainName[i], endCertName, P12_TRUSTED);
604 if (result != CERTSVC_SUCCESS) {
605 LogError("Failed to install the ca certificates. result : " << result);
611 LogDebug("Success to insert extracted pkcs12 data to db");
613 return CERTSVC_SUCCESS;
616 int insertToStorePEM(CertStoreType storeTypes, const std::string &path, const std::string &gname)
618 std::string content = readFromFile(path);
619 if (content.empty()) {
620 LogError("Failed to read the file : " << path);
621 return CERTSVC_IO_ERROR;
624 std::string parsed = parseCRT(content);
625 if (parsed.empty()) {
626 LogError("Failed to parse CRT : " << path);
630 for (CertStoreType storeType = VPN_STORE; storeType < SYSTEM_STORE; storeType = nextStore(storeType)) {
631 if (!hasStore(storeTypes, storeType))
634 int result = installCert(storeType, parsed, gname);
635 if (result != CERTSVC_SUCCESS) {
636 LogError("Failed to install PEM/CRT to db store : " << storeType << " result : " << result);
637 rollbackStore(storeTypes, gname);
641 LogDebug("Success to install PEM/CRT to db store : " << storeType);
644 LogDebug("Success to install PEM/CRT to db stores : " << storeTypes);
646 return CERTSVC_SUCCESS;
649 } // namespace anonymous
652 int pkcs12_import_from_file_to_store(CertStoreType storeTypes,
654 const char *_password,
660 if (_alias == NULL || _path == NULL || strlen(_path) < 4) {
661 LogError("Invalid input parameter.");
662 return CERTSVC_WRONG_ARGUMENT;
665 std::string path(_path);
666 std::string alias(_alias);
667 std::string password;
668 if (_password != NULL)
669 password = std::string(_password);
671 LogDebug("pkcs12_import_from_file_to_store start. path[" << path << "] password[" << password << "] alias[" << alias << "]");
673 if (storeTypes & SYSTEM_STORE) {
674 LogError("User should not install any form of certificates in SYSTEM_STORE.");
675 return CERTSVC_INVALID_STORE_TYPE;
679 * Installs CRT and PEM files.
680 * We will passing NULL for private_key_gname and associated_gname parameter
681 * in installFilePEM(). Which means that there is no private key involved
682 * in the certificate which we are installing and there are no other
683 * certificates related with the current certificate which is installed
685 std::string suffix = path.substr(path.length() - 4, 4);
686 if (strcasecmp(suffix.c_str(), ".pem") == 0 || strcasecmp(suffix.c_str(), ".crt") == 0) {
687 std::string gnamePEM = generateGname();
688 result = insertToStorePEM(storeTypes, path, gnamePEM);
689 if (result != CERTSVC_SUCCESS)
690 LogError("Failed to install PEM/CRT file to store. gname : " << gnamePEM << " result : " << result);
695 LogDebug("Convert ossl type to string start");
697 /* 0. extract pkcs12 data from file */
698 PKEYUniquePtr key(nullptr, EVP_PKEY_free);
699 X509UniquePtr cert(nullptr, X509_free);
700 X509StackUniquePtr certv(nullptr, X509_stack_free);
701 result = extractPkcs12(path, password, key, cert, certv);
702 if (result != CERTSVC_SUCCESS) {
703 LogError("Failed to extract pkcs12 file. result : " << result);
707 LogDebug("extract pkcs12 to unique ptr success");
709 result = verify_cert_details(cert.get(), certv.get());
710 if (result != CERTSVC_SUCCESS) {
711 LogError("Failed to verify p12 certificate. result : " << result);
715 /* 1. handling private key */
716 std::string prikeyName = generateGname();
717 std::string prikeyBuffer = osslToPEM(OsslType::PKEY, key.get());
718 if (prikeyName.empty() || prikeyBuffer.empty()) {
719 LogError("Failed to transform pkey to PEM. result : " << result);
723 LogDebug("Convert pkey to string success");
725 /* 2. handling end user certificate */
726 std::string endCertName = generateGname();
727 std::string endCertBuffer = osslToPEM(OsslType::X509, cert.get());
728 if (endCertName.empty() || endCertBuffer.empty()) {
729 LogError("Failed to transform x509 to PEM. result : " << result);
733 LogDebug("Convert end cert to string success");
735 /* 3. handling certificate chain */
736 std::vector<std::string> certChainName;
737 std::vector<std::string> certChainBuffer;
738 int ncerts = certv ? sk_X509_num(certv.get()) : 0;
739 for (int i = 0; i < ncerts; i++) {
740 std::string tempName = generateGname();
741 std::string tempBuffer = osslToPEM(OsslType::X509AUX, sk_X509_value(certv.get(), i));
742 if (tempName.empty() || tempBuffer.empty()) {
743 LogError("Failed to transform x509 AUX to PEM");
747 certChainName.push_back(std::move(tempName));
748 certChainBuffer.push_back(std::move(tempBuffer));
751 LogDebug("Convert cert chain to string success");
753 /* 4. insert extracted pkcs12 data to db */
754 result = insertToStore(storeTypes,
763 if (result != CERTSVC_SUCCESS)
764 rollbackStore(storeTypes, endCertName);
766 LogDebug("Success to import pkcs12 to store");
771 int pkcs12_has_password(const char *filepath, int *passworded)
773 if (filepath == NULL || passworded == NULL)
774 return CERTSVC_WRONG_ARGUMENT;
777 if ((stream = fopen(filepath, "rb")) == NULL)
778 return CERTSVC_IO_ERROR;
780 PKCS12 *container = d2i_PKCS12_fp(stream, NULL);
783 if (container == NULL)
786 EVP_PKEY *pkey = NULL;
788 int result = PKCS12_parse(container, NULL, &pkey, &cert, NULL);
790 PKCS12_free(container);
797 if (result != 1 && ERR_GET_REASON(ERR_peek_last_error()) != PKCS12_R_MAC_VERIFY_FAILURE)
800 *passworded = (result == 1) ? 1 : 0;
802 return CERTSVC_SUCCESS;