PKCS#12

PKCS#12 (also known as PFX) is a file format defined in RFC 7292 for storing cryptographic objects - typically a private key and its associated X.509 certificate chain - protected by a password. It is widely used for importing and exporting credentials in TLS servers, browsers, and certificate management tools.

This API is defined in botan/pkcs12.h.

Added in version 3.13.

PKCS12_Export_Options

class PKCS12_Export_Options

Options controlling how a PKCS#12 file is generated. Construct with the password (mandatory) and an optional friendly name; tweak individual fields with the chainable with_* mutators, or pick a preset via the static pseudo-constructors below.

explicit PKCS12_Export_Options(std::string_view password, std::optional<std::string> friendly_name = {})

Constructs an options object with modern defaults: PBES2-SHA256-AES256 key encryption, SHA-256 MAC, 100 000 KDF iterations, certificates stored unencrypted.

static PKCS12_Export_Options modern(std::string_view password, std::optional<std::string> friendly_name = {})

Pseudo-constructor for modern defaults (identical to the regular constructor). Spelled out for clarity at call sites.

static PKCS12_Export_Options legacy_compat(std::string_view password, std::optional<std::string> friendly_name = {})

Pseudo-constructor for legacy-compatible defaults: PBE-SHA1-3DES key encryption, SHA-1 MAC, 2 048 KDF iterations. Use when interoperability with old software (Java keytool pre-2019, legacy OpenSSL releases, pre-Windows-10) is required.

PKCS12_Export_Options &with_friendly_name(std::string name)

Overrides the friendly name. If unset, the bundle-level friendly name (see PKCS12::set_friendly_name) is used.

PKCS12_Export_Options &with_iterations(size_t n)

Sets the KDF iteration count. Values of 0 or above 1 000 000 (PKCS12_MAX_ITERATIONS) cause an Invalid_Argument exception at export time.

PKCS12_Export_Options &with_key_encryption_algo(std::string algo)

Algorithm used to encrypt the private key (PKCS8ShroudedKeyBag). Supported values:

  • "PBES2-SHA256-AES256" - modern (default)

  • "PBES2-SHA256-AES128" - modern

  • "PBE-SHA1-3DES" - legacy

  • "PBE-SHA1-2DES" - legacy

PKCS12_Export_Options &with_cert_encryption_algo(std::string algo)

Algorithm used to encrypt certificates. If the algo is empty (default), certificates are stored unencrypted.

PKCS12_Export_Options &with_mac_digest(std::string algo)

Hash algorithm for the integrity MAC. Supported: "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512", "SHA-512-256". Default is "SHA-256".

PKCS12_Export_Options &without_mac()

Disables the integrity MAC. Generally not recommended.

PKCS12

class PKCS12

PKCS#12/PFX bundle: parse, inspect, mutate, and export. The default constructor produces an empty bundle that the caller fills via the add_* / set_* mutators before invoking export_to.

PKCS12()

Constructs an empty bundle.

PKCS12(std::span<const uint8_t> data, std::string_view password)

Parses a DER-encoded PFX file. Throws Decoding_Error if the file is malformed, or Invalid_Authentication_Tag if MAC verification fails.

Accessors

const std::vector<std::shared_ptr<Private_Key>> &private_keys() const

Private keys stored in the bundle, in storage order (parse-order for parsed PFX, insertion-order for built ones). PKCS#12 supports multiple keys per file.

const std::vector<X509_Certificate> &certificates() const

All certificates stored in the bundle. The end-entity, if any, comes first when produced by parsing; insertion order is preserved for bundles built in memory.

std::optional<X509_Certificate> end_entity_certificate() const

The first certificate whose subjectPublicKeyInfo matches one of the stored private keys, or nullopt if none match (e.g. a certificate-only or key-only bundle).

std::vector<X509_Certificate> ca_certificates() const

Convenience helper: every certificate except the one returned by end_entity_certificate. Returned in storage order. For a key-less bundle, returns all certificates after the first.

