Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

PKCS#12

Overview

A PKCS#12 file (also called a PFX bundle) packs a private key, its end-entity certificate, and an optional chain of CA certificates into a single password-protected DER blob. It is the interchange format used by browsers, Java keystores, and many TLS deployment tools.

Pkcs12 wraps OpenSSL’s PKCS12* structure. The two main operations are:

  • Pkcs12::from_der + parse — load an existing bundle and extract its contents.
  • Pkcs12::create + to_der — assemble a new bundle from Rust objects.

Pkcs12 — Bundle Wrapper

#![allow(unused)]
fn main() {
pub struct Pkcs12 { /* PKCS12* */ }

impl Pkcs12 {
    /// Allocate a new, empty PKCS#12 structure (PKCS12_new).
    pub fn new() -> Result<Self, ErrorStack>;

    /// Load a bundle from DER-encoded bytes.
    pub fn from_der(der: &[u8]) -> Result<Self, ErrorStack>;

    /// Serialise the bundle to DER.
    pub fn to_der(&self) -> Result<Vec<u8>, ErrorStack>;

    /// Parse the bundle, returning the private key, end-entity certificate,
    /// and any additional CA certificates.
    ///
    /// Pass `""` for an unencrypted bundle.
    pub fn parse(
        &self,
        password: &str,
    ) -> Result<(Pkey<Private>, X509, Vec<X509>), ErrorStack>;

    /// Create a new bundle from a private key and certificate.
    ///
    /// - `password` — MAC / encryption passphrase.
    /// - `name`     — friendly name stored in the bundle (e.g. the subject CN).
    /// - `key`      — private key.
    /// - `cert`     — end-entity certificate.
    /// - `ca`       — slice of additional CA certificates (may be empty).
    ///
    /// Uses AES-256-CBC for key encryption and SHA-256 for the MAC.
    pub fn create(
        password: &str,
        name: &str,
        key: &Pkey<Private>,
        cert: &X509,
        ca: &[X509],
    ) -> Result<Self, ErrorStack>;
}

unsafe impl Send for Pkcs12 {}
unsafe impl Sync for Pkcs12 {}
}

Pkcs12 does not implement Clone — the underlying PKCS12* has no up_ref function.

Examples

Load a Bundle and Extract Key and Certificate

use native_ossl::pkcs12::Pkcs12;

let der = std::fs::read("bundle.p12")?;
let p12 = Pkcs12::from_der(&der)?;

let (key, cert, ca_chain) = p12.parse("my password")?;

println!("Key algorithm: {}", if key.is_a(c"RSA") { "RSA" } else { "other" });
if let Some(subject) = cert.subject_name().to_string() {
    println!("Certificate subject: {subject}");
}
println!("CA chain length: {}", ca_chain.len());

Create a Bundle and Write to Disk

use native_ossl::pkcs12::Pkcs12;
use native_ossl::pkey::KeygenCtx;
use native_ossl::x509::{X509Builder, X509NameOwned};

// Generate a key pair.
let key = KeygenCtx::new(c"ED25519")?.generate()?;

// Build a self-signed certificate.
let mut name = X509NameOwned::new()?;
name.add_entry_by_txt(c"CN", b"example.com")?;

let cert = X509Builder::new()?
    .set_version(2)?
    .set_serial_number(1)?
    .set_not_before_offset(0)?
    .set_not_after_offset(365 * 86400)?
    .set_subject_name(&name)?
    .set_issuer_name(&name)?
    .set_public_key(&key)?
    .sign(&key, None)?
    .build();

// Pack into a PKCS#12 bundle with no CA chain.
let p12 = Pkcs12::create("my password", "example.com", &key, &cert, &[])?;
let der = p12.to_der()?;
std::fs::write("bundle.p12", &der)?;

Round-Trip (Create, Serialise, Parse)

The DER encoding of a bundle is deterministic for a given set of inputs and OpenSSL defaults, so a DER round-trip can be used as a consistency check:

use native_ossl::pkcs12::Pkcs12;

let p12 = Pkcs12::create("pass", "friendly name", &key, &cert, &[])?;
let der1 = p12.to_der()?;

let p12b = Pkcs12::from_der(&der1)?;
let der2 = p12b.to_der()?;
assert_eq!(der1, der2);

let (key2, cert2, _ca) = p12b.parse("pass")?;
assert!(key2.is_a(c"ED25519"));
assert_eq!(cert.to_der()?, cert2.to_der()?);

Include a CA Chain

Pass CA certificates in chain order (intermediate first, root last):

use native_ossl::pkcs12::Pkcs12;

let p12 = Pkcs12::create(
    "pass",
    "my server",
    &server_key,
    &server_cert,
    &[intermediate_cert, root_cert],
)?;

Design Notes

  • Password encodingPkcs12::create and Pkcs12::parse accept a Rust &str. The string is converted to a null-terminated C string internally. Null bytes in the password are rejected.
  • Encryption defaultsPKCS12_create_ex is called with nid_key = 0 and nid_cert = 0, which tells OpenSSL to use its compiled-in defaults: AES-256-CBC for key bags and no additional certificate encryption. The MAC uses SHA-256.
  • CA certificate ownershipPkcs12::create calls X509_up_ref on each CA certificate before adding it to the internal stack. The original X509 values may be dropped before or after create returns.
  • parse output ownershipPkcs12::parse calls PKCS12_parse, which allocates a new EVP_PKEY* and X509* (both fully owned). The CA chain is returned as a Vec<X509> with each element independently reference-counted.
  • No ClonePKCS12 has no up_ref; serialise with to_der and re-parse with from_der if a second independent copy is needed.
  • new() returns an empty structurePkcs12::new wraps PKCS12_new and allocates an uninitialised bundle. It is useful for low-level construction via the OpenSSL API directly; for normal use, prefer Pkcs12::create.