From 561caed232eb4957cfb02437f335a6cc612c9c1f Mon Sep 17 00:00:00 2001 From: neru Date: Fri, 20 Mar 2026 12:38:42 -0300 Subject: [PATCH] feat: add cert generation --- src/unlocker/cert_manager.cpp | 185 ++++++++++++++++++++++++++++++++++ src/unlocker/cert_manager.h | 25 +++++ 2 files changed, 210 insertions(+) create mode 100644 src/unlocker/cert_manager.cpp create mode 100644 src/unlocker/cert_manager.h diff --git a/src/unlocker/cert_manager.cpp b/src/unlocker/cert_manager.cpp new file mode 100644 index 0000000..9a801ef --- /dev/null +++ b/src/unlocker/cert_manager.cpp @@ -0,0 +1,185 @@ +#include "cert_manager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +std::string randomizeString(size_t length) +{ + const char charset[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + std::string result; + result.resize(length); + for (size_t i = 0; i < length; ++i) + result[i] = charset[rand() % (sizeof(charset) - 1)]; + return result; +} + +CertManager::CertManager() {} + +CertManager::~CertManager() +{ + if (_caPkey) EVP_PKEY_free(_caPkey); + if (_caCert) X509_free(_caCert); + + for (auto& pair : _hostContexts) + SSL_CTX_free(pair.second); +} + +bool CertManager::Init() +{ + if (LoadCA()) + { + Log::verbose("Loaded existing CA certificate."); + return true; + } + + Log::verbose("No CA found. Generating new CA certificate."); + return GenerateCA(); +} + +bool CertManager::LoadCA() +{ + BIO* keyBio = BIO_new_file("ca_key.pem", "r"); + if (!keyBio) return false; + + _caPkey = PEM_read_bio_PrivateKey(keyBio, nullptr, nullptr, nullptr); + BIO_free(keyBio); + + if (!_caPkey) return false; + + BIO* certBio = BIO_new_file("ca_cert.pem", "r"); + if (!certBio) return false; + + _caCert = PEM_read_bio_X509(certBio, nullptr, nullptr, nullptr); + BIO_free(certBio); + + if (!_caCert) return false; + + return true; +} + +bool CertManager::GenerateCA() +{ + srand(static_cast(time(nullptr))); + + EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr); + if (!pctx) return false; + EVP_PKEY_keygen_init(pctx); + EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, 2048); + EVP_PKEY_keygen(pctx, &_caPkey); + EVP_PKEY_CTX_free(pctx); + + _caCert = X509_new(); + X509_set_version(_caCert, 2); + ASN1_INTEGER_set(X509_get_serialNumber(_caCert), 1); + X509_gmtime_adj(X509_get_notBefore(_caCert), 0); + X509_gmtime_adj(X509_get_notAfter(_caCert), 31536000L); // 1 year + + std::string org = randomizeString(16); + std::string cn = randomizeString(16); + + X509_NAME* name = X509_get_subject_name(_caCert); + X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char*)"US", -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char*)org.c_str(), -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char*)cn.c_str(), -1, -1, 0); + X509_set_issuer_name(_caCert, name); + X509_set_pubkey(_caCert, _caPkey); + + X509V3_CTX ctxV3; + X509V3_set_ctx_nodb(&ctxV3); + X509V3_set_ctx(&ctxV3, _caCert, _caCert, nullptr, nullptr, 0); + X509_EXTENSION* extCA = X509V3_EXT_conf_nid(nullptr, &ctxV3, NID_basic_constraints, "critical,CA:TRUE"); + if (extCA) + { + X509_add_ext(_caCert, extCA, -1); + X509_EXTENSION_free(extCA); + } + + X509_sign(_caCert, _caPkey, EVP_sha256()); + + BIO* keyBioOut = BIO_new_file("ca_key.pem", "w"); + if (keyBioOut) + { + PEM_write_bio_PrivateKey(keyBioOut, _caPkey, nullptr, nullptr, 0, nullptr, nullptr); + BIO_free(keyBioOut); + } + + BIO* certBioOut = BIO_new_file("ca_cert.pem", "w"); + if (certBioOut) + { + PEM_write_bio_X509(certBioOut, _caCert); + BIO_free(certBioOut); + } + + Log::info("Generated new CA key and certificate files. Installing to Windows Root CA store automatically..."); + + system("certutil.exe -user -addstore root ca_cert.pem"); + + return true; +} + +SSL_CTX* CertManager::CreateHostContext(const std::string& host) +{ + std::lock_guard lock(_mutex); + + auto it = _hostContexts.find(host); + if (it != _hostContexts.end()) + { + return it->second; + } + + Log::verbose("Generating dynamic certificate for {}", host); + + EVP_PKEY* pkey = nullptr; + EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr); + EVP_PKEY_keygen_init(pctx); + EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, 2048); + EVP_PKEY_keygen(pctx, &pkey); + EVP_PKEY_CTX_free(pctx); + + X509* cert = X509_new(); + X509_set_version(cert, 2); + ASN1_INTEGER_set(X509_get_serialNumber(cert), static_cast(std::hash{}(host) & 0x7FFFFFFF)); + X509_gmtime_adj(X509_get_notBefore(cert), 0); + X509_gmtime_adj(X509_get_notAfter(cert), 31536000L); + + std::string dynamicOrg = randomizeString(16); + + X509_NAME* name = X509_get_subject_name(cert); + X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char*)"US", -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char*)dynamicOrg.c_str(), -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char*)(host.c_str()), -1, -1, 0); + X509_set_issuer_name(cert, X509_get_subject_name(_caCert)); + X509_set_pubkey(cert, pkey); + + X509V3_CTX ctxV3; + X509V3_set_ctx_nodb(&ctxV3); + X509V3_set_ctx(&ctxV3, _caCert, cert, nullptr, nullptr, 0); + std::string san = "DNS:" + host; + X509_EXTENSION* extSAN = X509V3_EXT_conf_nid(nullptr, &ctxV3, NID_subject_alt_name, san.c_str()); + if (extSAN) + { + X509_add_ext(cert, extSAN, -1); + X509_EXTENSION_free(extSAN); + } + + X509_sign(cert, _caPkey, EVP_sha256()); + + SSL_CTX* ctx = SSL_CTX_new(TLS_server_method()); + SSL_CTX_use_certificate(ctx, cert); + SSL_CTX_use_PrivateKey(ctx, pkey); + + X509_free(cert); + EVP_PKEY_free(pkey); + + _hostContexts[host] = ctx; + return ctx; +} diff --git a/src/unlocker/cert_manager.h b/src/unlocker/cert_manager.h new file mode 100644 index 0000000..da1f8a3 --- /dev/null +++ b/src/unlocker/cert_manager.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include +#include + +class CertManager { +public: + CertManager(); + ~CertManager(); + + bool Init(); + SSL_CTX* CreateHostContext(const std::string& host); + +private: + bool GenerateCA(); + bool LoadCA(); + + EVP_PKEY* _caPkey = nullptr; + X509* _caCert = nullptr; + + std::mutex _mutex; + std::unordered_map _hostContexts; +};