Topic · Cookbook

PKI Governance Cookbook

Sechs Production-Rezepte: Root-CA + Tier-Hierarchie, Cert-Rotation ohne Service-Interruption, CRL-Publishing-Pipeline, OCSP-Stapling-Setup, HSM-/Smart-Card-Integration, Multi-Tenant-CA-Trennung. Trait-Surface + Code-Pfade in der PKI Reference.

6 Rezepte · Cert-Lifecycle Spec: DDS-Security 1.2 §9.3 + §10 Crate: zerodds-security-pki

Vorab — PKI-Aufbau in 3 Stufen

Für Production-DDS-Setups mit DDS-Security 1.2 brauchst du drei Komponenten:

  1. Trust-Anker — eine oder mehrere CA-Certs, die in jedem Participant als „akzeptierte Wurzel" geladen werden. Selbst-signiert (Closed-Network) oder von einer öffentlichen CA.
  2. Participant-Certs — pro DDS-Participant ein eigenes Cert (Subject-Name = Identity), unter dem Trust-Anker signiert.
  3. Revocation-Quelle — CRL (periodische Liste) oder OCSP (Live-Lookup) für widerrufene Certs.

Reference statt Rezept? Die PKI Reference dokumentiert das Identity-/Handshake-/Revocation-Protokoll Spec-Anker-für-Spec-Anker, plus PSK-Fallback und Multi-Tier-Delegation.

1 · Root-CA + Tier-Hierarchie

Wann: du baust eine ZeroDDS-Production-Infrastruktur und brauchst eine eigene CA-Hierarchie. Pattern: Offline Root-CA (selten genutzt, gut gesichert) → Online Issuing-CA (signiert Participant-Certs).

# 1) Offline Root-CA — 10 Jahre, RSA-4096, danach Cold-Storage
openssl req -x509 -newkey rsa:4096 -keyout root-ca-key.pem -out root-ca.pem \
    -sha256 -days 3650 -nodes \
    -subj "/CN=Acme ZeroDDS Root CA/O=Acme Corp/C=DE"

# 2) Issuing-CA — 2 Jahre, ECDSA-P-256 (schneller im Handshake)
openssl ecparam -name prime256v1 -genkey -noout -out issuing-ca-key.pem
openssl req -new -key issuing-ca-key.pem -out issuing-ca.csr \
    -subj "/CN=Acme ZeroDDS Issuing CA 2026/O=Acme Corp/C=DE"
openssl x509 -req -in issuing-ca.csr \
    -CA root-ca.pem -CAkey root-ca-key.pem -CAcreateserial \
    -out issuing-ca.pem -days 730 -sha256 \
    -extfile <(echo "basicConstraints=critical,CA:TRUE,pathlen:0")

# 3) Bundle: Issuing-CA + Root-CA als ein PEM (Trust-Anker)
cat issuing-ca.pem root-ca.pem > trust-bundle.pem

ZeroDDS-Config (jeder Participant): Property dds.sec.auth.identity_ca = file:/etc/zerodds/trust-bundle.pem

Trade-Off: Offline Root reduziert das Risiko bei Kompromittierung der Online-CA — Issuing-CA kann revoked werden, ohne dass alle Participant-Certs neu ausgestellt werden müssen. Aber: Offline-Root-Key braucht Cold-Storage (Tresor, HSM offline). Verlorener Root-Key = ganze Infrastruktur kompromittiert.

2 · Cert-Rotation ohne Downtime

Wann: Participant-Certs laufen typisch 1 Jahr. Ohne automatische Rotation kommt Downtime, sobald das Cert abläuft.

Pattern (Rolling-Reissue):

  1. ~30 Tage vor Ablauf: neues Cert vom Issuing-CA holen (selbe Subject-Name, neuer Serial + neue Validity).
  2. Per Participant: neues Cert + neuer Key ins Filesystem schreiben (part-cert.new.pem, part-key.new.pem).
  3. Participant-Restart mit neuen Pfaden — bestehende DDS-Connections re-handshake'n automatisch (SEDP triggert Re-Auth nach Identity-Change).
# Beispiel-cron (täglich, prüft Cert-Validity):
#!/bin/bash
EXPIRY=$(openssl x509 -enddate -noout -in /etc/zerodds/part-cert.pem | cut -d= -f2)
EXPIRY_TS=$(date -d "$EXPIRY" +%s)
NOW=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_TS - NOW) / 86400 ))

if [ $DAYS_LEFT -lt 30 ]; then
    /usr/local/bin/request-new-cert.sh
    systemctl reload zerodds-app    # graceful restart
fi

