openssl merges certificate (public key) and private key into pfx format file (C language version)

With base64 certificate and private key data converted to PFX

1. Load the certificate and private key
First the certificate and private key need to be loaded into memory. You can use the functions PEM_read_bio_X509() and PEM_read_bio_PrivateKey() to read the certificate and private key data respectively, and store them in the X509 and EVP_PKEY structures.

X509 *cert = NULL;
EVP_PKEY *pkey = NULL;

BIO *bio_cert = BIO_new_mem_buf(cert_data, cert_data_len);
cert = PEM_read_bio_X509(bio_cert, NULL, NULL, NULL);
BIO_free(bio_cert);

BIO *bio_key = BIO_new_mem_buf(key_data, key_data_len);
pkey = PEM_read_bio_PrivateKey(bio_key, NULL, NULL, NULL);
BIO_free(bio_key);

Where cert_data and key_data are the BASE64 encoded strings of the certificate and private key respectively, and cert_data_len and key_data_len are the lengths of the strings respectively.

2. Create a PKCS#12 object
Use the function PKCS12_create() to create a PKCS#12 object and bind the certificate and private key to it.

PKCS12 *p12 = PKCS12_create(
    password, password_len,
    alias, pkey, cert, NULL, 0, 0, 0, 0
);

Where password and password_len are the passwords used to encrypt the PKCS#12 file, alias is the key identifier, pkey and cert are the private key and certificate structures, respectively.

3. Save the PKCS#12 object
Finally, use the function i2d_PKCS12_bio() to serialize the PKCS#12 object into binary data and save it to a file.

BIO *bio_pfx = BIO_new_file(pfx_path, "wb");
i2d_PKCS12_bio(bio_pfx, p12);
BIO_free(bio_pfx);

where pfx_path is the output PFX file path.
The complete code is as follows:

#include <openssl/bio.h>
#include <openssl/pem.h>
#include <openssl/pkcs12.h>

int main() {<!-- -->
    char *cert_data = "<base64-encoded-certificate-data>";
    int cert_data_len = strlen(cert_data);
    char *key_data = "<base64-encoded-private-key-data>";
    int key_data_len = strlen(key_data);
    char *password = "password";
    int password_len = strlen(password);
    char *alias = "mykey";
    char *pfx_path = "out.pfx";

    X509 *cert = NULL;
    EVP_PKEY *pkey = NULL;

    BIO *bio_cert = BIO_new_mem_buf(cert_data, cert_data_len);
    cert = PEM_read_bio_X509(bio_cert, NULL, NULL, NULL);
    BIO_free(bio_cert);

    BIO *bio_key = BIO_new_mem_buf(key_data, key_data_len);
    pkey = PEM_read_bio_PrivateKey(bio_key, NULL, NULL, NULL);
    BIO_free(bio_key);

    PKCS12 *p12 = PKCS12_create(password, alias, pkey, cert, NULL, 0, 0, 0, 0, 0);
    if (p12 == NULL) {<!-- -->
        printf("Error creating PKCS#12 object.\
");
        ERR_print_errors_fp(stderr);
        return 1;
    }

    BIO *bio_pfx = BIO_new_file(pfx_path, "wb");
    i2d_PKCS12_bio(bio_pfx, p12);
    BIO_free(bio_pfx);

    PKCS12_free(p12);
    X509_free(cert);
    EVP_PKEY_free(pkey);

    return 0;
}

Note that this example is only a basic framework and needs to be modified and improved according to the actual situation.

Only the public-private key pair is converted to pfx

If there is only a public-private key pair and no certificate structure, you can use OpenSSL to directly create a self-signed certificate in C language, and combine it and the private key into a file in PFX format. Specific steps are as follows:

1. Generate a certificate request
First use the function X509_REQ_new() to create a certificate request object, use the function X509_REQ_set_subject_name() to set the certificate subject information, use the function EVP_PKEY_new() to create a new private key object, and use the function X509_REQ_set_pubkey() to bind the public key to the certificate request.

X509_REQ *req = X509_REQ_new();
X509_NAME *name = X509_NAME_new();
X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_ASC,
    (unsigned char *)"localhost", -1, -1, 0);
X509_REQ_set_subject_name(req, name);

EVP_PKEY *pkey = EVP_PKEY_new();
EVP_PKEY_set1_RSA(pkey, rsa);
X509_REQ_set_pubkey(req, pkey);

where rsa is the public key in the RSA public-private key pair.

2. Generate a self-signed certificate
Use the function X509_new() to create a new certificate object, use the functions X509_set_subject_name() and X509_set_issuer_name() to set the subject and issuer information of the certificate, use the function X509_set_pubkey() to bind the public key to the certificate, and use the function X509_sign() to sign certificate, to generate a self-signed certificate.

X509 *cert = X509_new();
X509_set_subject_name(cert, name);
X509_set_issuer_name(cert, name);
X509_set_pubkey(cert, pkey);
X509_sign(cert, pkey, EVP_sha256());

3. Create a PKCS#12 object
Use the function PKCS12_create() to create a PKCS#12 object and bind the certificate and private key to it.

PKCS12 *p12 = PKCS12_create(
    password, password_len,
    alias, pkey, cert, NULL, 0, 0, 0, 0
);

Where password and password_len are the passwords used to encrypt the PKCS#12 file, alias is the key identifier, pkey and cert are the private key and certificate structures, respectively.

4. Save the PKCS#12 object
Finally, use the function i2d_PKCS12_bio() to serialize the PKCS#12 object into binary data and save it to a file.

BIO *bio_pfx = BIO_new_file(pfx_path, "wb");
i2d_PKCS12_bio(bio_pfx, p12);
BIO_free(bio_pfx);

where pfx_path is the output PFX file path.

The complete code is as follows:

#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/pem.h>
#include <openssl/pkcs12.h>

int main() {<!-- -->
    char *pubkey_pem = "<base64-encoded-public-key>";
    char *pkey_pem = "<base64-encoded-private-key>";
    char *password = "password";
    int password_len = strlen(password);
    char *alias = "mykey";
    char *pfx_path = "out.pfx";

    BIO *pubkey_bio = BIO_new_mem_buf(pubkey_pem, -1);
    RSA *rsa = PEM_read_bio_RSA_PUBKEY(pubkey_bio, NULL, NULL, NULL);
    BIO_free(pubkey_bio);

    BIO *pkey_bio = BIO_new_mem_buf(pkey_pem, -1);
    EVP_PKEY *pkey = PEM_read_bio_PrivateKey(pkey_bio, NULL, NULL, NULL);
    BIO_free(pkey_bio);

    X509_REQ *req = X509_REQ_new();
    X509_NAME *name = X509_NAME_new();
    X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_ASC,
        (unsigned char *)"localhost", -1, -1, 0);
    X509_REQ_set_subject_name(req, name);

    EVP_PKEY_set1_RSA(pkey, rsa);
    X509_REQ_set_pubkey(req, pkey);

    X509 *cert = X509_new();
    X509_set_subject_name(cert, name);
    X509_set_issuer_name(cert, name);
    X509_set_pubkey(cert, pkey);
    X509_sign(cert, pkey, EVP_sha256());

    PKCS12 *p12 = PKCS12_create(
        password, alias, pkey, cert, NULL, 0, 0, 0, 0, 0);
    if (p12 == NULL) {<!-- -->
        printf("Error creating PKCS#12 object.\
");
        ERR_print_errors_fp(stderr);
        return 1;
    }

    BIO *bio_pfx = BIO_new_file(pfx_path, "wb");
    i2d_PKCS12_bio(bio_pfx, p12);
    BIO_free(bio_pfx);

    PKCS12_free(p12);
    X509_REQ_free(req);
    X509_free(cert);
    EVP_PKEY_free(pkey);

    return 0;
}

Where pubkey_pem and pkey_pem are BASE64-encoded RSA public and private keys, which can be obtained from files or the network. This example is just a basic framework and needs to be modified and perfected according to the actual situation.

Hexadecimal naked public and private key pair synthetic pfx certificate

If you only have the bare public-private key pair in hexadecimal format, you need to convert it to the PEM format supported by OpenSSL before using the above method to combine the certificate and private key to generate a PFX file. Specific steps are as follows:

1. Convert the public-private key pair in hexadecimal format to DER-encoded format
First, you need to convert the public-private key pair in hexadecimal format to DER-encoded format. You can use the function BN_hex2bn() in OpenSSL to convert a hexadecimal string to OpenSSL’s BIGNUM large number structure, and then use the function BN_bn2bin() to convert BIGNUM to binary data:

#include <openssl/bn.h>

char *hex_pubkey = "0123456789abcdef";
char *hex_privkey = "fedcba9876543210";

BIGNUM *n = BN_new();
BN_hex2bn( &n, hex_pubkey);
unsigned char pubkey_bin[BN_num_bytes(n)];
BN_bn2bin(n, pubkey_bin);
BN_free(n);

n = BN_new();
BN_hex2bn( &n, hex_privkey);
unsigned char privkey_bin[BN_num_bytes(n)];
BN_bn2bin(n, privkey_bin);
BN_free(n);

Where hex_pubkey and hex_privkey are the public key and private key in hexadecimal format, respectively.

2. Convert the binary data to the EVP_PKEY structure in OpenSSL
The next step is to convert the binary data into the EVP_PKEY structure in OpenSSL. You can use the function d2i_PrivateKey() to convert the private key data to the EVP_PKEY structure, and use the function d2i_PublicKey() to convert the public key data to the EVP_PKEY structure:

#include <openssl/evp.h>

EVP_PKEY *privkey = d2i_PrivateKey(EVP_PKEY_RSA, NULL, privkey_bin, sizeof(privkey_bin));
EVP_PKEY *pubkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, pubkey_bin, sizeof(pubkey_bin));

Among them, privkey and pubkey are the structure of private key and public key respectively.

3. Convert the EVP_PKEY structure to PEM format
Finally, the EVP_PKEY structure needs to be converted to a string in PEM format. A private key can be converted to PEM format using the function PEM_write_bio_PrivateKey() and a public key can be converted to PEM format using the function PEM_write_bio_PUBKEY().

#include <openssl/pem.h>

BIO *bio_priv = BIO_new(BIO_s_mem());
PEM_write_bio_PrivateKey(bio_priv, privkey, NULL, NULL, 0, NULL, NULL);

BIO *bio_pub = BIO_new(BIO_s_mem());
PEM_write_bio_PUBKEY(bio_pub, pubkey);

where bio_priv and bio_pub are BIO objects that store private and public keys in PEM format, respectively.

4. Save the public and private keys in PEM format to a file and generate a PFX file
Finally, save the public and private keys in PEM format to a file, and use the above method to merge the certificate and private key to generate a PFX file. For specific operations, please refer to steps 2, 3, and 4 in the above method.

The complete code is as follows:

#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/pkcs12.h>

int main() {<!-- -->
    char *hex_pubkey = "0123456789abcdef";
    char *hex_privkey = "fedcba9876543210";
    char *password = "password";
    int password_len = strlen(password);
    char *alias = "mykey";
    char *pfx_path = "out.pfx";

    BIGNUM *n = BN_new();
    BN_hex2bn( &n, hex_privkey);
    unsigned char privkey_bin[BN_num_bytes(n)];
    BN_bn2bin(n, privkey_bin);
    BN_free(n);

    n = BN_new();
    BN_hex2bn( &n, hex_pubkey);
    unsigned char pubkey_bin[BN_num_bytes(n)];
    BN_bn2bin(n, pubkey_bin);
    BN_free(n);

    EVP_PKEY *privkey = d2i_PrivateKey(EVP_PKEY_RSA, NULL, privkey_bin, sizeof(privkey_bin));
    EVP_PKEY *pubkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, pubkey_bin, sizeof(pubkey_bin));

    BIO *bio_priv = BIO_new(BIO_s_mem());
    PEM_write_bio_PrivateKey(bio_priv, privkey, NULL, NULL, 0, NULL, NULL);

    BIO *bio_pub = BIO_new(BIO_s_mem());
    PEM_write_bio_PUBKEY(bio_pub, pubkey);

    PKCS12 *p12 = PKCS12_create(
        password, alias, privkey, X509_NEW(NULL, privkey),
        NULL, 0, 0, 0, 0, 0
    );
    if (p12 == NULL) {<!-- -->
        printf("Error creating PKCS#12 object.\
");
        ERR_print_errors_fp(stderr);
        return 1;
    }

    BIO *bio_pfx = BIO_new_file(pfx_path, "wb");
    i2d_PKCS12_bio(bio_pfx, p12);
    BIO_free(bio_pfx);

    PKCS12_free(p12);
    BIO_free(bio_priv);
    BIO_free(bio_pub);
    EVP_PKEY_free(privkey);
    EVP_PKEY_free(pubkey);

    return 0;
}

Note that this example is only a basic framework and needs to be modified and improved according to the actual situation.