~~NOTOC~~
====== Scenario 12.2: PFX/PKCS#12 Export ======
**Category:** [[.:start|Import/Export]] \\
**Complexity:** *** (Medium) \\
**Prerequisites:** Certificate with Private Key \\
**Estimated Time:** 15-20 minutes
----
===== Description =====
This scenario describes **export and import in PFX/PKCS#12 format**. PFX (Personal Information Exchange) is the standard format for Windows and .NET to store certificates together with private keys and optionally the certificate chain in a password-protected file.
**PFX/PKCS#12 Properties:**
* **Content:** Certificate + Private Key + Chain
* **Encoding:** Binary (ASN.1/DER)
* **Encryption:** Password-protected
* **Extensions:** .pfx, .p12
----
===== Workflow =====
flowchart LR
subgraph Input
CERT[Certificate]
KEY[Private Key]
CHAIN[Chain]
end
subgraph PFX["PFX File"]
BAG1[Cert Bag]
BAG2[Key Bag]
BAG3[CA Bags]
end
CERT --> BAG1
KEY --> BAG2
CHAIN --> BAG3
PFX --> ENC[Encrypted]
----
===== Code Example: Create PFX =====
using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
using System.Security.Cryptography.X509Certificates;
using var ctx = PqCryptoContext.Initialize();
// Load certificate and key
var cert = ctx.LoadCertificate("server.crt.pem");
var privateKey = ctx.LoadPrivateKey("server.key.pem", "KeyPassword!");
// Combine certificate with key
var certWithKey = ctx.CombineCertificateAndKey(cert, privateKey);
// Export PFX
byte[] pfxBytes = certWithKey.Export(X509ContentType.Pfx, "PfxPassword123!");
File.WriteAllBytes("server.pfx", pfxBytes);
Console.WriteLine("PFX created: server.pfx");
----
===== Code Example: PFX with Chain =====
public class PfxExporter
{
public byte[] ExportWithChain(
X509Certificate2 certificate,
X509Certificate2Collection chain,
string password,
PfxExportOptions options = null)
{
options ??= PfxExportOptions.Default;
using var ctx = PqCryptoContext.Initialize();
// Create collection for export
var exportCollection = new X509Certificate2Collection();
exportCollection.Add(certificate);
// Add chain (without root, if desired)
foreach (var caCert in chain)
{
if (options.IncludeRoot || !IsSelfSigned(caCert))
{
exportCollection.Add(caCert);
}
}
// Export PFX
var pfxBytes = exportCollection.Export(X509ContentType.Pfx, password);
Console.WriteLine($"PFX created with {exportCollection.Count} certificates");
return pfxBytes;
}
public void ExportToFile(
X509Certificate2 certificate,
X509Certificate2Collection chain,
string outputPath,
string password)
{
var pfxBytes = ExportWithChain(certificate, chain, password);
File.WriteAllBytes(outputPath, pfxBytes);
// Set permissions (owner only)
if (OperatingSystem.IsWindows())
{
var fileInfo = new FileInfo(outputPath);
var security = fileInfo.GetAccessControl();
security.SetAccessRuleProtection(true, false);
fileInfo.SetAccessControl(security);
}
}
private bool IsSelfSigned(X509Certificate2 cert)
{
return cert.Subject == cert.Issuer;
}
}
public class PfxExportOptions
{
public bool IncludeRoot { get; set; } = false;
public bool IncludeChain { get; set; } = true;
public static PfxExportOptions Default => new PfxExportOptions();
}
----
===== Code Example: Import PFX =====
using var ctx = PqCryptoContext.Initialize();
// Load PFX
var pfxBytes = File.ReadAllBytes("server.pfx");
var cert = new X509Certificate2(
pfxBytes,
"PfxPassword123!",
X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet
);
Console.WriteLine($"Subject: {cert.Subject}");
Console.WriteLine($"Has Private Key: {cert.HasPrivateKey}");
// Extract private key
if (cert.HasPrivateKey)
{
var privateKey = cert.GetRSAPrivateKey()
?? cert.GetECDsaPrivateKey()
?? (AsymmetricAlgorithm)ctx.GetPqPrivateKey(cert);
Console.WriteLine($"Key Type: {privateKey.GetType().Name}");
}
// Extract chain from PFX
var collection = new X509Certificate2Collection();
collection.Import(pfxBytes, "PfxPassword123!", X509KeyStorageFlags.DefaultKeySet);
Console.WriteLine($"Certificates in PFX: {collection.Count}");
foreach (var c in collection)
{
Console.WriteLine($" - {c.Subject}");
}
----
===== Code Example: Secure PFX with AES-256 =====
public class SecurePfxExporter
{
public byte[] ExportSecure(
X509Certificate2 certificate,
string password)
{
// .NET 5+ supports modern PFX encryption
var exportParams = new PbeParameters(
PbeEncryptionAlgorithm.Aes256Cbc,
HashAlgorithmName.SHA256,
iterations: 100000
);
return certificate.Export(X509ContentType.Pfx, password);
}
public byte[] ExportWithOpenSsl(
X509Certificate2 certificate,
AsymmetricAlgorithm privateKey,
X509Certificate2Collection chain,
string password)
{
using var ctx = PqCryptoContext.Initialize();
// OpenSSL-based export for maximum compatibility
var pfxBuilder = new Pkcs12Builder();
// Add Key Bag
var keyBag = new Pkcs12KeyBag(
privateKey.ExportPkcs8PrivateKey(),
skipCopy: false
);
// Add Cert Bag
var certBag = new Pkcs12CertBag(certificate);
// Create Safe Contents
var safeContents = new Pkcs12SafeContents();
safeContents.AddBag(keyBag);
safeContents.AddBag(certBag);
foreach (var caCert in chain)
{
safeContents.AddBag(new Pkcs12CertBag(caCert));
}
// Encrypt with password
pfxBuilder.AddSafeContentsEncrypted(
safeContents,
password,
new PbeParameters(
PbeEncryptionAlgorithm.Aes256Cbc,
HashAlgorithmName.SHA256,
100000
)
);
pfxBuilder.SealWithMac(password, HashAlgorithmName.SHA256, 100000);
return pfxBuilder.Encode();
}
}
----
===== Code Example: PFX for Windows Certificate Store =====
public class WindowsCertificateInstaller
{
public void InstallPfxToStore(
string pfxPath,
string password,
StoreName storeName = StoreName.My,
StoreLocation location = StoreLocation.LocalMachine)
{
var pfxBytes = File.ReadAllBytes(pfxPath);
var cert = new X509Certificate2(
pfxBytes,
password,
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet |
X509KeyStorageFlags.Exportable
);
using var store = new X509Store(storeName, location);
store.Open(OpenFlags.ReadWrite);
// Check if already present
var existing = store.Certificates.Find(
X509FindType.FindByThumbprint,
cert.Thumbprint,
validOnly: false
);
if (existing.Count > 0)
{
Console.WriteLine("Certificate already exists in store");
return;
}
store.Add(cert);
store.Close();
Console.WriteLine($"Certificate installed: {cert.Subject}");
Console.WriteLine($"Store: {storeName} ({location})");
Console.WriteLine($"Thumbprint: {cert.Thumbprint}");
}
public void ExportFromStore(
string thumbprint,
string outputPath,
string password,
StoreName storeName = StoreName.My,
StoreLocation location = StoreLocation.LocalMachine)
{
using var store = new X509Store(storeName, location);
store.Open(OpenFlags.ReadOnly);
var certs = store.Certificates.Find(
X509FindType.FindByThumbprint,
thumbprint,
validOnly: false
);
if (certs.Count == 0)
{
throw new Exception($"Certificate not found: {thumbprint}");
}
var cert = certs[0];
if (!cert.HasPrivateKey)
{
throw new Exception("Certificate has no private key");
}
var pfxBytes = cert.Export(X509ContentType.Pfx, password);
File.WriteAllBytes(outputPath, pfxBytes);
Console.WriteLine($"PFX exported: {outputPath}");
}
}
----
===== Create PFX with OpenSSL =====
# Create PFX from PEM files
openssl pkcs12 -export \
-out server.pfx \
-inkey server.key \
-in server.crt \
-certfile chain.pem \
-passout pass:MyPassword
# PFX with modern algorithm (AES-256)
openssl pkcs12 -export \
-out server.pfx \
-inkey server.key \
-in server.crt \
-certfile chain.pem \
-aes256 \
-passout pass:MyPassword
# Inspect PFX
openssl pkcs12 -info -in server.pfx -passin pass:MyPassword
# Unpack PFX
openssl pkcs12 -in server.pfx \
-out combined.pem \
-nodes \
-passin pass:MyPassword
----
===== Industry-Specific PFX Requirements =====
^ Industry ^ Key Storage ^ Export ^ Specifics ^
| **Windows Server** | MachineKeySet | Exportable | IIS SSL Binding |
| **Azure** | UserKeySet | Non-Exportable | App Service |
| **Code Signing** | MachineKeySet | Non-Exportable | Authenticode |
| **Smart Card** | Hardware | Non-Exportable | PIV Certificates |
----
===== Related Scenarios =====
^ Relationship ^ Scenario ^ Description ^
| **Alternative** | [[.:pem_export|12.1 PEM Export]] | Linux format |
| **Related** | [[.:pkcs7_chain|12.3 PKCS#7 Chain]] | Certificates only |
| **Prerequisite** | [[en:int:pqcrypt:szenarien:zertifikate:server_cert|3.1 Server Certificate]] | Create certificate |
----
<< [[.:pem_export|<- 12.1 PEM Export]] | [[.:start|^ Import/Export]] | [[.:pkcs7_chain|12.3 PKCS#7 Chain ->]] >>
{{tag>scenario import export pfx pkcs12 windows}}
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//