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