Scenario 12.3: PKCS#7 Chain Export
Category: Import/Export
Complexity: (Low)
Prerequisites: Certificate chain
Estimated Time: 10-15 minutes
</WRAP>
—-
===== Description =====
This scenario describes export and import of certificate chains in PKCS#7 format. PKCS#7 (also CMS - Cryptographic Message Syntax) is ideal for distributing certificate chains without private keys.
PKCS#7 Properties:
* Content: Certificates only (no private keys!)
* Usage: Chain distribution, S/MIME
* Extensions: .p7b, .p7c
* Encoding: DER (Binary) or PEM (Base64)
—-
===== Workflow =====
<mermaid>
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]
</mermaid>
—-
===== Code Example: Export Chain as PKCS#7 =====
<code csharp>
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
public class Pkcs7ChainExporter
{
public byte[] ExportChain(X509Certificate2Collection certificates)
{
SignedCms without signature (certificates only)
var content = new ContentInfo(Array.Empty<byte>());
var signedCms = new SignedCms(content, detached: true);
Add certificates
foreach (var cert in certificates)
{
signedCms.Certificates.Add(cert);
}
Export as 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 exported: {outputPath} ({certificates.Count} certificates)“);
}
}
</code>
—-
===== Code Example: Create and Export Full Chain =====
<code csharp>
using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
public class FullChainExporter
{
public void ExportFullChain(
X509Certificate2 endEntity,
string outputPath)
{
using var ctx = PqCryptoContext.Initialize();
Build chain
using var chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;
chain.Build(endEntity);
Collect all certificates
var collection = new X509Certificate2Collection();
collection.Add(endEntity);
foreach (var element in chain.ChainElements)
{
if (element.Certificate.Thumbprint != endEntity.Thumbprint)
{
collection.Add(element.Certificate);
}
}
Export as PKCS#7
var exporter = new Pkcs7ChainExporter();
exporter.ExportToFile(collection, outputPath);
Output details
Console.WriteLine(„Chain exported:“);
foreach (var cert in collection)
{
Console.WriteLine($„ [{cert.Thumbprint.Substring(0, 8)}] {cert.Subject}“);
}
}
}
</code>
—-
===== Code Example: Import PKCS#7 =====
<code csharp>
public class Pkcs7ChainImporter
{
public X509Certificate2Collection ImportChain(byte[] p7bBytes)
{
var collection = new X509Certificate2Collection();
collection.Import(p7bBytes);
Console.WriteLine($„{collection.Count} certificates imported“);
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)
{
Check if self-signed (Root)
var targetStore = cert.Subject == cert.Issuer
? new X509Store(StoreName.Root, location)
: store;
if (targetStore.Name != store.Name)
{
targetStore.Open(OpenFlags.ReadWrite);
}
Check if already present
var existing = targetStore.Certificates.Find(
X509FindType.FindByThumbprint,
cert.Thumbprint,
validOnly: false
);
if (existing.Count == 0)
{
targetStore.Add(cert);
Console.WriteLine($„Installed: {cert.Subject}“);
}
if (targetStore.Name != store.Name)
{
targetStore.Close();
}
}
store.Close();
}
}
</code>
—-
===== Code Example: CA Bundle for Webserver =====
<code csharp>
public class CaBundleCreator
{
public void CreateCaBundle(
X509Certificate2 rootCa,
X509Certificate2Collection intermediateCas,
string outputPath)
{
var collection = new X509Certificate2Collection();
Intermediates first (closest to server)
foreach (var intermediate in intermediateCas.OrderByDescending(c ⇒ c.NotAfter))
{
collection.Add(intermediate);
}
Root last
collection.Add(rootCa);
var exporter = new Pkcs7ChainExporter();
exporter.ExportToFile(collection, outputPath);
Console.WriteLine(„CA Bundle created:“);
Console.WriteLine($„ Intermediates: {intermediateCas.Count}“);
Console.WriteLine($„ Root: {rootCa.Subject}“);
}
public void CreateNginxCaBundle(
X509Certificate2 serverCert,
X509Certificate2Collection chain,
string outputPath)
{
Nginx expects PEM chain (server first, then intermediates)
var sb = new StringBuilder();
Server certificate
sb.AppendLine(ToPem(serverCert));
Intermediates (not 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();
}
}
</code>
—-
===== PKCS#7 with OpenSSL =====
<code bash>
# Create PKCS#7 from multiple certificates
openssl crl2pkcs7 -nocrl \
-certfile root-ca.pem \
-certfile intermediate-ca.pem \
-certfile server.pem \
-out chain.p7b \
-outform DER
# PKCS#7 as PEM
openssl crl2pkcs7 -nocrl \
-certfile chain.pem \
-out chain.p7b \
-outform PEM
# Inspect PKCS#7
openssl pkcs7 -in chain.p7b -print_certs -noout
# Extract certificates from PKCS#7
openssl pkcs7 -in chain.p7b -print_certs -out extracted.pem
# PKCS#7 information
openssl pkcs7 -in chain.p7b -inform DER -text
</code>
—-
===== Usage in Various Systems =====
^ System ^ PKCS#7 Usage ^ Format ^
| Windows | Intermediate CA Store | .p7b (DER) |
| IIS | SSL Certificate Chain | .p7b |
| Java | Trust Store Import | .p7b (DER) |
| S/MIME | Email Encryption | Part of message |
| Code Signing | Timestamp + Chain | Embedded |
—-
===== Related Scenarios =====
^ Relationship ^ Scenario ^ Description ^
| Alternative | 12.1 PEM Export | Chain as PEM |
| Related | 12.2 PFX Export | With Private Key |
| Prerequisite | 1.3 CA Hierarchy | Build chain |
| Related** | 5.1 Chain Building | Validate chain |
« <- 12.2 PFX Export | ^ Import/Export | 12.4 Interoperability -> »
Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional