Category: Import/Export
Complexity: * (Medium)
Prerequisites: Certificate with Private Key
Estimated Time: 15-20 minutes
</WRAP>
—-
===== 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 =====
<mermaid>
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]
</mermaid>
—-
===== Code Example: Create PFX =====
<code csharp>
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>
—-
===== Code Example: PFX with Chain =====
<code csharp>
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>
—-
===== Code Example: Import PFX =====
<code csharp>
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>
—-
===== Code Example: Secure PFX with AES-256 =====
<code csharp>
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>
—-
===== Code Example: PFX for Windows Certificate Store =====
<code csharp>
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}“);
}
}
</code>
—-
===== Create PFX with OpenSSL =====
<code bash>
# 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
</code>
—-
===== 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 | 12.1 PEM Export | Linux format |
| Related | 12.3 PKCS#7 Chain | Certificates only |
| Prerequisite | 3.1 Server Certificate | Create certificate |
—-
« <- 12.1 PEM Export | ^ Import/Export | 12.3 PKCS#7 Chain -> »
—- Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional