~~NOTOC~~
====== Scenario 12.1: PEM Export/Import ======
**Category:** [[.:start|Import/Export]] \\
**Complexity:** ** (Low) \\
**Prerequisites:** Certificate or key \\
**Estimated Time:** 10-15 minutes
----
===== Description =====
This scenario describes **export and import in PEM format** (Privacy-Enhanced Mail). PEM is the most common format for certificates and keys on Linux/Unix systems and is natively supported by OpenSSL.
**PEM Properties:**
* **Encoding:** Base64 with header/footer
* **Header:** ''-----BEGIN CERTIFICATE-----'' etc.
* **Readability:** Text file, easy to inspect
* **Concatenation:** Multiple objects possible in one file
----
===== PEM Types =====
^ Type ^ Header ^ Content ^
| Certificate | ''BEGIN CERTIFICATE'' | X.509 certificate |
| Private Key | ''BEGIN PRIVATE KEY'' | PKCS#8 unencrypted |
| Encrypted Key | ''BEGIN ENCRYPTED PRIVATE KEY'' | PKCS#8 encrypted |
| RSA Key | ''BEGIN RSA PRIVATE KEY'' | PKCS#1 (legacy) |
| CSR | ''BEGIN CERTIFICATE REQUEST'' | PKCS#10 CSR |
| CRL | ''BEGIN X509 CRL'' | Certificate Revocation List |
----
===== Code Example: Export Certificate as PEM =====
using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
using System.Security.Cryptography.X509Certificates;
using var ctx = PqCryptoContext.Initialize();
// Load certificate
var cert = new X509Certificate2("certificate.pfx", "password");
// Export as PEM
ctx.ToPemFile(cert, "certificate.pem");
// Or as string
string pemString = ctx.ToPem(cert);
Console.WriteLine(pemString);
// Output:
// -----BEGIN CERTIFICATE-----
// MIIBkTCB+wIJAK... (Base64)
// -----END CERTIFICATE-----
----
===== Code Example: Private Key as PEM =====
using var ctx = PqCryptoContext.Initialize();
// Generate key
using var keyPair = ctx.GenerateKeyPair(PqAlgorithm.MlDsa65);
// Unencrypted PEM (ONLY for tests!)
keyPair.ToPemFile("private-key.pem");
// Encrypted PEM (RECOMMENDED)
keyPair.ToEncryptedPemFile(
path: "private-key-encrypted.pem",
password: "StrongPassword123!",
pbeAlgorithm: PbeAlgorithm.Aes256Cbc,
iterations: 100000
);
// Public key separately
keyPair.PublicKey.ToPemFile("public-key.pem");
----
===== Code Example: Import PEM =====
using var ctx = PqCryptoContext.Initialize();
// Load certificate from PEM
var cert = ctx.LoadCertificate("certificate.pem");
Console.WriteLine($"Subject: {cert.Subject}");
Console.WriteLine($"Valid until: {cert.NotAfter:d}");
// Load encrypted private key
var privateKey = ctx.LoadPrivateKey(
path: "private-key-encrypted.pem",
password: "StrongPassword123!"
);
// Combine certificate with private key
var certWithKey = ctx.LoadCertificateWithPrivateKey(
certPath: "certificate.pem",
keyPath: "private-key-encrypted.pem",
keyPassword: "StrongPassword123!"
);
Console.WriteLine($"Has private key: {certWithKey.HasPrivateKey}");
----
===== Code Example: Certificate Chain as PEM =====
public class PemChainExporter
{
public void ExportChainAsPem(
X509Certificate2 endEntity,
X509Certificate2Collection chain,
string outputPath)
{
using var ctx = PqCryptoContext.Initialize();
var sb = new StringBuilder();
// End-entity certificate first
sb.AppendLine("# End-Entity Certificate");
sb.AppendLine(ctx.ToPem(endEntity));
// Then intermediate CAs
foreach (var cert in chain.OrderBy(c => c.NotAfter))
{
if (cert.Thumbprint != endEntity.Thumbprint)
{
sb.AppendLine($"# Intermediate: {cert.Subject}");
sb.AppendLine(ctx.ToPem(cert));
}
}
File.WriteAllText(outputPath, sb.ToString());
Console.WriteLine($"Chain exported: {outputPath}");
}
public X509Certificate2Collection ImportChainFromPem(string pemPath)
{
using var ctx = PqCryptoContext.Initialize();
var pemContent = File.ReadAllText(pemPath);
var collection = new X509Certificate2Collection();
// Extract PEM blocks
var certPattern = @"-----BEGIN CERTIFICATE-----(.*?)-----END CERTIFICATE-----";
var matches = Regex.Matches(pemContent, certPattern, RegexOptions.Singleline);
foreach (Match match in matches)
{
var base64 = match.Groups[1].Value.Trim();
var certBytes = Convert.FromBase64String(base64);
collection.Add(new X509Certificate2(certBytes));
}
Console.WriteLine($"{collection.Count} certificates imported");
return collection;
}
}
----
===== PEM Conversion with OpenSSL =====
# Convert DER to PEM
openssl x509 -in cert.der -inform DER -out cert.pem -outform PEM
# Convert PEM to DER
openssl x509 -in cert.pem -inform PEM -out cert.der -outform DER
# Inspect PEM certificate
openssl x509 -in cert.pem -text -noout
# Encrypt private key
openssl pkcs8 -topk8 -in key.pem -out key-encrypted.pem -aes256
# Decrypt private key
openssl pkcs8 -in key-encrypted.pem -out key.pem
----
===== Industry-Specific PEM Usage =====
^ System ^ PEM Format ^ Specifics ^
| **Apache** | Separate .crt and .key | SSLCertificateFile, SSLCertificateKeyFile |
| **Nginx** | Chain in one .pem | ssl_certificate (fullchain) |
| **HAProxy** | Cert + key in one file | bind ... ssl crt combined.pem |
| **OpenSSL** | All formats | Standard tool |
| **Kubernetes** | Base64-encoded in secrets | kubectl create secret tls |
----
===== Related Scenarios =====
^ Relationship ^ Scenario ^ Description ^
| **Alternative** | [[.:pfx_export|12.2 PFX Export]] | Windows format |
| **Related** | [[.:pkcs7_chain|12.3 PKCS#7 Chain]] | Certificates only |
| **Related** | [[en:int:pqcrypt:szenarien:schluessel:speicherung|11.2 Key Storage]] | Secure storage |
----
<< [[.:start|<- Import/Export Overview]] | [[.:start|^ Import/Export]] | [[.:pfx_export|12.2 PFX Export ->]] >>
{{tag>scenario import export pem base64 openssl}}
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//