Szenario 12.3: PKCS#7 Chain Export

Kategorie: Import/Export
Komplexität: ⭐⭐ (Niedrig)
Voraussetzungen: Zertifikatskette
Geschätzte Zeit: 10-15 Minuten


Beschreibung

Dieses Szenario beschreibt den Export und Import von Zertifikatsketten im PKCS#7-Format. PKCS#7 (auch CMS - Cryptographic Message Syntax) ist ideal für die Verteilung von Zertifikatsketten ohne Private Keys.

PKCS#7 Eigenschaften:


Workflow

flowchart TD subgraph Input ROOT[Root-CA] INT[Intermediate-CA] EE[End-Entity] end subgraph PKCS7["PKCS#7 Container"] CERTS[SignedData.Certificates] end ROOT --> CERTS INT --> CERTS EE --> CERTS PKCS7 --> DER[.p7b Binary] PKCS7 --> PEM[.p7b PEM]


Code-Beispiel: Chain als PKCS#7 exportieren

using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
 
public class Pkcs7ChainExporter
{
    public byte[] ExportChain(X509Certificate2Collection certificates)
    {
        // SignedCms ohne Signatur (nur Zertifikate)
        var content = new ContentInfo(Array.Empty<byte>());
        var signedCms = new SignedCms(content, detached: true);
 
        // Zertifikate hinzufügen
        foreach (var cert in certificates)
        {
            signedCms.Certificates.Add(cert);
        }
 
        // Als PKCS#7 (DER) exportieren
        return signedCms.Encode();
    }
 
    public void ExportToFile(
        X509Certificate2Collection certificates,
        string outputPath,
        bool asPem = false)
    {
        var p7bBytes = ExportChain(certificates);
 
        if (asPem)
        {
            // PEM-Format
            var pem = new StringBuilder();
            pem.AppendLine("-----BEGIN PKCS7-----");
            pem.AppendLine(Convert.ToBase64String(p7bBytes, Base64FormattingOptions.InsertLineBreaks));
            pem.AppendLine("-----END PKCS7-----");
            File.WriteAllText(outputPath, pem.ToString());
        }
        else
        {
            // Binary (DER)
            File.WriteAllBytes(outputPath, p7bBytes);
        }
 
        Console.WriteLine($"PKCS#7 exportiert: {outputPath} ({certificates.Count} Zertifikate)");
    }
}

Code-Beispiel: Vollständige Chain erstellen und exportieren

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
 
public class FullChainExporter
{
    public void ExportFullChain(
        X509Certificate2 endEntity,
        string outputPath)
    {
        using var ctx = PqCryptoContext.Initialize();
 
        // Chain bauen
        using var chain = new X509Chain();
        chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
        chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;
        chain.Build(endEntity);
 
        // Alle Zertifikate sammeln
        var collection = new X509Certificate2Collection();
        collection.Add(endEntity);
 
        foreach (var element in chain.ChainElements)
        {
            if (element.Certificate.Thumbprint != endEntity.Thumbprint)
            {
                collection.Add(element.Certificate);
            }
        }
 
        // Als PKCS#7 exportieren
        var exporter = new Pkcs7ChainExporter();
        exporter.ExportToFile(collection, outputPath);
 
        // Details ausgeben
        Console.WriteLine("Chain exportiert:");
        foreach (var cert in collection)
        {
            Console.WriteLine($"  [{cert.Thumbprint.Substring(0, 8)}] {cert.Subject}");
        }
    }
}

Code-Beispiel: PKCS#7 importieren

public class Pkcs7ChainImporter
{
    public X509Certificate2Collection ImportChain(byte[] p7bBytes)
    {
        var collection = new X509Certificate2Collection();
        collection.Import(p7bBytes);
 
        Console.WriteLine($"{collection.Count} Zertifikate importiert");
        return collection;
    }
 
    public X509Certificate2Collection ImportFromFile(string filePath)
    {
        byte[] data;
 
        var content = File.ReadAllText(filePath);
        if (content.Contains("-----BEGIN PKCS7-----"))
        {
            // PEM-Format
            var base64 = Regex.Match(
                content,
                @"-----BEGIN PKCS7-----(.*?)-----END PKCS7-----",
                RegexOptions.Singleline
            ).Groups[1].Value.Trim();
 
            data = Convert.FromBase64String(base64);
        }
        else
        {
            // Binary (DER)
            data = File.ReadAllBytes(filePath);
        }
 
        return ImportChain(data);
    }
 
