~~NOTOC~~
====== Szenario 12.3: PKCS#7 Chain Export ======
**Kategorie:** [[.:start|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:**
* **Inhalt:** Nur Zertifikate (keine Private Keys!)
* **Verwendung:** Chain-Distribution, S/MIME
* **Erweiterungen:** .p7b, .p7c
* **Encoding:** DER (Binary) oder PEM (Base64)
----
===== 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());
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** | [[.:pem_export|12.1 PEM Export]] | Chain als PEM |
| **Verwandt** | [[.:pfx_export|12.2 PFX Export]] | Mit Private Key |
| **Voraussetzung** | [[de:int:pqcrypt:szenarien:pki:ca_hierarchie|1.3 CA-Hierarchie]] | Chain aufbauen |
| **Verwandt** | [[de:int:pqcrypt:szenarien:validierung:chain_building|5.1 Chain Building]] | Chain validieren |
----
<< [[.:pfx_export|← 12.2 PFX Export]] | [[.:start|↑ Import/Export]] | [[.:interop|12.4 Interoperabilität →]] >>
{{tag>szenario import export pkcs7 chain p7b}}
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//