Trade-Off: Reload macht Re-Discovery (~3-10s). Für 24/7-Production-Setups: zweistufige Rotation (zwei aktive Certs parallel, alte erst nach 7 Tagen entfernen) — braucht eigene Property-Erweiterung, Roadmap.

3 · CRL-Publishing-Pipeline

Wann: du musst widerrufene Certs schnell aus dem Trust-Pool entfernen (z.B. kompromittierter Worker-Host, ausgeschiedener Mitarbeiter).

# 1) Cert revoken (Issuing-CA-Operator)
openssl ca -config openssl-ca.cnf -revoke part-cert-X.pem \
    -keyfile issuing-ca-key.pem -cert issuing-ca.pem

# 2) Neue CRL generieren
openssl ca -config openssl-ca.cnf -gencrl \
    -keyfile issuing-ca-key.pem -cert issuing-ca.pem \
    -out crl.pem

# 3) CRL an alle Participant-Hosts deployen (per Ansible / rsync)
ansible-playbook -i hosts deploy-crl.yml

ZeroDDS-Config: Property dds.sec.auth.identity_crl = file:/etc/zerodds/crl.pem

Update-Frequenz: CRL hat eine nextUpdate-Frist (Default: 30 Tage). Bei häufigeren Revocations: täglich neu generieren und deployen. Für Real-Time-Revocation → OCSP (Rezept 4).

Limitierung: ZeroDDS reload CRL erst bei Participant-Start. Live-Reload ist Roadmap.

4 · OCSP-Stapling-Setup

Wann: große Deployments (10k+ Certs), Real-Time-Revocation-Reaktion erforderlich. CRL würde zu groß / zu langsam.

# 1) OCSP-Responder aufsetzen (auf einem CA-Host)
openssl ocsp -port 8888 -text \
    -CA issuing-ca.pem \
    -rkey ocsp-key.pem -rsigner ocsp-cert.pem \
    -index ca-index.txt &

# 2) Pro Participant-Cert: aktuelle OCSP-Response abholen
openssl ocsp -url http://ca.acme.internal:8888 \
    -CAfile trust-bundle.pem \
    -issuer issuing-ca.pem \
    -cert part-cert.pem \
    -respout part-ocsp.der

# 3) ZeroDDS-Config: OCSP-Stapling-Response als IdentityStatusToken
# Property dds.sec.auth.identity_status_token = file:/etc/zerodds/part-ocsp.der

Refresh-Cron: OCSP-Responses sind kurz gültig (typisch 7 Tage). Refresh-Job alle 1-3 Tage.

Trade-Off: OCSP-Stapling braucht Responder-Infrastruktur + Refresh-Job pro Participant. Dafür: instantane Revocation-Wirkung sobald nächste Stapling-Response abgelehnt wird. Implementation: crates/security-pki/src/ocsp.rs.

5 · HSM / Smart-Card-Integration

Wann: Compliance-Pflicht (CRA, ISO 27001, FIPS-140) verlangt, dass Private-Keys nie das HSM verlassen. Embedded-Devices mit Smart-Card (z.B. YubiKey 5, IDEMIA).

Status: ZeroDDS-Default-Plugin liest Private-Keys vom Filesystem. PKCS#11-Bridge ist Roadmap (v1.1) — geplant via cryptoki als Custom-AuthenticationPlugin.

Übergangslösung — Linux mit OpenSSL-Engine-PKCS11:

# 1) Engine + Token-Lib installieren
sudo apt install opensc libengine-pkcs11-openssl

# 2) Cert-Signing-Request mit HSM-Key
openssl req -new -engine pkcs11 -keyform engine \
    -key "pkcs11:object=zerodds-key;type=private" \
    -out csr.pem -subj "/CN=robot-arm-7/O=Acme/C=DE"

# 3) Sign via Issuing-CA — normal weiter
# 4) ZeroDDS bekommt Cert + die URL des HSM-Keys statt File-Path
# (Custom-Plugin notwendig — heute nicht out-of-the-box)

Custom-AuthenticationPlugin schreiben: implementiere AuthenticationPlugin-Trait, ersetz validate_local_identity mit einem PKCS#11-Sign-Call. Engagement-Modell: Custom-Development.

6 · Multi-Tenant-CA-Trennung

Wann: mehrere Mandanten teilen sich eine ZeroDDS-Infrastruktur (z.B. SaaS-DDS-Cloud). Pro Tenant eigene CA, kein Cross-Tenant-Trust.

Pattern: Tier-CAs unter dem Root

Root CA (Plattform-Operator, offline)
├── Tenant-A Issuing CA
│   ├── tenant-a-participant-1
│   └── tenant-a-participant-2
└── Tenant-B Issuing CA
    ├── tenant-b-participant-1
    └── tenant-b-participant-2

Pro Tenant-Participant: Trust-Anker enthält NUR die eigene Tenant-CA, nicht die Root (sonst würde der Participant Cross-Tenant-Certs akzeptieren).

# Tenant-A-Participant trust-bundle:
cat tenant-a-issuing.pem > tenant-a-trust.pem
# NICHT root-ca.pem dazu!
# dds.sec.auth.identity_ca = file:/etc/zerodds/tenant-a-trust.pem

Zusätzlich Topic-Isolation: via Partition-QoS — Tenant-A nutzt Partition tenant-a/*, Tenant-B nutzt tenant-b/*. Doppelte Isolation: Auth (kein Cert-Match) + AccessControl-Permissions (kein Topic-Match).

Trade-Off: mehr CA-Operations-Overhead. Aber: viel sauberer als Permissions-XML-Mandantentrennung allein — bei kompromittiertem Tenant-Operator bleibt der Cross-Tenant-Pfad zu, weil das Cert nicht vertraut wird.

Topic · Cookbook

PKI Governance Cookbook

Six production recipes: root CA + tier hierarchy, cert rotation without service interruption, a CRL publishing pipeline, OCSP stapling setup, HSM/smart-card integration, multi-tenant CA separation. Trait surface + code paths in the PKI Reference.

6 recipes · cert lifecycle Spec: DDS-Security 1.2 §9.3 + §10 Crate: zerodds-security-pki

First — PKI in 3 stages

For production DDS setups with DDS-Security 1.2 you need three components:

  1. Trust anchor — one or more CA certs loaded into every participant as the "accepted root". Self-signed (closed network) or from a public CA.
  2. Participant certs — one cert per DDS participant (subject name = identity), signed under the trust anchor.
  3. Revocation source — CRL (periodic list) or OCSP (live lookup) for revoked certs.

Reference instead of recipe? The PKI Reference documents the identity/handshake/revocation protocol spec-anchor by spec-anchor, plus the PSK fallback and multi-tier delegation.

1 · Root CA + tier hierarchy

When: you're building a ZeroDDS production infrastructure and need your own CA hierarchy. Pattern: an offline root CA (rarely used, well secured) → an online issuing CA (signs participant certs).

# 1) offline root CA — 10 years, RSA-4096, then cold storage
openssl req -x509 -newkey rsa:4096 -keyout root-ca-key.pem -out root-ca.pem \
    -sha256 -days 3650 -nodes \
    -subj "/CN=Acme ZeroDDS Root CA/O=Acme Corp/C=DE"

# 2) issuing CA — 2 years, ECDSA-P-256 (faster in the handshake)
openssl ecparam -name prime256v1 -genkey -noout -out issuing-ca-key.pem
openssl req -new -key issuing-ca-key.pem -out issuing-ca.csr \
    -subj "/CN=Acme ZeroDDS Issuing CA 2026/O=Acme Corp/C=DE"
openssl x509 -req -in issuing-ca.csr \
    -CA root-ca.pem -CAkey root-ca-key.pem -CAcreateserial \
    -out issuing-ca.pem -days 730 -sha256 \
    -extfile <(echo "basicConstraints=critical,CA:TRUE,pathlen:0")

# 3) bundle: issuing CA + root CA as one PEM (trust anchor)
cat issuing-ca.pem root-ca.pem > trust-bundle.pem

ZeroDDS config (every participant): property dds.sec.auth.identity_ca = file:/etc/zerodds/trust-bundle.pem

Trade-off: an offline root reduces the risk if the online CA is compromised — the issuing CA can be revoked without re-issuing all participant certs. But: the offline root key needs cold storage (vault, offline HSM). A lost root key = the whole infrastructure compromised.

2 · Cert rotation without downtime

When: participant certs typically last 1 year. Without automatic rotation you get downtime the moment a cert expires.

Pattern (rolling reissue):

  1. ~30 days before expiry: fetch a new cert from the issuing CA (same subject name, new serial + new validity).
  2. Per participant: write the new cert + new key to the filesystem (part-cert.new.pem, part-key.new.pem).
  3. Participant restart with the new paths — existing DDS connections re-handshake automatically (SEDP triggers re-auth after an identity change).
# example cron (daily, checks cert validity):
#!/bin/bash
EXPIRY=$(openssl x509 -enddate -noout -in /etc/zerodds/part-cert.pem | cut -d= -f2)
EXPIRY_TS=$(date -d "$EXPIRY" +%s)
NOW=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_TS - NOW) / 86400 ))

if [ $DAYS_LEFT -lt 30 ]; then
    /usr/local/bin/request-new-cert.sh
    systemctl reload zerodds-app    # graceful restart
fi

Trade-off: a reload triggers re-discovery (~3-10s). For 24/7 production setups: two-stage rotation (two active certs in parallel, remove the old one only after 7 days) — needs a dedicated property extension, on the roadmap.

3 · CRL publishing pipeline

When: you have to remove revoked certs from the trust pool quickly (e.g. a compromised worker host, a departed employee).

# 1) revoke a cert (issuing-CA operator)
openssl ca -config openssl-ca.cnf -revoke part-cert-X.pem \
    -keyfile issuing-ca-key.pem -cert issuing-ca.pem

# 2) generate a new CRL
openssl ca -config openssl-ca.cnf -gencrl \
    -keyfile issuing-ca-key.pem -cert issuing-ca.pem \
    -out crl.pem

# 3) deploy the CRL to all participant hosts (via Ansible / rsync)
ansible-playbook -i hosts deploy-crl.yml

ZeroDDS config: property dds.sec.auth.identity_crl = file:/etc/zerodds/crl.pem

Update frequency: a CRL has a nextUpdate deadline (default: 30 days). For more frequent revocations: regenerate and deploy daily. For real-time revocation → OCSP (recipe 4).

Limitation: ZeroDDS reloads the CRL only at participant start. Live reload is on the roadmap.

4 · OCSP stapling setup

When: large deployments (10k+ certs), real-time revocation reaction required. A CRL would be too big / too slow.

# 1) set up an OCSP responder (on a CA host)
openssl ocsp -port 8888 -text \
    -CA issuing-ca.pem \
    -rkey ocsp-key.pem -rsigner ocsp-cert.pem \
    -index ca-index.txt &

# 2) per participant cert: fetch the current OCSP response
openssl ocsp -url http://ca.acme.internal:8888 \
    -CAfile trust-bundle.pem \
    -issuer issuing-ca.pem \
    -cert part-cert.pem \
    -respout part-ocsp.der

# 3) ZeroDDS config: OCSP stapling response as the IdentityStatusToken
# property dds.sec.auth.identity_status_token = file:/etc/zerodds/part-ocsp.der

Refresh cron: OCSP responses are short-lived (typically 7 days). A refresh job every 1-3 days.

Trade-off: OCSP stapling needs responder infrastructure + a refresh job per participant. In return: instant revocation effect once the next stapling response is rejected. Implementation: crates/security-pki/src/ocsp.rs.

5 · HSM / smart-card integration

When: a compliance obligation (CRA, ISO 27001, FIPS-140) requires that private keys never leave the HSM. Embedded devices with a smart card (e.g. YubiKey 5, IDEMIA).

Status: the ZeroDDS default plugin reads private keys from the filesystem. A PKCS#11 bridge is on the roadmap (v1.1) — planned via cryptoki as a custom AuthenticationPlugin.

Interim solution — Linux with the OpenSSL PKCS11 engine:

# 1) install the engine + token lib
sudo apt install opensc libengine-pkcs11-openssl

# 2) cert signing request with the HSM key
openssl req -new -engine pkcs11 -keyform engine \
    -key "pkcs11:object=zerodds-key;type=private" \
    -out csr.pem -subj "/CN=robot-arm-7/O=Acme/C=DE"

# 3) sign via the issuing CA — as usual
# 4) ZeroDDS gets the cert + the HSM key URL instead of a file path
# (a custom plugin is required — not out-of-the-box today)

Writing a custom AuthenticationPlugin: implement the AuthenticationPlugin trait, replace validate_local_identity with a PKCS#11 sign call. Engagement model: custom development.

6 · Multi-tenant CA separation

When: multiple tenants share one ZeroDDS infrastructure (e.g. a SaaS DDS cloud). One CA per tenant, no cross-tenant trust.

Pattern: tier CAs under the root

Root CA (platform operator, offline)
├── Tenant-A Issuing CA
│   ├── tenant-a-participant-1
│   └── tenant-a-participant-2
└── Tenant-B Issuing CA
    ├── tenant-b-participant-1
    └── tenant-b-participant-2

Per tenant participant: the trust anchor contains ONLY its own tenant CA, not the root (otherwise the participant would accept cross-tenant certs).

# Tenant-A participant trust bundle:
cat tenant-a-issuing.pem > tenant-a-trust.pem
# do NOT add root-ca.pem!
# dds.sec.auth.identity_ca = file:/etc/zerodds/tenant-a-trust.pem

Plus topic isolation: via partition QoS — Tenant A uses partition tenant-a/*, Tenant B uses tenant-b/*. Double isolation: auth (no cert match) + AccessControl permissions (no topic match).

Trade-off: more CA operations overhead. But: much cleaner than permissions-XML tenant separation alone — if a tenant operator is compromised the cross-tenant path stays closed, because the cert is not trusted.