const std::optional<std::string> &friendly_name() const

The friendlyName attribute, if present.

const std::optional<std::vector<uint8_t>> &local_key_id() const

The localKeyId attribute, if present.

const std::vector<OID> &unknown_bag_types() const

OIDs of bag types encountered during parsing but not handled by this implementation (e.g. SecretBag). Empty for normal PKCS#12 files and for bundles constructed in-memory.

Mutators

void add_key(std::shared_ptr<Private_Key> key)

Adds a private key to the bundle.

void add_certificate(X509_Certificate cert)

Adds a certificate. End-entity vs CA is determined at export time by matching against stored keys.

void set_friendly_name(std::string name)
void clear_friendly_name()
void set_local_key_id(std::vector<uint8_t> id)
void clear_local_key_id()

Set or clear the bundle-level friendly name / localKeyId attributes.

Export

std::vector<uint8_t> export_to(const PKCS12_Export_Options &options, RandomNumberGenerator &rng) const

Serializes the bundle as a PKCS#12/PFX file.

Throws Invalid_Argument if the options are inconsistent (e.g. an unsupported algorithm is requested) or if a stored private key does not match any stored certificate.

Iteration counts above PKCS12_MAX_ITERATIONS (1 000 000) and a SafeContentsBag nesting depth above PKCS12_MAX_NESTING (10) are rejected.

Examples

Generating a PFX file from a freshly created key and self-signed certificate:

#include <botan/auto_rng.h>
#include <botan/ec_group.h>
#include <botan/ecdsa.h>
#include <botan/hex.h>
#include <botan/pkcs12.h>
#include <botan/x509self.h>

#include <iostream>
#include <memory>

int main() {
   Botan::AutoSeeded_RNG rng;

   // Generate an ECDSA private key + self-signed certificate to bundle.
   auto key = std::make_shared<Botan::ECDSA_PrivateKey>(rng, Botan::EC_Group::from_name("secp256r1"));

   const Botan::X509_Cert_Options cert_opts("example.com");
   const auto cert = Botan::X509::create_self_signed_cert(cert_opts, *key, "SHA-256", rng);

   // Populate the PKCS#12 bundle.
   Botan::PKCS12 bundle;
   bundle.add_key(key);
   bundle.add_certificate(cert);
   bundle.set_friendly_name("My Key");

   // Export with modern defaults (PBES2-SHA256-AES256, SHA-256 MAC,
   // 100 000 iterations). For maximum interoperability with legacy software
   // use Botan::PKCS12_Export_Options::legacy_compat("secret") instead.
   const auto pfx = bundle.export_to(Botan::PKCS12_Export_Options::modern("secret"), rng);

   std::cout << Botan::hex_encode(pfx) << '\n';
   return 0;
}

Parsing a PFX file with error handling:

#include <botan/exceptn.h>
#include <botan/hex.h>
#include <botan/pk_keys.h>
#include <botan/pkcs12.h>
#include <botan/pkix_types.h>
#include <botan/x509cert.h>

#include <iostream>
#include <iterator>
#include <vector>

int main() {
   // Read a hex-encoded PFX from stdin.
   const std::string hex_input((std::istreambuf_iterator<char>(std::cin)), std::istreambuf_iterator<char>());
   const auto pfx_bytes = Botan::hex_decode(hex_input);

   try {
      const Botan::PKCS12 bundle(pfx_bytes, "secret");

      if(!bundle.private_keys().empty()) {
         const auto& key = bundle.private_keys().front();
         std::cout << "Key: " << key->algo_name() << " (" << key->key_length() << " bits)\n";
      }

      if(const auto ee = bundle.end_entity_certificate()) {
         std::cout << "End-entity: " << ee->subject_dn().to_string() << '\n'
                   << "Fingerprint (SHA-256): " << ee->fingerprint("SHA-256") << '\n';
      }

      for(const auto& ca : bundle.ca_certificates()) {
         std::cout << "CA: " << ca.subject_dn().to_string() << '\n';
      }

      if(bundle.friendly_name()) {
         std::cout << "Friendly name: " << *bundle.friendly_name() << '\n';
      }
   } catch(const Botan::Invalid_Authentication_Tag&) {
      std::cerr << "Wrong password or corrupted MAC\n";
      return 1;
   } catch(const Botan::Decoding_Error& e) {
      std::cerr << "Malformed or unsupported PFX file: " << e.what() << '\n';
      return 2;
   }
   return 0;
}

