~~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//