Transfer certificates with PQ keys as PFX files
You want to move a certificate together with its classical key and its PQ key to another process or machine, for example from the machine that issued it to the server that uses it.
Solution overview
| Element | Purpose |
|---|---|
ExportPfx(password) | writes the certificate, the classical key, and the PQ key to a PKCS #12 file |
X509Certificate2ExportExtensions.ImportPfx(bytes, password, flags) | reads the file and associates the PQ key again |
X509Certificate2Collection.ExportPfx, ImportPfxCollection | the same for several certificates, for example with a chain |
ExportPfx(password, pbeParameters) | as above, with encryption settings you choose |
Implementation
Export the certificate. Here, server is a certificate whose classical and PQ keys are in the process:
using var serverRsa = RSA.Create(3072); using var server = new CertificateRequest( "CN=www.example.com", serverRsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1) .CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(1), CryptoMode.Hybrid); string password = Convert.ToBase64String(RandomNumberGenerator.GetBytes(24)); File.WriteAllBytes("www.example.com.pfx", server.ExportPfx(password));
Import it in the other process:
using var imported = X509Certificate2ExportExtensions.ImportPfx( File.ReadAllBytes("www.example.com.pfx"), password, X509KeyStorageFlags.EphemeralKeySet); using (var pqKey = imported.GetAlternativePrivateMLDsaKey()) { Console.WriteLine($"RSA: {imported.HasPrivateKey}, ML-DSA: {pqKey is not null}"); }
For a program that can't read PBES2, write the Triple DES variant explicitly:
var legacy = new PbeParameters(PbeEncryptionAlgorithm.TripleDes3KeyPkcs12, HashAlgorithmName.SHA1, 2000); File.WriteAllBytes("www.example.com-3des.pfx", server.ExportPfx(password, legacy));
Remarks
- Import with the library. The .NET
new X509Certificate2(pfx, password)constructor also reads the file and picks up the certificate and the classical key, but it skips the PQ key. OnlyImportPfxassociates it. - No export without the PQ key. If you explicitly request a PQ export with
ExportPfx(password, CryptoMode.Hybrid)and the process doesn't know a PQ key, the method throws aCryptographicExceptioninstead of silently writing a file without the PQ key. - Pure ML-DSA certificates. For these, the file carries only the ML-DSA key. After the import,
GetPrivateMLDsaKeyreturns the key, andHasPrivateKeystaysfalse. - Encryption. Without
PbeParameters,ExportPfxwith a PQ key encrypts according to PBES2: AES-256-CBC, PBKDF2 with HMAC-SHA-256 and 2,048 iterations, and a SHA-256 MAC. WithPbeParameters, you can useAes128Cbc,Aes192Cbc, orAes256Cbcwith SHA-1, SHA-256, SHA-384, or SHA-512, orTripleDes3KeyPkcs12with SHA-1. The method rejects other combinations with aCryptographicException. On .NET Framework, the library providesPbeParametersandPbeEncryptionAlgorithmitself, in theSystem.Security.Cryptographynamespace. CryptoMode.Classic. WithoutPbeParameters,ExportPfxwrites exactly the file that the .NETExport(X509ContentType.Pfx, password)method writes, without the PQ key and with the .NET encryption.- Protect the password and the file. The file is only as secure as its password. Use a long, random password, send it through a different channel than the file, and delete the file after the import.
- .NET 8 on Linux. If the certificate's own key is an ML-DSA or ML-KEM key rather than RSA or ECDSA, .NET 8 on Linux can't read the PFX file, and
ImportPfxfails there with the .NETCryptographicException. Hybrid certificates aren't affected. On Linux, move such certificates as PEM files, and associate the key with the matching Attach method.
Related topics
en/wiki/pqcrypt/lib/how-to/export-and-import-pfx.txt · Last modified: by Wolfgang van der Stille