Building a PFX bundle that contains a CA certificate chain alongside the end-entity key and certificate:

#include <botan/asn1_time.h>
#include <botan/auto_rng.h>
#include <botan/ec_group.h>
#include <botan/ecdsa.h>
#include <botan/hex.h>
#include <botan/pkcs10.h>
#include <botan/pkcs12.h>
#include <botan/x509_ca.h>
#include <botan/x509self.h>

#include <iostream>
#include <memory>
#include <vector>

int main() {
   Botan::AutoSeeded_RNG rng;
   const auto group = Botan::EC_Group::from_name("secp256r1");

   // Issuing CA.
   const Botan::ECDSA_PrivateKey ca_key(rng, group);
   Botan::X509_Cert_Options ca_opts("Example CA");
   ca_opts.CA_key();
   const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts, ca_key, "SHA-256", rng);

   // End-entity, signed by the CA.
   auto ee_key = std::make_shared<Botan::ECDSA_PrivateKey>(rng, group);
   Botan::X509_Cert_Options ee_opts("example.com");
   ee_opts.dns = "example.com";
   const auto csr = Botan::X509::create_cert_req(ee_opts, *ee_key, "SHA-256", rng);
   const Botan::X509_CA ca(ca_cert, ca_key, "SHA-256", rng);
   const auto ee_cert = ca.sign_request(csr, rng, Botan::X509_Time("200101000000Z"), Botan::X509_Time("300101000000Z"));

   // Bundle: end-entity key + cert + issuing CA in the chain.
   Botan::PKCS12 bundle;
   bundle.add_key(ee_key);
   bundle.add_certificate(ee_cert);
   bundle.add_certificate(ca_cert);

   const auto pfx = bundle.export_to(Botan::PKCS12_Export_Options::modern("secret", "Server Key"), rng);

   std::cout << Botan::hex_encode(pfx) << '\n';
   return 0;
}

Note

The default encryption algorithm is PBES2-SHA256-AES256 with SHA-256 MAC and 100 000 KDF iterations. For maximum compatibility with legacy software (older Java keytool, legacy OpenSSL builds), use PKCS12_Export_Options::legacy_compat or explicitly configure with_key_encryption_algo("PBE-SHA1-3DES"), with_mac_digest("SHA-1"), and with_iterations(2048).

Supported Algorithms

The following algorithms are available depending on which Botan modules are built:

Field

Value

Required module

Notes

with_key_encryption_algo

"PBES2-SHA256-AES256"

pbes2, aes

Default; recommended for modern use

with_key_encryption_algo

"PBES2-SHA256-AES128"

pbes2, aes

Modern

with_key_encryption_algo

"PBE-SHA1-3DES"

pkcs12_pbe, des

Legacy; widest compatibility

with_key_encryption_algo

"PBE-SHA1-2DES"

pkcs12_pbe, des

Legacy

with_cert_encryption_algo

Same as above, or ""

Empty = certificates stored unencrypted

with_mac_digest

"SHA-256"

sha2_32

Default; required by OpenSSL 3.x default policy

with_mac_digest

"SHA-1"

sha1

Legacy; widest compatibility

with_mac_digest

"SHA-384", "SHA-512"

sha2_64

Uncommon; supported for parsing and generation

See Command Line Interface for the pkcs12_export / pkcs12_info CLI commands.