    public void InstallChainToStore(
        string p7bPath,
        StoreName storeName = StoreName.CertificateAuthority,
        StoreLocation location = StoreLocation.LocalMachine)
    {
        var certs = ImportFromFile(p7bPath);
 
        using var store = new X509Store(storeName, location);
        store.Open(OpenFlags.ReadWrite);
 
        foreach (var cert in certs)
        {
            // Prüfen ob selbstsigniert (Root)
            var targetStore = cert.Subject == cert.Issuer
                ? new X509Store(StoreName.Root, location)
                : store;
 
            if (targetStore.Name != store.Name)
            {
                targetStore.Open(OpenFlags.ReadWrite);
            }
 
            // Prüfen ob bereits vorhanden
            var existing = targetStore.Certificates.Find(
                X509FindType.FindByThumbprint,
                cert.Thumbprint,
                validOnly: false
            );
 
            if (existing.Count == 0)
            {
                targetStore.Add(cert);
                Console.WriteLine($"Installiert: {cert.Subject}");
            }
 
            if (targetStore.Name != store.Name)
            {
                targetStore.Close();
            }
        }
 
        store.Close();
    }
}

Code-Beispiel: CA Bundle für Webserver

public class CaBundleCreator
{
    public void CreateCaBundle(
        X509Certificate2 rootCa,
        X509Certificate2Collection intermediateCas,
        string outputPath)
    {
        var collection = new X509Certificate2Collection();
 
        // Intermediates zuerst (nächste zum Server)
        foreach (var intermediate in intermediateCas.OrderByDescending(c => c.NotAfter))
        {
            collection.Add(intermediate);
        }
 
        // Root zuletzt
        collection.Add(rootCa);
 
        var exporter = new Pkcs7ChainExporter();
        exporter.ExportToFile(collection, outputPath);
 
        Console.WriteLine("CA Bundle erstellt:");
        Console.WriteLine($"  Intermediates: {intermediateCas.Count}");
        Console.WriteLine($"  Root: {rootCa.Subject}");
    }
 
    public void CreateNginxCaBundle(
        X509Certificate2 serverCert,
        X509Certificate2Collection chain,
        string outputPath)
    {
        // Nginx erwartet PEM-Chain (Server zuerst, dann Intermediates)
        var sb = new StringBuilder();
 
        // Server-Zertifikat
        sb.AppendLine(ToPem(serverCert));
 
        // Intermediates (nicht Root!)
        foreach (var cert in chain.Where(c => c.Subject != c.Issuer))
        {
            sb.AppendLine(ToPem(cert));
        }
 
        File.WriteAllText(outputPath, sb.ToString());
        Console.WriteLine($"Nginx CA Bundle: {outputPath}");
    }
 
    private string ToPem(X509Certificate2 cert)
    {
        var sb = new StringBuilder();
        sb.AppendLine("-----BEGIN CERTIFICATE-----");
        sb.AppendLine(Convert.ToBase64String(cert.RawData, Base64FormattingOptions.InsertLineBreaks));
        sb.AppendLine("-----END CERTIFICATE-----");
        return sb.ToString();
    }
}

PKCS#7 mit OpenSSL

# PKCS#7 aus mehreren Zertifikaten erstellen
openssl crl2pkcs7 -nocrl \
    -certfile root-ca.pem \
    -certfile intermediate-ca.pem \
    -certfile server.pem \
    -out chain.p7b \
    -outform DER
 
# PKCS#7 als PEM
openssl crl2pkcs7 -nocrl \
    -certfile chain.pem \
    -out chain.p7b \
    -outform PEM
 
# PKCS#7 inspizieren
openssl pkcs7 -in chain.p7b -print_certs -noout
 
# Zertifikate aus PKCS#7 extrahieren
openssl pkcs7 -in chain.p7b -print_certs -out extracted.pem
 
# PKCS#7 Informationen
openssl pkcs7 -in chain.p7b -inform DER -text

Verwendung in verschiedenen Systemen

System PKCS#7-Verwendung Format
Windows Intermediate-CA Store .p7b (DER)
IIS SSL Certificate Chain .p7b
Java Trust Store Import .p7b (DER)
S/MIME E-Mail Verschlüsselung Teil der Nachricht
Code Signing Timestamp + Chain Eingebettet

Verwandte Szenarien

Beziehung Szenario Beschreibung
Alternative 12.1 PEM Export Chain als PEM
Verwandt 12.2 PFX Export Mit Private Key
Voraussetzung 1.3 CA-Hierarchie Chain aufbauen
Verwandt 5.1 Chain Building Chain validieren

« ← 12.2 PFX Export | ↑ Import/Export | 12.4 Interoperabilität → »


Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional