// 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;
}