host/securitylib/gencert.h (728 lines of code) (raw):

/// /// \file gencert.h /// /// \brief /// #ifndef GENCERT_H #define GENCERT_H #include <iostream> #include <cstdlib> #include <cstdio> #include <vector> #include <string> #include <fstream> #include <iomanip> #include <openssl/pem.h> #include <openssl/x509.h> #include <openssl/x509v3.h> #include <openssl/err.h> #include <openssl/bio.h> #include <openssl/pkcs12.h> #include <openssl/rand.h> #include <boost/bind.hpp> #include <boost/lexical_cast.hpp> #include <boost/algorithm/string.hpp> #include <boost/exception/diagnostic_information.hpp> #include <boost/exception/all.hpp> #include <boost/chrono/chrono.hpp> #include "scopeguard.h" #include "atloc.h" #include "securityutils.h" #include "defaultdirs.h" #include "createpaths.h" #include "errorexception.h" namespace securitylib { #define REPORT_ERROR(xStrToWrite) \ do { \ std::cerr << xStrToWrite; \ ERR_print_errors_fp(stderr); \ std::cerr << std::endl; \ } while (false) #define REPORT_VERBOSE(xReallyReport, xVerboseStrToWrite) \ if (xReallyReport) { \ std::cout << xVerboseStrToWrite << std::endl; \ } struct CertData { CertData(const bool prompt = false) : m_expiresInDays(0), m_prompt(prompt) {} void promptForCertData(char const* prompt, std::string& value) { std::cout << prompt << " [" << value << "]: "; std::cout.flush(); std::string tmp; std::getline(std::cin, tmp); if (!tmp.empty()) { value = tmp; } } void updateCertData() { if (m_country.empty()) { m_country = "US"; if (m_prompt) { promptForCertData("Country/Region Name (2 letter code)", m_country); } } if (m_stateOrProvince.empty()) { m_stateOrProvince = "Washington"; if (m_prompt) { promptForCertData("State or Province Name (full name)", m_stateOrProvince); } } if (m_locality.empty()) { m_locality = "Redmond"; if (m_prompt) { promptForCertData("Locality Name (eg, city)", m_locality); } } if (m_organization.empty()) { m_organization = "Microsoft"; if (m_prompt) { promptForCertData("Organization Name (eg, company)", m_organization); } } if (m_unit.empty()) { m_unit = "InMage"; if (m_prompt) { promptForCertData("Organizational Unit Name (eg, section)", m_unit); } } if (m_commonName.empty()) { m_commonName = "Scout"; if (m_prompt) { promptForCertData("Common Name (e.g. server FQDN or YOUR name)", m_commonName); } } if (m_expiresInDays == 0) { m_expiresInDays = 1095; if (m_prompt) { bool done = false; do { std::string tmp = "1095"; promptForCertData("Expires in Days", tmp); if (!tmp.empty()) { try { m_expiresInDays = boost::lexical_cast<long>(tmp); done = true; } catch (...) { std::cerr << "tmp is not valid days.\n"; } } else { done = true; m_expiresInDays = 1095; } } while (!done); } } } std::string m_country; std::string m_stateOrProvince; std::string m_locality; std::string m_organization; std::string m_unit; std::string m_commonName; std::string m_fqdn; long m_expiresInDays; std::string m_caCert; std::string m_caKey; bool m_prompt; }; class GenCert { public: GenCert(const std::string &name, const std::string &certName, const std::string &keyName, const std::string &pfxName, const std::string &fingerprintName, const std::string &pfxFriendlyName, const std::string &dhName, const CertData &certData, bool verbose, const bool generateDh = false, const bool importPfx = false, const std::string &extractPfx = std::string(""), const std::string &pfxKey = std::string(""), const bool overwrite = false) : m_name(name), m_extractPfx(extractPfx), m_pfxKey(pfxKey), m_certName(certName), m_keyName(keyName), m_pfxName(pfxName), m_fingerprintName(fingerprintName), m_pfxFriendlyName(pfxFriendlyName), m_dhName(dhName), m_certData(certData), m_verbose(verbose), m_importPfx(importPfx), m_generateDh(generateDh), m_overwrite(overwrite) { } GenCert(const std::string &name, const CertData &certData, bool verbose = false, const bool generateDh = false, const bool importPfx = false, const std::string &extractPfx = std::string(""), const std::string &pfxKey = std::string(""), const bool overwrite = false, const std::string &outDir = std::string("")) : m_name(name), m_certData(certData), m_verbose(verbose), m_generateDh(generateDh), m_importPfx(importPfx), m_extractPfx(extractPfx), m_pfxKey(pfxKey), m_overwrite(overwrite) { m_certName = getCertPath(outDir); m_keyName = (outDir.empty() ? securitylib::getPrivateDir() : outDir); m_keyName += securitylib::SECURITY_DIR_SEPARATOR; m_keyName += m_name; m_keyName += securitylib::EXTENSION_KEY; CreatePaths::createPathsAsNeeded(m_keyName); m_pfxName = (outDir.empty() ? securitylib::getPrivateDir() : outDir); m_pfxName += securitylib::SECURITY_DIR_SEPARATOR; m_pfxName += m_name; m_pfxName += securitylib::EXTENSION_KEYCERT_PFX; CreatePaths::createPathsAsNeeded(m_pfxName); m_fingerprintName = (outDir.empty() ? securitylib::getFingerprintDir() : outDir); m_fingerprintName += securitylib::SECURITY_DIR_SEPARATOR; m_fingerprintName += m_name; m_fingerprintName += securitylib::EXTENSION_FINGERPRINT; CreatePaths::createPathsAsNeeded(m_fingerprintName); m_dhName = (outDir.empty() ? securitylib::getPrivateDir() : outDir); m_dhName += securitylib::SECURITY_DIR_SEPARATOR; m_dhName += m_name; m_dhName += securitylib::EXTENSION_DH;; CreatePaths::createPathsAsNeeded(m_dhName); m_pfxFriendlyName = m_name; m_pfxFriendlyName += securitylib::EXTENSION_FRIENDLY_NAME; } std::string getCertPath(const std::string &outDir = std::string("")) { std::string certName = (outDir.empty() ? securitylib::getCertDir() : outDir); certName += securitylib::SECURITY_DIR_SEPARATOR; certName += m_name; certName += securitylib::EXTENSION_CRT; CreatePaths::createPathsAsNeeded(certName); return certName; } void backupIfNeeded(const std::string & name) { if (m_overwrite) { return; } try { if (!securitylib::backup(name)) { throw ERROR_EXCEPTION << "The process cannot access the file " << name << " because it is being used by another process." << '\n'; } } catch (boost::exception& e) { throw ERROR_EXCEPTION << "GenCert::backupIfNeeded: failed to backup file " << name << ". Error: " << boost::diagnostic_information(e) << '\n'; } } void writeKey(EVP_PKEY* pkey) { REPORT_VERBOSE(m_verbose, "Writing key to " << m_keyName); backupIfNeeded(m_keyName); BIO* bio = BIO_new_file(m_keyName.c_str(), "wb"); if (0 == bio) { throw ERROR_EXCEPTION << "GenCert::writeKey: Error opening " << m_keyName << " for writing" << '\n'; } ON_BLOCK_EXIT(boost::bind(&BIO_free, bio)); EVP_CIPHER* cipher = 0; char* passphrase = 0; #if 0 // TODO: if we want passphrase on key file need to enable this if (usePassphrase) { cipher = EVP_aes_256_cbc(); passphrase = ?; } #endif int rc = PEM_write_bio_PKCS8PrivateKey(bio, pkey, cipher, 0, 0, 0, 0); if (0 == rc) { throw ERROR_EXCEPTION << "GenCert::writeKey: Unable to write server key to " << m_keyName << '\n'; } } void writeCert(X509* cert) { REPORT_VERBOSE(m_verbose, "Writing certificate to " << m_certName); backupIfNeeded(m_certName); BIO* bio = BIO_new_file(m_certName.c_str(), "wb"); if (0 == bio) { throw ERROR_EXCEPTION << "GenCert::writeCert: Error opening " << m_certName << " for writing" << '\n'; } ON_BLOCK_EXIT(boost::bind(&BIO_free, bio)); int rc = PEM_write_bio_X509(bio, cert); if (0 == rc) { throw ERROR_EXCEPTION << "GenCert::writeCert: Error writing certificate to " << m_certName << '\n'; } } void appendCerts(STACK_OF(X509)* ca) { int caCount = sk_X509_num(ca); if (caCount > 0) { REPORT_VERBOSE(m_verbose, "Writing other certificate to " << m_certName); BIO* bio = BIO_new_file(m_certName.c_str(), "ab"); if (0 == bio) { throw ERROR_EXCEPTION << "GenCert::appendCerts: Error opening " << m_certName << " for writing" << '\n'; } ON_BLOCK_EXIT(boost::bind(&BIO_free, bio)); for (int i = 0; i < caCount; ++i) { if (0 != PEM_write_bio_X509(bio, sk_X509_value(ca, i))) { BIO_free(bio); throw ERROR_EXCEPTION << "GenCert::appendCerts: Error writing other certificates to " << m_certName << '\n'; } } } } void writePfx(EVP_PKEY* pkey, X509* cert) { REPORT_VERBOSE(m_verbose, "Writing certificate to " << m_pfxName); PKCS12* p12 = PKCS12_create((char*)PFX_PASSPHRASE, (char*)m_pfxFriendlyName.c_str(), pkey, cert, 0, 0, 0, 0, 0, 0); if (0 == p12) { throw ERROR_EXCEPTION << "GenCert::writePfx: Error creating PKCS12 structures" << '\n'; } backupIfNeeded(m_pfxName); BIO* bio = BIO_new_file(m_pfxName.c_str(), "wb"); if (0 == bio) { throw ERROR_EXCEPTION << "GenCert::writePfx: Error opening " << m_pfxName << " for writing" << '\n'; } ON_BLOCK_EXIT(boost::bind(&BIO_free, bio)); int rc = i2d_PKCS12_bio(bio, p12); PKCS12_free(p12); if (0 == rc) { throw ERROR_EXCEPTION << "GenCert::writePfx: Error writing to " << m_pfxName << '\n'; } } void writeFingerprint(X509* cert) { REPORT_VERBOSE(m_verbose, "Writing certificate fingerprint to " << m_fingerprintName); backupIfNeeded(m_fingerprintName); std::ofstream certFingerprint(m_fingerprintName.c_str()); certFingerprint << extractFingerprint(cert); if (!certFingerprint.good()) { throw ERROR_EXCEPTION << "GenCert::writeFingerprint: Error writing fingerprint to " << m_fingerprintName << '\n'; } } static std::string extractFingerprint(const X509* cert) { EVP_MD const* evpSha1 = EVP_sha1(); unsigned char md[EVP_MAX_MD_SIZE]; unsigned int len; X509_digest(cert, evpSha1, md, &len); std::stringstream certFingerprint; for (unsigned int i = 0; i < len; ++i) { certFingerprint << std::hex << std::setfill('0') << std::setw(2) << (int)md[i]; } return certFingerprint.str(); } void writeKeyAndCert(EVP_PKEY* pkey, X509* cert) { writeKey(pkey); writeCert(cert); writeFingerprint(cert); if (m_importPfx) { writePfx(pkey, cert); } } void generateDiffieHellman(std::string const& fileName) { REPORT_VERBOSE(m_verbose, "Generating Diffie-Hellman key pair this may take a while"); RAND_load_file(fileName.c_str(), -1); DH* dh = DH_new(); if (0 == dh) { throw ERROR_EXCEPTION << "GenCert::generateDiffieHellman: Error allocating DH structure" << '\n'; } ON_BLOCK_EXIT(DH_free, dh); int primeLen = 2048; int generator = 2; if (!DH_generate_parameters_ex(dh, primeLen, generator, 0)) { throw ERROR_EXCEPTION << "GenCert::generateDiffieHellman: Error generating Diffile-Hellman prameters" << '\n'; } backupIfNeeded(m_dhName); BIO* bio = BIO_new_file(m_dhName.c_str(), "wb"); if (0 == bio) { throw ERROR_EXCEPTION << "GenCert::generateDiffieHellman: Error opening " << m_dhName << '\n'; } ON_BLOCK_EXIT(boost::bind(&BIO_free, bio)); int rc = PEM_write_bio_DHparams(bio, dh); if (0 == rc) { BIO_free(bio); throw ERROR_EXCEPTION << "GenCert::generateDiffieHellman: Error writing Diffie-Hellman keys to " << m_dhName << '\n'; } } static EVP_PKEY* generateKey(int bits) { REPORT_VERBOSE(true, "Generating RSA key"); EVP_PKEY* pkey = EVP_PKEY_new(); if (!pkey) { throw ERROR_EXCEPTION << "GenCert::generateKey: Error creating EVP_PKEY" << '\n'; } RSA * rsa = RSA_generate_key(bits, RSA_F4, 0, 0); if (!EVP_PKEY_assign_RSA(pkey, rsa)) { EVP_PKEY_free(pkey); throw ERROR_EXCEPTION << "GenCert::generateKey: Error generating " << bits << "-bit RSA key" << '\n'; } return pkey; } void addSubjectName(X509_NAME* subject, char const* lookup, std::string const& value) { int nid = OBJ_txt2nid(lookup); if (NID_undef == nid) { throw ERROR_EXCEPTION << "GenCert::addSubjectName: Error finding NID for " << lookup << '\n'; } X509_NAME_ENTRY* entry = X509_NAME_ENTRY_create_by_NID(NULL, nid, MBSTRING_ASC, (unsigned char*)value.c_str(), -1); if (0 == entry) { throw ERROR_EXCEPTION << "GenCert::addSubjectName: Error creating Name entry from NID" << '\n'; } if (1 != X509_NAME_add_entry(subject, entry, -1, 0)) { throw ERROR_EXCEPTION << "GenCert::addSubjectName: Error adding entry to Name" << '\n'; } } static EVP_PKEY* readCaPrivateKey(std::string const& caKeyName) { EVP_PKEY* key = 0; REPORT_VERBOSE(true, "Reading CA key from " << caKeyName); BIO* bio = BIO_new_file(caKeyName.c_str(), "rb"); if (0 == bio) { throw ERROR_EXCEPTION << "GenCert::readCaPrivateKey: Error opening " << caKeyName << '\n'; } ON_BLOCK_EXIT(boost::bind(&BIO_free, bio)); key = PEM_read_bio_PrivateKey(bio, 0, 0, 0); if (0 == key) { throw ERROR_EXCEPTION << "GenCert::readCaPrivateKey: Error reading " << caKeyName << '\n'; } return key; } static X509* readCert(std::string const& certName) { X509* cert = 0; REPORT_VERBOSE(true, "Reading Certificate from " << certName); BIO* bio = BIO_new_file(certName.c_str(), "rb"); if (0 == bio) { throw ERROR_EXCEPTION << "GenCert::readCert: Error opening " << certName << '\n'; } ON_BLOCK_EXIT(boost::bind(&BIO_free, bio)); cert = PEM_read_bio_X509(bio, 0, 0, 0); if (0 == cert) { throw ERROR_EXCEPTION << "GenCert::readCert: Error reading " << certName << '\n'; } return cert; } static X509* readCaCert(std::string const& caCertName) { REPORT_VERBOSE(true, "Reading CA Certificate from " << caCertName); return readCert(caCertName); } static bool isCertLive(const X509 * cert) { return (X509_cmp_time(X509_get_notAfter(cert), 0) > 0); } static bool isCertLive(const X509 * cert, const boost::chrono::system_clock::time_point& expTime) { time_t eTime = boost::chrono::system_clock::to_time_t(expTime); return (X509_cmp_time(X509_get_notAfter(cert), &eTime) > 0); } static bool isCertLive(const std::string& certName, int days) { X509 *clientCert = NULL; std::string clientCertPath = getCertDir(); clientCertPath += SECURITY_DIR_SEPARATOR; clientCertPath += certName; clientCertPath += EXTENSION_CRT; clientCert = readCert(clientCertPath); boost::chrono::system_clock::time_point expTime = boost::chrono::system_clock::now() + boost::chrono::hours(days * 24); return isCertLive(clientCert, expTime); } X509_REQ* generateX509Req(EVP_PKEY* pkey) { X509_REQ* req = X509_REQ_new(); if (0 == req) { throw ERROR_EXCEPTION << "GenCert::generateX509Req: Error creating X509_REQ" << '\n'; } SCOPE_GUARD reqGuard = MAKE_SCOPE_GUARD(boost::bind(&X509_REQ_free, req)); X509_REQ_set_pubkey(req, pkey); X509_NAME* subject = X509_NAME_new(); if (0 == subject) { throw ERROR_EXCEPTION << "GenCert::generateX509Req: Error create X509_NAME" << '\n'; } if (m_certData.m_country.empty()) { addSubjectName(subject, "countryName", m_certData.m_country); } if (!m_certData.m_stateOrProvince.empty()) { addSubjectName(subject, "stateOrProvinceName", m_certData.m_stateOrProvince); } if (!m_certData.m_locality.empty()) { addSubjectName(subject, "localityName", m_certData.m_locality); } if (!m_certData.m_organization.empty()) { addSubjectName(subject, "organizationName", m_certData.m_organization); } if (!m_certData.m_unit.empty()) { addSubjectName(subject, "organizationalUnitName", m_certData.m_unit); } if (!m_certData.m_commonName.empty()) { addSubjectName(subject, "commonName", m_certData.m_commonName); } if (1 != X509_REQ_set_subject_name(req, subject)) { throw ERROR_EXCEPTION << "GenCert::generateX509Req: Error adding subject to request" << '\n'; } if (!m_certData.m_fqdn.empty()) { STACK_OF(X509_EXTENSION)* extensionList; extensionList = sk_X509_EXTENSION_new_null(); X509_EXTENSION* extension = X509V3_EXT_conf(NULL, NULL, "subjectAltName", (char*)m_certData.m_fqdn.c_str()); if (0 == extension) { throw ERROR_EXCEPTION << "GenCert::generateX509Req: Error creating X509_EXTENSION" << '\n'; } sk_X509_EXTENSION_push(extensionList, extension); int rc = X509_REQ_add_extensions(req, extensionList); sk_X509_EXTENSION_pop_free(extensionList, X509_EXTENSION_free); if (0 == rc) { throw ERROR_EXCEPTION << "GenCert::generateX509Req: Error adding subjectAltName to request" << '\n'; } } if (!X509_REQ_sign(req, pkey, EVP_sha256())) { throw ERROR_EXCEPTION << "GenCert::generateX509Req: Error signing request" << '\n'; } reqGuard.dismiss(); return req; } void addExtensions(X509* caCert, X509* cert) { X509V3_CTX ctx; X509V3_set_ctx(&ctx, caCert, cert, NULL, NULL, 0); for (int i = 0; 0 != extensions[i].m_key; ++i) { X509_EXTENSION* extension = X509V3_EXT_conf(NULL, &ctx, extensions[i].m_key, extensions[i].m_val); ON_BLOCK_EXIT(X509_EXTENSION_free, extension); if (0 == extension) { throw ERROR_EXCEPTION << "GenCert::addExtensions: Error creating extensions" << '\n'; } if (!X509_add_ext(cert, extension, -1)) { throw ERROR_EXCEPTION << "GenCert::addExtensions: Error adding extensions to certificate" << '\n'; } } } X509* caGenerateAndSignX509Certificate(EVP_PKEY* caKey, X509* caCert, X509_REQ* csr) { REPORT_VERBOSE(m_verbose, "Generating x509 certificate"); X509 * cert = X509_new(); if (!cert) { throw ERROR_EXCEPTION << "GenCert::caGenerateAndSignX509Certificate: Error creating X509" << '\n'; } SCOPE_GUARD certGuard = MAKE_SCOPE_GUARD(boost::bind(&X509_free, cert)); if (!X509_set_version(cert, 2)) { throw ERROR_EXCEPTION << "GenCert::caGenerateAndSignX509Certificate: Error setting verson in certificate" << '\n'; } ASN1_INTEGER_set(X509_get_serialNumber(cert), 1); if (0 == X509_set_subject_name(cert, X509_REQ_get_subject_name(csr))) { throw ERROR_EXCEPTION << "GenCert::caGenerateAndSignX509Certificate: Error setting request subject name" << '\n'; } if (0 == X509_set_issuer_name(cert, X509_get_subject_name(caCert))) { throw ERROR_EXCEPTION << "GenCert::caGenerateAndSignX509Certificate: Error setting issuer name" << '\n'; } EVP_PKEY* pkey = X509_REQ_get_pubkey(csr); X509_set_pubkey(cert, pkey); EVP_PKEY_free(pkey); X509_gmtime_adj(X509_get_notBefore(cert), -(60*60*24)); // 1 day earlier X509_gmtime_adj(X509_get_notAfter(cert), m_certData.m_expiresInDays * 60 * 60 * 24); addExtensions(caCert, cert); STACK_OF(X509_EXTENSION)* csrExtensions = X509_REQ_get_extensions(csr); if (0 != csrExtensions) { int idx = X509v3_get_ext_by_NID (csrExtensions, OBJ_sn2nid ("subjectAltName"), -1); X509_EXTENSION* subjectAltName = X509v3_get_ext(csrExtensions, idx); if (!X509_add_ext(cert, subjectAltName, -1)) { throw ERROR_EXCEPTION << "GenCert::caGenerateAndSignX509Certificate: Error setting subjectAltName" << '\n'; } } if (!X509_sign(cert, caKey, EVP_sha256())) { throw ERROR_EXCEPTION << "GenCert::caGenerateAndSignX509Certificate: Error signing certificate" << '\n'; } certGuard.dismiss(); return cert; } void caSignReq() { EVP_PKEY * pkey = generateKey(2048); if (!pkey) { throw ERROR_EXCEPTION << "GenCert::caSignReq: generateKey failed" << '\n';; } ON_BLOCK_EXIT(boost::bind(&EVP_PKEY_free, pkey)); X509_REQ* csr = generateX509Req(pkey); if (0 == csr) { throw ERROR_EXCEPTION << "GenCert::caSignReq: generateX509Req failed" << '\n';; } ON_BLOCK_EXIT(boost::bind(&X509_REQ_free, csr)); EVP_PKEY* pubKey = X509_REQ_get_pubkey(csr); if (0 == pubKey) { throw ERROR_EXCEPTION << "GenCert::caSignReq: X509_REQ_get_pubkey failed" << '\n';; } ON_BLOCK_EXIT(boost::bind(&EVP_PKEY_free, pubKey)); if (1 != X509_REQ_verify(csr, pubKey)) { throw ERROR_EXCEPTION << "GenCert::caSignReq: Error verifying signature on certificate" << '\n'; } EVP_PKEY* caKey = readCaPrivateKey(m_certData.m_caKey); if (0 == caKey) { throw ERROR_EXCEPTION << "GenCert::caSignReq: readCaPrivateKey failed" << '\n';; } ON_BLOCK_EXIT(boost::bind(&EVP_PKEY_free, caKey)); X509* caCert = readCaCert(m_certData.m_caCert); if (0 == caCert) { throw ERROR_EXCEPTION << "GenCert::caSignReq: readCaCert failed" << '\n';; } ON_BLOCK_EXIT(boost::bind(&X509_free, caCert)); X509* cert = caGenerateAndSignX509Certificate(caKey, caCert, csr); if (!cert) { throw ERROR_EXCEPTION << "GenCert::caSignReq: caGenerateAndSignX509Certificate failed" << '\n';; } writeKeyAndCert(pkey, cert); X509_free(cert); } X509* generateAndSignX509Certificate(EVP_PKEY * pkey) { REPORT_VERBOSE(m_verbose, "Generating x509 certificate"); X509 * cert = X509_new(); if (!cert) { throw ERROR_EXCEPTION << "GenCert::generateAndSignX509Certificate: Error creating X509" << '\n'; } ASN1_INTEGER_set(X509_get_serialNumber(cert), 1); X509_gmtime_adj(X509_get_notBefore(cert), -(60*60*24)); // 1 day earlier X509_gmtime_adj(X509_get_notAfter(cert), m_certData.m_expiresInDays * 60 * 60 * 24); X509_set_pubkey(cert, pkey); X509_NAME* name = X509_get_subject_name(cert); if (!m_certData.m_country.empty()) { X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char *)m_certData.m_country.c_str(), -1, -1, 0); } if (!m_certData.m_stateOrProvince.empty()) { X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_ASC, (unsigned char *)m_certData.m_stateOrProvince.c_str(), -1, -1, 0); } if (!m_certData.m_locality.empty()) { X509_NAME_add_entry_by_txt(name, "L", MBSTRING_ASC, (unsigned char *)m_certData.m_locality.c_str(), -1, -1, 0); } if (!m_certData.m_organization.empty()) { X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char *)m_certData.m_organization.c_str(), -1, -1, 0); } if (!m_certData.m_unit.empty()) { X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC, (unsigned char *)m_certData.m_unit.c_str(), -1, -1, 0); } if (!m_certData.m_commonName.empty()) { X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)m_certData.m_commonName.c_str(), -1, -1, 0); } X509_set_issuer_name(cert, name); X509V3_CTX ctx; X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0); X509_EXTENSION* extension = X509V3_EXT_conf(NULL, &ctx, "basicConstraints", "CA:TRUE"); ON_BLOCK_EXIT(X509_EXTENSION_free, extension); if (0 == extension) { REPORT_ERROR("Error creating extensions "); throw ERROR_EXCEPTION << "GenCert::generateAndSignX509Certificate: Error creating extensions" << '\n'; } if (!X509_add_ext(cert, extension, -1)) { throw ERROR_EXCEPTION << "GenCert::generateAndSignX509Certificate: Error adding extensions to certificate" << '\n'; } if (!X509_sign(cert, pkey, EVP_sha256())) { X509_free(cert); throw ERROR_EXCEPTION << "GenCert::generateAndSignX509Certificate: Error signing certificate" << '\n'; } return cert; } void selfSignReq() { EVP_PKEY* pkey = generateKey(2048); ON_BLOCK_EXIT(boost::bind(&EVP_PKEY_free, pkey)); X509* cert = generateAndSignX509Certificate(pkey); writeKeyAndCert(pkey, cert); X509_free(cert); } void freeX509Stack(STACK_OF(X509)* ca) { sk_X509_pop_free(ca, X509_free); } std::string getPfxPrivateKey(std::string const& pfxFile, std::string const& passphrase = std::string()) { BIO* bio = BIO_new_file(pfxFile.c_str(), "rb"); if (0 == bio) { throw ERROR_EXCEPTION << "GenCert::generateAndSignX509Certificate: Error opening " << m_pfxName << " for writing" << '\n'; } ON_BLOCK_EXIT(boost::bind(&BIO_free, bio)); PKCS12* p12 = d2i_PKCS12_bio(bio, 0); if (0 == p12) { throw ERROR_EXCEPTION << "GenCert::generateAndSignX509Certificate: Error reading pfx file: " << pfxFile << '\n'; } ON_BLOCK_EXIT(boost::bind(&PKCS12_free, p12)); EVP_PKEY* evpKey; X509* cert; STACK_OF(X509)* ca = 0; char const* pass; if (PKCS12_verify_mac(p12, "", 0) || PKCS12_verify_mac(p12, NULL, 0)) { pass = ""; // no passphrase required so make sure not to use the default one from gencert } else { pass = passphrase.c_str(); } if (!PKCS12_parse(p12, pass, &evpKey, &cert, &ca)) { throw ERROR_EXCEPTION << "GenCert::generateAndSignX509Certificate: Error parsing pfx file: " << pfxFile << '\n'; } ON_BLOCK_EXIT(&X509_free, cert); ON_BLOCK_EXIT(&EVP_PKEY_free, evpKey); ON_BLOCK_EXIT(boost::bind(&GenCert::freeX509Stack, this, ca)); BIO* memBio = BIO_new(BIO_s_mem()); if (0 == memBio) { throw ERROR_EXCEPTION << "GenCert::generateAndSignX509Certificate: Error allocting buffer" << '\n'; } ON_BLOCK_EXIT(&BIO_free, memBio); BUF_MEM* memBuf; BIO_get_mem_ptr(memBio, &memBuf); if (evpKey) { if (!PEM_write_bio_PrivateKey(memBio, evpKey, NULL, NULL, 0, NULL, NULL)) { throw ERROR_EXCEPTION << "GenCert::generateAndSignX509Certificate: Error writing key" << '\n'; } } if (cert) { // Certificate PEM_write_bio_X509(memBio, cert); } if (ca && sk_X509_num(ca)) { // Other Certificates for (int i = 0; i < sk_X509_num(ca); i++) PEM_write_bio_X509_AUX(memBio, sk_X509_value(ca, i)); } std::string certificate((memBuf->data ? memBuf->data : ""), (memBuf->data ? memBuf->length : 0)); return certificate; } void extractPfxPrivateKey(std::string const& pfxFile, std::string const& passphrase = std::string()) { std::string CertAndKey = getPfxPrivateKey(pfxFile, passphrase); std::cout << CertAndKey << '\n'; } std::string getPfxName() const { return m_pfxName; } protected: private: std::string m_name; std::string m_extractPfx; std::string m_pfxKey; std::string m_certName; std::string m_keyName; std::string m_pfxName; std::string m_fingerprintName; std::string m_pfxFriendlyName; std::string m_dhName; const CertData & m_certData; bool m_verbose; bool m_importPfx; bool m_generateDh; bool m_overwrite; }; } // namespace securitylib #include "importpfxcert.h" #endif // GENCERT_H