---
title: "Convert PEM to PFX (PKCS#12) in C#"
url: https://recursiveai.org/onecrypto/cookbook/convert-pem-to-pfx/
description: "Convert a PEM certificate and private key to a PKCS#12 .pfx file in C#, with AES-256 and PBKDF2 protection, then open it with .NET and OpenSSL. Runnable code with OneCrypto 0.1.0-preview.1."
version: "0.1.0-preview.1"
---

# Convert PEM to PFX (PKCS#12) in C#

Load the certificate and key with `Crypto.Load`, then write both into one password-protected file with `Pkcs12File.Create`. The program below uses a certificate and key made by OpenSSL, and opens the result with OneCrypto, with .NET’s `X509CertificateLoader` and with OpenSSL.

```shell
dotnet new console -o PemToPfx && cd PemToPfx
dotnet add package RecursiveAI.OneCrypto --version 0.1.0-preview.1
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes -days 365 \
  -subj "/CN=api.example.com" -keyout key.pem -out cert.pem
```

OneCrypto is a preview, so `dotnet add package` needs `--version` or `--prerelease`. Put this in `Program.cs` and run `dotnet run`:

```csharp
using System.Security.Cryptography.X509Certificates;
using RecursiveAI.OneCrypto;
using RecursiveAI.OneCrypto.Pkcs;

using LoadedItems certFile = Crypto.Load("cert.pem");
using LoadedItems keyFile = Crypto.Load("key.pem");
Certificate cert = certFile.Certificates[0];
PrivateKeyInfo key = keyFile.PrivateKeys[0].Info;

// Defaults: AES-256-CBC, PBKDF2-HMAC-SHA-256 with 600,000 iterations, HMAC-SHA-256 MAC
Pkcs12File pfx = Pkcs12File.Create(key, cert, chain: [], "pfx-password",
    friendlyName: "api.example.com");
File.WriteAllBytes("server.pfx", pfx.Encoded.ToArray());
Console.WriteLine($"Wrote server.pfx: {pfx.Encoded.Length:N0} bytes, " +
    $"MAC {pfx.MacKind}");

// Read it back: the MAC is checked before anything is decrypted
using Pkcs12Contents contents = Pkcs12File.Parse(File.ReadAllBytes("server.pfx"))
    .Open("pfx-password");
Console.WriteLine($"OneCrypto: {contents.Certificates.Count} certificate " +
    $"({contents.Certificates[0].Subject}), {contents.PrivateKeys.Count} private key");

// .NET's own loader stops at 300,000 iterations by default: raise its limits
var limits = new Pkcs12LoaderLimits { MacIterationLimit = 600_000,
    IndividualKdfIterationLimit = 600_000, TotalKdfIterationLimit = 2_000_000 };
using X509Certificate2 x509 = X509CertificateLoader.LoadPkcs12FromFile("server.pfx",
    "pfx-password", loaderLimits: limits);
Console.WriteLine($".NET:      {x509.Subject}, has private key: {x509.HasPrivateKey}");
```

```text
Wrote server.pfx: 1,184 bytes, MAC Pkcs12
OneCrypto: 1 certificate (CN=api.example.com), 1 private key
.NET:      CN=api.example.com, has private key: True
```

Pass the intermediate certificates as `chain` so servers can send the whole chain. `Pkcs12File.Create` also takes a list of bags for files with several keys, and `Pkcs12WriteOptions` chooses the MAC (`Pbmac1` for RFC 9579), the iteration counts and whether certificates are encrypted.

## Check it with OpenSSL

```shell
openssl pkcs12 -in server.pfx -passin pass:pfx-password -noout -info 2>&1
```

```text
MAC: sha256, Iteration 600000
MAC length: 32, salt length: 16
PKCS7 Data
Shrouded Keybag: PBES2, PBKDF2, AES-256-CBC, Iteration 600000, PRF hmacWithSHA256
PKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC, Iteration 600000, PRF hmacWithSHA256
Certificate bag
```

## Limits

- **Not audited.** This library has not been independently audited. It is a preview: 0.1.0-preview.1, and the API can change.
- **Iteration counts.** The defaults use 600,000 PBKDF2 iterations for the key, the certificates and the MAC. .NET’s `X509CertificateLoader` refuses more than 300,000 unless you raise its `Pkcs12LoaderLimits`, as above; other readers may have limits of their own. A lower `MacIterations` in `Pkcs12WriteOptions` makes the password cheaper to guess. Triple DES and RC2 are never written; legacy PBEs are read, except RC4.
- **Limits on input.** Files read from elsewhere are bounded: 64 MiB, 5,000,000 iterations, 256 safes and 4,096 bags by default (`PkcsLimits`). A file with no MAC is refused unless you pass `requireMac: false`.

How PKCS#8, PKCS#12 and OpenSSH keys are read and written: [PKCS](https://recursiveai.org/onecrypto/docs/pkcs/). To read any `.pfx` without knowing what it holds: [Load a PEM or PKCS#8 private key](https://recursiveai.org/onecrypto/cookbook/load-pem-private-key/).
