// Author: L V K Subhash Rayudu Battina // Date: 2025-04-03 // Description: Converts a modern pkcs12 cert to a legacy format and writes it to a file // Pre-requisites: Ensure you system's openssl version is 3.0 or above // Steps to compile: g++ -o pkcs12_converter pkcs12_modern_to_legacy_converter.cpp -lcrypto -L/opt/homebrew/opt/openssl@3/lib -I/opt/homebrew/opt/openssl@3/include // Steps to execute: ./pkcs12_converter #include #include #include #include #include #include #include #include #include // Static global variables for OpenSSL providers static OSSL_PROVIDER* legacyProvider = nullptr; static OSSL_PROVIDER* defaultProvider = nullptr; // Cleanup function to unload OpenSSL providers static void cleanupOpenSSL() { if (legacyProvider) OSSL_PROVIDER_unload(legacyProvider); if (defaultProvider) OSSL_PROVIDER_unload(defaultProvider); } // Function to initialize OpenSSL and load providers void initializeOpenSSL() { std::cout << "[INFO] Initializing OpenSSL..." << std::endl; // Check OpenSSL version compatibility if (OpenSSL_version_num() < 0x30000000L) { throw std::runtime_error("OpenSSL 3.0 or later is required to load the legacy provider"); } // Load the legacy provider std::cout << "[INFO] Loading legacy provider..." << std::endl; legacyProvider = OSSL_PROVIDER_load(NULL, "legacy"); if (!legacyProvider) { std::cerr << "[ERROR] Failed to load legacy provider. Ensure OpenSSL is configured correctly." << std::endl; throw std::runtime_error("Failed to load legacy provider"); } // Load the default provider std::cout << "[INFO] Loading default provider..." << std::endl; defaultProvider = OSSL_PROVIDER_load(NULL, "default"); if (!defaultProvider) { std::cerr << "[ERROR] Failed to load default provider. Ensure OpenSSL is configured correctly." << std::endl; throw std::runtime_error("Failed to load default provider"); } // Unload providers at program exit to prevent resource leaks atexit(cleanupOpenSSL); std::cout << "[INFO] OpenSSL initialized with legacy and default providers loaded." << std::endl; } // Function to read a file into a vector of bytes std::vector readFile(const std::string& filePath) { std::cout << "[INFO] Reading file: " << filePath << std::endl; std::ifstream file(filePath, std::ios::binary); if (!file) { throw std::runtime_error("Failed to open file: " + filePath); } std::vector data((std::istreambuf_iterator(file)), std::istreambuf_iterator()); std::cout << "[INFO] Successfully read " << data.size() << " bytes from file: " << filePath << std::endl; return data; } // Function to write a vector of bytes to a file void writeFile(const std::string& filePath, const std::vector& data) { std::cout << "[INFO] Writing to file: " << filePath << std::endl; std::ofstream file(filePath, std::ios::binary); if (!file) { throw std::runtime_error("Failed to write to file: " + filePath); } file.write(reinterpret_cast(data.data()), data.size()); std::cout << "[INFO] Successfully wrote " << data.size() << " bytes to file: " << filePath << std::endl; } // Function to validate a PKCS#12 file void validatePKCS12(const std::vector& pkcs12Data, const std::string& passphrase) { std::cout << "[INFO] Validating PKCS#12 file with passphrase: " << passphrase << std::endl; BIO* bio = BIO_new_mem_buf(pkcs12Data.data(), pkcs12Data.size()); if (!bio) { throw std::runtime_error("Failed to create BIO for PKCS#12 data"); } PKCS12* pkcs12 = d2i_PKCS12_bio(bio, nullptr); BIO_free(bio); if (!pkcs12) { throw std::runtime_error("Failed to parse PKCS#12 data"); } EVP_PKEY* privateKey = nullptr; X509* certificate = nullptr; STACK_OF(X509)* caChain = nullptr; if (!PKCS12_parse(pkcs12, passphrase.c_str(), &privateKey, &certificate, &caChain)) { PKCS12_free(pkcs12); throw std::runtime_error("Failed to validate PKCS#12 with the provided passphrase"); } std::cout << "[INFO] PKCS#12 file validated successfully." << std::endl; PKCS12_free(pkcs12); EVP_PKEY_free(privateKey); X509_free(certificate); sk_X509_pop_free(caChain, X509_free); } // Function to create a new PKCS#12 file with specific encryption algorithms std::vector createPKCS12fromPKCS12(const std::vector& inputData, const std::string& passphrase) { std::cout << "[INFO] Creating new PKCS#12 file from input data..." << std::endl; // Parse the original PKCS#12 file std::cout << "[INFO] Parsing original PKCS#12 file..." << std::endl; BIO* bp = BIO_new_mem_buf(inputData.data(), (int)inputData.size()); if (!bp) { throw std::runtime_error("Failed to create BIO for input data"); } PKCS12* originalPKCS12 = NULL; d2i_PKCS12_bio(bp, &originalPKCS12); BIO_free(bp); if (!originalPKCS12) { throw std::runtime_error("Failed to parse PKCS#12 data"); } EVP_PKEY* privateKey = nullptr; X509* x509 = nullptr; STACK_OF(X509)* caChain = NULL; if (!PKCS12_parse(originalPKCS12, passphrase.c_str(), &privateKey, &x509, &caChain)) { PKCS12_free(originalPKCS12); throw std::runtime_error("Failed to parse PKCS#12 with the original passphrase"); } PKCS12_free(originalPKCS12); std::cout << "[INFO] Successfully parsed original PKCS#12 file." << std::endl; // Create a new PKCS#12 structure with the specified encryption algorithms std::cout << "[INFO] Creating new PKCS#12 structure..." << std::endl; int nid_key = NID_pbe_WithSHA1And3_Key_TripleDES_CBC; int nid_cert = NID_pbe_WithSHA1And40BitRC2_CBC; // Use SHA-1 as the MAC algorithm PKCS12* newPKCS12 = PKCS12_create_ex( passphrase.c_str(), "SampleCert", privateKey, x509, caChain, NID_pbe_WithSHA1And3_Key_TripleDES_CBC, NID_pbe_WithSHA1And40BitRC2_CBC, PKCS12_DEFAULT_ITER, -1, 0, nullptr, nullptr ); char* outPKCS12 = nullptr; int outPKCS12Len = 0; if (newPKCS12) { const char* pswd = passphrase.c_str(); if (PKCS12_set_mac(newPKCS12, pswd, strlen(pswd), nullptr, 0, 1, EVP_sha1()) == 1) { int nP12Len = 0; BIO* outBioP12 = BIO_new(BIO_s_mem()); i2d_PKCS12_bio(outBioP12, newPKCS12); nP12Len = BIO_pending(outBioP12); outPKCS12 = static_cast(calloc(1, nP12Len + 1)); if (outPKCS12) { BIO_read(outBioP12, reinterpret_cast(outPKCS12), nP12Len); outPKCS12Len = nP12Len; } else { std::cerr << "[ERROR] Memory allocation failed for outPKCS12." << std::endl; } BIO_free(outBioP12); } else { std::cerr << "[ERROR] Failed to add P12 MAC." << std::endl; } } else { // EVP_PKEY_free(privateKey); // X509_free(x509); // sk_X509_pop_free(caChain, X509_free); std::cerr << "[ERROR] Failed to create P12." << std::endl; throw std::runtime_error("Failed to create new PKCS#12 structure"); } // Serialize the new PKCS#12 structure to memory std::cout << "[INFO] Serializing new PKCS#12 structure..." << std::endl; BIO* outputBio = BIO_new(BIO_s_mem()); if (!i2d_PKCS12_bio(outputBio, newPKCS12)) { PKCS12_free(newPKCS12); BIO_free(outputBio); EVP_PKEY_free(privateKey); X509_free(x509); sk_X509_pop_free(caChain, X509_free); throw std::runtime_error("Failed to serialize new PKCS#12 structure"); } PKCS12_free(newPKCS12); char* outputData = nullptr; long outputLength = BIO_get_mem_data(outputBio, &outputData); std::vector result(outputData, outputData + outputLength); BIO_free(outputBio); std::cout << "[INFO] Successfully created new PKCS#12 file." << std::endl; // Free resources EVP_PKEY_free(privateKey); X509_free(x509); sk_X509_pop_free(caChain, X509_free); return result; } int GeneratePKCS12(char* passPhrase, char* friendlyName, EVP_PKEY* privateKey, X509* cert, STACK_OF(X509)* caCerts, char** outPKCS12, int* outPKCS12Len) { // Load the legacy provider OSSL_PROVIDER* legacy = OSSL_PROVIDER_load(nullptr, "legacy"); OSSL_PROVIDER* defaultProvider = OSSL_PROVIDER_load(nullptr, "default"); if (legacy != nullptr) { PKCS12* p12OutCert = PKCS12_create_ex( passPhrase, friendlyName, privateKey, cert, caCerts, NID_pbe_WithSHA1And3_Key_TripleDES_CBC, NID_pbe_WithSHA1And40BitRC2_CBC, PKCS12_DEFAULT_ITER, -1, 0, nullptr, nullptr ); if (p12OutCert) { const char* pswd = passPhrase; if (PKCS12_set_mac(p12OutCert, pswd, strlen(pswd), nullptr, 0, 1, EVP_sha1()) == 1) { int nP12Len = 0; BIO* outBioP12 = BIO_new(BIO_s_mem()); i2d_PKCS12_bio(outBioP12, p12OutCert); nP12Len = BIO_pending(outBioP12); *outPKCS12 = static_cast(calloc(1, nP12Len + 1)); if (*outPKCS12) { BIO_read(outBioP12, *outPKCS12, nP12Len); *outPKCS12Len = nP12Len; } else { std::cerr << "[ERROR] Memory allocation failed for outPKCS12." << std::endl; } BIO_free(outBioP12); } else { std::cerr << "[ERROR] Failed to add P12 MAC." << std::endl; } PKCS12_free(p12OutCert); } else { std::cerr << "[ERROR] Failed to create P12." << std::endl; } OSSL_PROVIDER_unload(legacy); OSSL_PROVIDER_unload(defaultProvider); } else { char buf[256]; unsigned long err = ERR_get_error(); ERR_error_string_n(err, buf, sizeof(buf)); std::cerr << "[ERROR] Failed to load provider - " << buf << std::endl; } return 0; } int main(int argc, char* argv[]) { if (argc != 4) { std::cerr << "[ERROR] Usage: " << argv[0] << " " << std::endl; return 1; } const std::string inputFilePath = argv[1]; const std::string outputFilePath = argv[2]; const std::string passphrase = argv[3]; try { std::cout << "[INFO] Starting PKCS#12 processing..." << std::endl; initializeOpenSSL(); // Read the input PKCS#12 file std::vector inputData = readFile(inputFilePath); // Validate the original PKCS#12 file validatePKCS12(inputData, passphrase); // Create a new PKCS#12 file with the new passphrase std::vector outputData = createPKCS12fromPKCS12(inputData, passphrase); // Validate the modified PKCS#12 file validatePKCS12(outputData, passphrase); // Write the modified PKCS#12 file to disk writeFile(outputFilePath, outputData); std::cout << "[INFO] Successfully created new PKCS#12 file: " << outputFilePath << std::endl; } catch (const std::exception& ex) { std::cerr << "[ERROR] " << ex.what() << std::endl; return 1; } return 0; }