Scenarij 12.3: PKCS#7 izvoz lanca

Kategorija: Uvoz/Izvoz
Složenost: ⭐⭐ (Niska)
Preduvjeti: Lanac certifikata
Procijenjeno vrijeme: 10-15 minuta


Opis

Ovaj scenarij opisuje izvoz i uvoz lanaca certifikata u PKCS#7 formatu. PKCS#7 (također CMS - Cryptographic Message Syntax) idealan je za distribuciju lanaca certifikata bez privatnih ključeva.

PKCS#7 svojstva:


Tijek rada

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


Primjer koda: Izvoz lanca kao PKCS#7

using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
 
public class Pkcs7ChainExporter
{
    public byte[] ExportChain(X509Certificate2Collection certificates)
    {
        // SignedCms bez potpisa (samo certifikati)
        var content = new ContentInfo(Array.Empty<byte>());
        var signedCms = new SignedCms(content, detached: true);
 
        // Dodavanje certifikata
        foreach (var cert in certificates)
        {
            signedCms.Certificates.Add(cert);
        }
 
        // Izvoz kao PKCS#7 (DER)
        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 izvezen: {outputPath} ({certificates.Count} certifikata)");
    }
}

Primjer koda: Kreiranje i izvoz potpunog lanca

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
 
public class FullChainExporter
{
    public void ExportFullChain(
        X509Certificate2 endEntity,
        string outputPath)
    {
        using var ctx = PqCryptoContext.Initialize();
 
        // Izgradnja lanca
        using var chain = new X509Chain();
        chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
        chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;
        chain.Build(endEntity);
 
        // Prikupljanje svih certifikata
        var collection = new X509Certificate2Collection();
        collection.Add(endEntity);
 
        foreach (var element in chain.ChainElements)
        {
            if (element.Certificate.Thumbprint != endEntity.Thumbprint)
            {
                collection.Add(element.Certificate);
            }
        }
 
        // Izvoz kao PKCS#7
        var exporter = new Pkcs7ChainExporter();
        exporter.ExportToFile(collection, outputPath);
 
        // Ispis detalja
        Console.WriteLine("Lanac izvezen:");
        foreach (var cert in collection)
        {
            Console.WriteLine($"  [{cert.Thumbprint.Substring(0, 8)}] {cert.Subject}");
        }
    }
}

Primjer koda: Uvoz PKCS#7

public class Pkcs7ChainImporter
{
    public X509Certificate2Collection ImportChain(byte[] p7bBytes)
    {
        var collection = new X509Certificate2Collection();
        collection.Import(p7bBytes);
 
        Console.WriteLine($"{collection.Count} certifikata uvezeno");
        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)
        {
            // Provjera je li samopotpisan (Root)
            var targetStore = cert.Subject == cert.Issuer
                ? new X509Store(StoreName.Root, location)
                : store;
 
            if (targetStore.Name != store.Name)
            {
                targetStore.Open(OpenFlags.ReadWrite);
            }
 
            // Provjera postoji li već
            var existing = targetStore.Certificates.Find(
                X509FindType.FindByThumbprint,
                cert.Thumbprint,
                validOnly: false
            );
 
            if (existing.Count == 0)
            {
                targetStore.Add(cert);
                Console.WriteLine($"Instalirano: {cert.Subject}");
            }
 
            if (targetStore.Name != store.Name)
            {
                targetStore.Close();
            }
        }
 
        store.Close();
    }
}

Primjer koda: CA Bundle za web server

public class CaBundleCreator
{
    public void CreateCaBundle(
        X509Certificate2 rootCa,
        X509Certificate2Collection intermediateCas,
        string outputPath)
    {
        var collection = new X509Certificate2Collection();
 
        // Intermediates prvo (najbliži serveru)
        foreach (var intermediate in intermediateCas.OrderByDescending(c => c.NotAfter))
        {
            collection.Add(intermediate);
        }
 
        // Root zadnji
        collection.Add(rootCa);
 
        var exporter = new Pkcs7ChainExporter();
        exporter.ExportToFile(collection, outputPath);
 
        Console.WriteLine("CA Bundle kreiran:");
        Console.WriteLine($"  Intermediates: {intermediateCas.Count}");
        Console.WriteLine($"  Root: {rootCa.Subject}");
    }
 
    public void CreateNginxCaBundle(
        X509Certificate2 serverCert,
        X509Certificate2Collection chain,
        string outputPath)
    {
        // Nginx očekuje PEM lanac (server prvo, zatim intermediates)
        var sb = new StringBuilder();
 
        // Serverski certifikat
        sb.AppendLine(ToPem(serverCert));
 
        // Intermediates (ne 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 s OpenSSL

# Kreiranje PKCS#7 iz više certifikata
openssl crl2pkcs7 -nocrl \
    -certfile root-ca.pem \
    -certfile intermediate-ca.pem \
    -certfile server.pem \
    -out chain.p7b \
    -outform DER
 
# PKCS#7 kao PEM
openssl crl2pkcs7 -nocrl \
    -certfile chain.pem \
    -out chain.p7b \
    -outform PEM
 
# Pregled PKCS#7
openssl pkcs7 -in chain.p7b -print_certs -noout
 
# Ekstrakcija certifikata iz PKCS#7
openssl pkcs7 -in chain.p7b -print_certs -out extracted.pem
 
# PKCS#7 informacije
openssl pkcs7 -in chain.p7b -inform DER -text

Uporaba u različitim sustavima

Sustav PKCS#7 uporaba Format
Windows Intermediate-CA Store .p7b (DER)
IIS SSL Certificate Chain .p7b
Java Trust Store Import .p7b (DER)
S/MIME E-mail šifriranje Dio poruke
Code Signing Timestamp + Chain Ugrađeno

Povezani scenariji

Povezanost Scenarij Opis
Alternativa 12.1 PEM izvoz Lanac kao PEM
Povezano 12.2 PFX izvoz S privatnim ključem
Preduvjet 1.3 CA hijerarhija Izgradnja lanca
Povezano 5.1 Izgradnja lanca Validacija lanca

« ← 12.2 PFX izvoz | ↑ Uvoz/Izvoz | 12.4 Interoperabilnost → »


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