Szenario 1.2: Intermediate-CA erstellen

Kategorie: PKI-Infrastruktur
Komplexität: ⭐⭐⭐ (Mittel)
Voraussetzungen: Szenario 1.1: Root-CA erstellen
Geschätzte Zeit: 20-40 Minuten


Beschreibung

Dieses Szenario beschreibt die Erstellung einer Intermediate-CA (auch Sub-CA oder Issuing-CA genannt), die von der Root-CA signiert wird. Die Intermediate-CA stellt End-Entity-Zertifikate aus und schützt den Root-CA-Schlüssel, der offline bleiben kann.

Was wird erstellt:

  • ML-DSA-65 Schlüsselpaar für Intermediate-CA
  • Certificate Signing Request (CSR)
  • Von Root-CA signiertes Intermediate-Zertifikat
  • Verschlüsselter Private Key

Vorteile einer Intermediate-CA:

  • Sicherheit: Root-CA bleibt offline, nur Intermediate-CA online
  • Flexibilität: Mehrere Intermediate-CAs für verschiedene Zwecke (Server, Client, Code-Signing)
  • Widerruf: Intermediate-CA kann widerrufen werden, ohne Root zu kompromittieren
  • Compliance: Entspricht Best Practices (CA/Browser Forum, BSI)

Typische Intermediate-CA-Typen:

  • Server-CA: Für TLS-Server-Zertifikate
  • Client-CA: Für mTLS-Client-Zertifikate
  • User-CA: Für E-Mail/S-MIME-Zertifikate
  • CodeSign-CA: Für Code-Signing-Zertifikate
  • Device-CA: Für IoT/Geräte-Zertifikate

Ablaufdiagramm

┌─────────────────────────────────────────────────────────────────────────┐
│                    INTERMEDIATE-CA ERSTELLUNG                           │
│                                                                         │
│   ┌─────────────────────────┐       ┌─────────────────────────┐        │
│   │   INTERMEDIATE-CA       │       │      ROOT-CA            │        │
│   │   (Online-System)       │       │   (Air-Gapped System)   │        │
│   └─────────────────────────┘       └─────────────────────────┘        │
└─────────────────────────────────────────────────────────────────────────┘

     ┌──────────────┐
     │ 1. Init      │
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 2. KeyPair   │ ──────► ML-DSA-65 für Intermediate
     │ generieren   │
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 3. DN        │ ──────► CN=WvdS Server CA
     │ erstellen    │         O=DATECpro GmbH
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 4. Extensions│ ──────► BasicConstraints, KeyUsage, EKU
     │ für CSR      │         (gewünschte Extensions)
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 5. CSR       │ ──────► PKCS#10 Request
     │ erstellen    │         Signiert mit Intermediate-Key
     └──────┬───────┘
            │
            │  ══════════════════════════════════════════
            │  CSR-Transfer (USB-Stick, sicherer Kanal)
            ▼  ══════════════════════════════════════════
                                    
     ┌──────────────┐               
     │ 6. Root-CA   │ ──────► root-ca.key.pem laden
     │ laden        │         (Air-Gapped System)
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 7. CSR       │ ──────► Prüfen: DN, Signatur, Extensions
     │ validieren   │
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 8. Extensions│ ──────► BC, KU, SKI, AKI, CDP, AIA
     │ setzen       │         (CA bestimmt finale Extensions)
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 9. Zertifikat│ ──────► Root-CA signiert
     │ ausstellen   │         Intermediate-Zertifikat
     └──────┬───────┘
            │
            │  ══════════════════════════════════════════
            │  Zertifikat-Transfer zurück
            ▼  ══════════════════════════════════════════

     ┌──────────────┐
     │ 10. Speichern│ ──────► intermediate-ca.crt.pem
     │              │         intermediate-ca.key.pem
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 11. Chain    │ ──────► ca-chain.pem erstellen
     │ erstellen    │         (Intermediate + Root)
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 12. Cleanup  │
     └──────────────┘

Beteiligte Funktionen

Intermediate-CA Seite (CSR-Erstellung)

Schritt FFI-Funktion Rust-Crate Beschreibung
1 wvds_sec_crypto_x509_init() std::sync Bibliothek initialisieren
2 wvds_sec_crypto_x509_keypair_generate_mldsa(65) ml-dsa ML-DSA-65 Schlüsselpaar
3a wvds_sec_crypto_x509_dn_create() x509-cert DN-Handle erstellen
3b wvds_sec_crypto_x509_dn_add_component() x509-cert CN, O, OU, C hinzufügen
4 wvds_sec_crypto_x509_ext_set_*() x509-cert Gewünschte Extensions
5a wvds_sec_crypto_x509_csr_create() x509-cert CSR erstellen
5b wvds_sec_crypto_x509_csr_sign() ml-dsa CSR signieren
5c wvds_sec_crypto_x509_csr_to_pem() pem-rfc7468 CSR als PEM exportieren

Root-CA Seite (Zertifikat-Ausstellung)

Schritt FFI-Funktion Rust-Crate Beschreibung
6a wvds_sec_crypto_x509_cert_load_pem() x509-parser Root-Zertifikat laden
6b wvds_sec_crypto_x509_keypair_load_pem_encrypted() pkcs8, argon2 Root-Key laden
7a wvds_sec_crypto_x509_csr_load_pem() x509-cert CSR laden
7b wvds_sec_crypto_x509_csr_verify() ml-dsa CSR-Signatur prüfen
7c wvds_sec_crypto_x509_csr_get_subject() x509-cert Subject auslesen
8a wvds_sec_crypto_x509_ext_set_basic_constraints() x509-cert CA=true, pathLen=0
8b wvds_sec_crypto_x509_ext_set_key_usage() x509-cert keyCertSign, cRLSign
8c wvds_sec_crypto_x509_ext_set_ski_from_csr() sha2 SKI aus CSR-PublicKey
8d wvds_sec_crypto_x509_ext_set_aki_from_cert() x509-parser AKI = Root-SKI
8e wvds_sec_crypto_x509_ext_set_crl_distribution_points() x509-cert CRL-URL
8f wvds_sec_crypto_x509_ext_set_aia() x509-cert OCSP + CA Issuers URL
9 wvds_sec_crypto_x509_cert_create_from_csr() x509-cert, ml-dsa Zertifikat erstellen
10 wvds_sec_crypto_x509_cert_to_pem() pem-rfc7468 Zertifikat exportieren
11 wvds_sec_crypto_x509_chain_create() Chain-Handle erstellen

Funktionsbaum

═══════════════════════════════════════════════════════════════════════════
 PHASE 1: CSR-Erstellung (Intermediate-CA System)
═══════════════════════════════════════════════════════════════════════════

wvds_sec_crypto_x509_init()
│
├── wvds_sec_crypto_x509_keypair_generate_mldsa(level: 65)  → *mut KeyPairHandle
│   └── ml_dsa::ml_dsa_65::KeyPair::generate(&mut OsRng)
│
├── wvds_sec_crypto_x509_dn_create()  → *mut DnHandle
│
├── wvds_sec_crypto_x509_dn_add_component(dn, OID_CN, "WvdS Server CA")
├── wvds_sec_crypto_x509_dn_add_component(dn, OID_O, "DATECpro GmbH")
├── wvds_sec_crypto_x509_dn_add_component(dn, OID_OU, "PKI Services")
├── wvds_sec_crypto_x509_dn_add_component(dn, OID_C, "DE")
│
├── // Extensions für CSR (optional, CA kann überschreiben)
│   ├── wvds_sec_crypto_x509_ext_create()  → *mut ExtHandle
│   ├── wvds_sec_crypto_x509_ext_set_basic_constraints(ext, ca=true, path_len=0)
│   └── wvds_sec_crypto_x509_ext_set_key_usage(ext, KEY_CERT_SIGN | CRL_SIGN)
│
├── wvds_sec_crypto_x509_csr_create(subject: dn, keypair, extensions)  → *mut CsrHandle
│   │
│   ├── // CertificationRequestInfo aufbauen
│   │   └── cri = x509_cert::request::CertReqInfo {
│   │           version: 0,
│   │           subject: dn.clone(),
│   │           subject_public_key_info: keypair.to_spki(),
│   │           attributes: extensions.to_csr_attributes()
│   │       }
│   │
│   └── // CSR mit Intermediate-Key signieren
│       └── wvds_sec_crypto_x509_csr_sign(csr, keypair)
│           │
│           ├── cri_der = der::Encode::to_der(&cri)
│           ├── signature = ml_dsa::ml_dsa_65::SigningKey::sign(&cri_der)
│           │
│           └── csr = x509_cert::request::CertReq {
│                   info: cri,
│                   algorithm: OID_ML_DSA_65,
│                   signature: BitString::from_bytes(&signature)
│               }
│
├── wvds_sec_crypto_x509_csr_to_pem(csr, out, out_len)  → i32
│   └── pem_rfc7468::encode_string("CERTIFICATE REQUEST", &csr_der)
│
├── // CSR auf USB-Stick speichern → Air-Gapped System
│
└── wvds_sec_crypto_x509_keypair_to_pem_encrypted(keypair, password, ARGON2ID)
    └── // Intermediate Private Key sicher speichern


═══════════════════════════════════════════════════════════════════════════
 PHASE 2: Zertifikat-Ausstellung (Root-CA Air-Gapped System)
═══════════════════════════════════════════════════════════════════════════

wvds_sec_crypto_x509_init()
│
├── // Root-CA Materialien laden
│   │
│   ├── wvds_sec_crypto_x509_cert_load_pem(root_cert_pem)  → *mut CertHandle
│   │   └── x509_parser::parse_x509_certificate(&pem_decode(pem))
│   │
│   └── wvds_sec_crypto_x509_keypair_load_pem_encrypted(root_key_pem, password)  → *mut KeyPairHandle
│       ├── pem_rfc7468::decode("ENCRYPTED PRIVATE KEY", pem)
│       ├── pkcs8::EncryptedPrivateKeyInfo::from_der(der)
│       ├── kek = argon2::hash_password_into(password, salt)
│       ├── private_key_der = aes_gcm::decrypt(kek, encrypted_data)
│       └── KeyPair::from_pkcs8_der(private_key_der)
│
├── // CSR laden und validieren
│   │
│   ├── wvds_sec_crypto_x509_csr_load_pem(csr_pem)  → *mut CsrHandle
│   │   └── x509_cert::request::CertReq::from_der(&pem_decode(pem))
│   │
│   ├── wvds_sec_crypto_x509_csr_verify(csr)  → i32
│   │   │
│   │   ├── // Signatur verifizieren
│   │   │   ├── cri_der = der::Encode::to_der(&csr.info)
│   │   │   ├── public_key = csr.info.subject_public_key_info
│   │   │   └── ml_dsa::VerifyingKey::verify(&cri_der, &csr.signature)
│   │   │
│   │   └── return WVDS_OK oder WVDS_ERROR_SIGNATURE_INVALID
│   │
│   ├── wvds_sec_crypto_x509_csr_get_subject(csr)  → *mut DnHandle
│   │   └── csr.info.subject.clone()
│   │
│   └── wvds_sec_crypto_x509_csr_get_public_key(csr)  → *mut PublicKeyHandle
│       └── csr.info.subject_public_key_info.clone()
│
├── // Extensions für Intermediate-Zertifikat
│   │
│   ├── wvds_sec_crypto_x509_ext_create()  → *mut ExtHandle
│   │
│   ├── wvds_sec_crypto_x509_ext_set_basic_constraints(ext, ca=true, path_len=0)
│   │   │
│   │   └── // pathLen=0: Diese CA darf nur End-Entity-Zertifikate ausstellen
│   │       bc = BasicConstraints { ca: true, path_len_constraint: Some(0) }
│   │
│   ├── wvds_sec_crypto_x509_ext_set_key_usage(ext, KEY_CERT_SIGN | CRL_SIGN)
│   │
│   ├── wvds_sec_crypto_x509_ext_set_ski_from_public_key(ext, csr_public_key)
│   │   └── ski = sha2::Sha256::digest(&public_key_bytes)[0..20]
│   │
│   ├── wvds_sec_crypto_x509_ext_set_aki_from_cert(ext, root_cert)
│   │   │
│   │   ├── // Authority Key Identifier = SKI der Root-CA
│   │   │   └── root_ski = root_cert.get_extension(OID_SKI)
│   │   │
│   │   └── aki = AuthorityKeyIdentifier {
│   │           key_identifier: Some(root_ski),
│   │           authority_cert_issuer: None,
│   │           authority_cert_serial: None
│   │       }
│   │
│   ├── wvds_sec_crypto_x509_ext_set_crl_distribution_points(ext, urls[], url_count)
│   │   │
│   │   └── cdp = CrlDistributionPoints([
│   │           DistributionPoint {
│   │               distribution_point: Some(FullName([
│   │                   GeneralName::Uri("http://crl.example.com/root.crl")
│   │               ])),
│   │               reasons: None,
│   │               crl_issuer: None
│   │           }
│   │       ])
│   │
│   ├── wvds_sec_crypto_x509_ext_set_aia(ext, ocsp_url, ca_issuers_url)
│   │   │
│   │   └── aia = AuthorityInfoAccess([
│   │           AccessDescription {
│   │               access_method: OID_OCSP,
│   │               access_location: GeneralName::Uri("http://ocsp.example.com")
│   │           },
│   │           AccessDescription {
│   │               access_method: OID_CA_ISSUERS,
│   │               access_location: GeneralName::Uri("http://ca.example.com/root.crt")
│   │           }
│   │       ])
│   │
│   └── // Optional: Certificate Policies, Name Constraints, etc.
│
├── // Seriennummer und Gültigkeit
│   │
│   ├── wvds_sec_crypto_x509_serial_generate(serial, 20)
│   │
│   └── wvds_sec_crypto_x509_validity_create(now, now + 10 years)
│       └── // Intermediate-CA: 8-12 Jahre (kürzer als Root)
│
├── wvds_sec_crypto_x509_cert_create_from_csr(
│       csr,
│       issuer_cert: root_cert,
│       issuer_key: root_keypair,
│       serial,
│       validity,
│       extensions
│   )  → *mut CertHandle
│   │
│   ├── // TBSCertificate aufbauen
│   │   └── tbs = x509_cert::TbsCertificate {
│   │           version: Version::V3,
│   │           serial_number: SerialNumber::new(&serial),
│   │           signature: AlgorithmIdentifier { oid: OID_ML_DSA_65 },
│   │           issuer: root_cert.subject(),     // Issuer = Root-CA Subject
│   │           validity: validity,
│   │           subject: csr.info.subject,       // Subject aus CSR
│   │           subject_public_key_info: csr.info.subject_public_key_info,
│   │           extensions: Some(extensions)
│   │       }
│   │
│   ├── // Mit Root-CA signieren
│   │   └── signature = ml_dsa::SigningKey::sign(&tbs_der)
│   │
│   └── cert = x509_cert::Certificate { tbs, signature_algorithm, signature }
│
├── wvds_sec_crypto_x509_cert_to_pem(intermediate_cert, out, out_len)
│   └── pem_rfc7468::encode_string("CERTIFICATE", &cert_der)
│
└── // Cleanup
    ├── wvds_sec_crypto_x509_free_csr(csr)
    ├── wvds_sec_crypto_x509_free_cert(root_cert)
    ├── wvds_sec_crypto_x509_free_cert(intermediate_cert)
    ├── wvds_sec_crypto_x509_free_keypair(root_keypair)
    └── wvds_sec_crypto_x509_shutdown()


═══════════════════════════════════════════════════════════════════════════
 PHASE 3: Deployment (Intermediate-CA System)
═══════════════════════════════════════════════════════════════════════════

wvds_sec_crypto_x509_init()
│
├── // Zertifikats-Chain erstellen
│   │
│   ├── wvds_sec_crypto_x509_cert_load_pem(intermediate_cert_pem)  → intermediate
│   ├── wvds_sec_crypto_x509_cert_load_pem(root_cert_pem)  → root
│   │
│   ├── wvds_sec_crypto_x509_chain_create()  → *mut ChainHandle
│   ├── wvds_sec_crypto_x509_chain_add_cert(chain, intermediate)
│   ├── wvds_sec_crypto_x509_chain_add_cert(chain, root)
│   │
│   └── wvds_sec_crypto_x509_chain_to_pem(chain, out, out_len)
│       │
│       └── // ca-chain.pem:
│           // -----BEGIN CERTIFICATE-----
│           // (Intermediate-CA)
│           // -----END CERTIFICATE-----
│           // -----BEGIN CERTIFICATE-----
│           // (Root-CA)
│           // -----END CERTIFICATE-----
│
├── // Validierung
│   │
│   ├── wvds_sec_crypto_x509_validate_chain(chain, trust_store)
│   └── wvds_sec_crypto_x509_cert_verify_signature(intermediate, root)
│
└── wvds_sec_crypto_x509_shutdown()

Code-Beispiele

C# (.NET Wrapper)

using System;
using System.IO;
using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
 
namespace IntermediateCaExample
{
    class Program
    {
        // Passwörter (in Produktion: sicher eingeben!)
        const string ROOT_KEY_PASSWORD = "MyStr0ng!RootCA#Password2024";
        const string INTERMEDIATE_KEY_PASSWORD = "MyStr0ng!IntermediateCA#2024";
 
        static void Main(string[] args)
        {
            // === PHASE 1: CSR erstellen (Intermediate-CA System) ===
            Console.WriteLine("=== PHASE 1: CSR-Erstellung ===\n");
 
            using var context = PqCryptoContext.Initialize();
 
            // 1. Schlüsselpaar für Intermediate-CA generieren
            Console.WriteLine("Generiere ML-DSA-65 Schlüsselpaar für Intermediate-CA...");
            using var intermediateKeyPair = context.GenerateKeyPair(PqAlgorithm.MlDsa65);
            intermediateKeyPair.SelfTest();
 
            // 2. Distinguished Name
            var intermediateDn = new DistinguishedNameBuilder()
                .AddCommonName("WvdS Server CA")
                .AddOrganization("DATECpro GmbH")
                .AddOrganizationalUnit("PKI Services")
                .AddCountry("DE")
                .Build();
 
            Console.WriteLine($"Intermediate DN: {intermediateDn}");
 
            // 3. Gewünschte Extensions für CSR
            var csrExtensions = new X509ExtensionsBuilder()
                .AddBasicConstraints(isCa: true, pathLengthConstraint: 0, critical: true)
                .AddKeyUsage(KeyUsageFlags.KeyCertSign | KeyUsageFlags.CrlSign, critical: true)
                .Build();
 
            // 4. CSR erstellen und signieren
            Console.WriteLine("Erstelle Certificate Signing Request...");
            using var csr = context.CreateCertificateRequest(
                subject: intermediateDn,
                keyPair: intermediateKeyPair,
                extensions: csrExtensions
            );
 
            // 5. CSR als PEM speichern
            string csrPem = csr.ExportToPem();
            File.WriteAllText("intermediate-ca.csr.pem", csrPem);
            Console.WriteLine("CSR gespeichert: intermediate-ca.csr.pem");
 
            // 6. Intermediate Private Key verschlüsselt speichern
            File.WriteAllText("intermediate-ca.key.pem", 
                intermediateKeyPair.ExportToEncryptedPem(INTERMEDIATE_KEY_PASSWORD));
            Console.WriteLine("Private Key gespeichert: intermediate-ca.key.pem\n");
 
            // === PHASE 2: Zertifikat ausstellen (Root-CA System) ===
            Console.WriteLine("=== PHASE 2: Zertifikat-Ausstellung durch Root-CA ===\n");
 
            // 7. Root-CA Materialien laden
            Console.WriteLine("Lade Root-CA...");
            using var rootCert = context.LoadCertificateFromPem(
                File.ReadAllText("root-ca.crt.pem")
            );
            using var rootKeyPair = context.LoadKeyPairFromEncryptedPem(
                File.ReadAllText("root-ca.key.pem"),
                ROOT_KEY_PASSWORD
            );
 
            Console.WriteLine($"Root-CA geladen: {rootCert.Subject}");
 
            // 8. CSR laden und validieren
            Console.WriteLine("Lade und validiere CSR...");
            using var loadedCsr = context.LoadCertificateRequestFromPem(csrPem);
 
            if (!loadedCsr.VerifySignature())
                throw new CryptographicException("CSR-Signatur ungültig!");
            Console.WriteLine("CSR-Signatur: OK");
 
            // 9. Extensions für Intermediate-Zertifikat (CA bestimmt)
            var certExtensions = new X509ExtensionsBuilder()
                // BasicConstraints: CA, aber keine weitere Sub-CA erlaubt
                .AddBasicConstraints(isCa: true, pathLengthConstraint: 0, critical: true)
                // KeyUsage: Zertifikat- und CRL-Signierung
                .AddKeyUsage(KeyUsageFlags.KeyCertSign | KeyUsageFlags.CrlSign, critical: true)
                // Subject Key Identifier (aus CSR Public Key)
                .AddSubjectKeyIdentifier(loadedCsr.PublicKey)
                // Authority Key Identifier (= Root-CA SKI)
                .AddAuthorityKeyIdentifier(rootCert)
                // CRL Distribution Point
                .AddCrlDistributionPoint("http://crl.datecpro.de/root-ca.crl")
                // Authority Info Access (OCSP + CA Issuers)
                .AddAuthorityInfoAccess(
                    ocspUri: "http://ocsp.datecpro.de",
                    caIssuersUri: "http://ca.datecpro.de/root-ca.crt"
                )
                .Build();
 
            // 10. Gültigkeit: 10 Jahre (kürzer als Root-CA)
            var validity = new CertificateValidity(
                notBefore: DateTime.UtcNow,
                notAfter: DateTime.UtcNow.AddYears(10)
            );
 
            // 11. Intermediate-Zertifikat ausstellen
            Console.WriteLine("Stelle Intermediate-CA-Zertifikat aus...");
            using var intermediateCert = context.IssueCertificateFromRequest(
                request: loadedCsr,
                issuerCertificate: rootCert,
                issuerKeyPair: rootKeyPair,
                validity: validity,
                extensions: certExtensions
            );
 
            // 12. Zertifikat-Informationen ausgeben
            Console.WriteLine("\n=== INTERMEDIATE-CA ZERTIFIKAT ===");
            Console.WriteLine($"Subject:      {intermediateCert.Subject}");
            Console.WriteLine($"Issuer:       {intermediateCert.Issuer}");
            Console.WriteLine($"Serial:       {intermediateCert.SerialNumber}");
            Console.WriteLine($"Not Before:   {intermediateCert.NotBefore:yyyy-MM-dd}");
            Console.WriteLine($"Not After:    {intermediateCert.NotAfter:yyyy-MM-dd}");
            Console.WriteLine($"Algorithm:    {intermediateCert.SignatureAlgorithm}");
            Console.WriteLine($"Is CA:        {intermediateCert.IsCertificateAuthority}");
            Console.WriteLine($"Path Length:  {intermediateCert.PathLengthConstraint}");
            Console.WriteLine($"Thumbprint:   {intermediateCert.Thumbprint}");
 
            // 13. Zertifikat speichern
            File.WriteAllText("intermediate-ca.crt.pem", intermediateCert.ExportToPem());
            Console.WriteLine("\nZertifikat gespeichert: intermediate-ca.crt.pem");
 
            // === PHASE 3: Chain erstellen ===
            Console.WriteLine("\n=== PHASE 3: Certificate Chain ===\n");
 
            // 14. CA-Chain erstellen (Intermediate + Root)
            using var chain = context.CreateCertificateChain();
            chain.Add(intermediateCert);
            chain.Add(rootCert);
 
            File.WriteAllText("ca-chain.pem", chain.ExportToPem());
            Console.WriteLine("CA-Chain gespeichert: ca-chain.pem");
 
            // 15. Validierung
            Console.WriteLine("\n=== VALIDIERUNG ===");
 
            // Signatur prüfen (Intermediate von Root signiert?)
            bool signatureValid = intermediateCert.VerifySignature(rootCert);
            Console.WriteLine($"Signatur durch Root-CA: {(signatureValid ? "OK" : "FEHLER")}");
 
            // Chain-Validierung
            using var trustStore = context.CreateTrustStore();
            trustStore.AddCertificate(rootCert);
 
            var validationResult = context.ValidateChain(chain, trustStore);
            Console.WriteLine($"Chain-Validierung:      {(validationResult.IsValid ? "OK" : "FEHLER")}");
 
            Console.WriteLine("\n✓ Intermediate-CA erfolgreich erstellt!");
        }
    }
}

Delphi (FFI)

program CreateIntermediateCA;
 
{$APPTYPE CONSOLE}
 
uses
  SysUtils, DateUtils, Classes,
  WvdS.PqCrypto.FFI;
 
const
  ROOT_KEY_PASSWORD = 'MyStr0ng!RootCA#Password2024';
  INTERMEDIATE_KEY_PASSWORD = 'MyStr0ng!IntermediateCA#2024';
 
var
  res: Integer;
  // Intermediate-CA
  intKeyPair: Pointer;
  intDn: Pointer;
  intExt: Pointer;
  csr: Pointer;
  intCert: Pointer;
  // Root-CA
  rootCert: Pointer;
  rootKeyPair: Pointer;
  // Zertifikat-Erstellung
  certExt: Pointer;
  validity: Pointer;
  serial: array[0..19] of Byte;
  chain: Pointer;
  // Buffer
  pem_buf: array[0..65535] of AnsiChar;
  pem_len: NativeUInt;
 
  function LoadFileToString(const FileName: string): AnsiString;
  begin
    with TStringList.Create do
    try
      LoadFromFile(FileName);
      Result := AnsiString(Text);
    finally
      Free;
    end;
  end;
 
  procedure SaveStringToFile(const FileName: string; const Content: AnsiString);
  begin
    with TFileStream.Create(FileName, fmCreate) do
    try
      Write(Content[1], Length(Content));
    finally
      Free;
    end;
  end;
 
begin
  WriteLn('=== WvdS Intermediate-CA Erstellung ===');
  WriteLn;
 
  res := wvds_sec_crypto_x509_init();
  if res <> WVDS_OK then
  begin
    WriteLn('FEHLER: Init fehlgeschlagen');
    Exit;
  end;
 
  try
    // ═══════════════════════════════════════════════════════════
    // PHASE 1: CSR erstellen
    // ═══════════════════════════════════════════════════════════
    WriteLn('=== PHASE 1: CSR-Erstellung ===');
    WriteLn;
 
    // 1. Schlüsselpaar generieren
    WriteLn('Generiere ML-DSA-65 Schlüsselpaar...');
    intKeyPair := wvds_sec_crypto_x509_keypair_generate_mldsa(65);
    if intKeyPair = nil then
      raise Exception.Create('KeyPair-Generierung fehlgeschlagen');
 
    // 2. Distinguished Name
    intDn := wvds_sec_crypto_x509_dn_create();
    wvds_sec_crypto_x509_dn_add_component(intDn, '2.5.4.3', 'WvdS Server CA');
    wvds_sec_crypto_x509_dn_add_component(intDn, '2.5.4.10', 'DATECpro GmbH');
    wvds_sec_crypto_x509_dn_add_component(intDn, '2.5.4.11', 'PKI Services');
    wvds_sec_crypto_x509_dn_add_component(intDn, '2.5.4.6', 'DE');
 
    // 3. Extensions für CSR
    intExt := wvds_sec_crypto_x509_ext_create();
    wvds_sec_crypto_x509_ext_set_basic_constraints(intExt, True, 0);
    wvds_sec_crypto_x509_ext_set_key_usage(intExt, $0006);  // keyCertSign + cRLSign
 
    // 4. CSR erstellen
    WriteLn('Erstelle CSR...');
    csr := wvds_sec_crypto_x509_csr_create(intDn, intKeyPair, intExt);
    if csr = nil then
      raise Exception.Create('CSR-Erstellung fehlgeschlagen');
 
    // 5. CSR signieren
    res := wvds_sec_crypto_x509_csr_sign(csr, intKeyPair);
    if res <> WVDS_OK then
      raise Exception.Create('CSR-Signierung fehlgeschlagen');
 
    // 6. CSR als PEM speichern
    pem_len := SizeOf(pem_buf);
    wvds_sec_crypto_x509_csr_to_pem(csr, @pem_buf[0], @pem_len);
    SaveStringToFile('intermediate-ca.csr.pem', pem_buf);
    WriteLn('CSR gespeichert: intermediate-ca.csr.pem');
 
    // 7. Private Key speichern
    pem_len := SizeOf(pem_buf);
    wvds_sec_crypto_x509_keypair_to_pem_encrypted(
      intKeyPair, INTERMEDIATE_KEY_PASSWORD, WVDS_KDF_ARGON2ID, @pem_buf[0], @pem_len
    );
    SaveStringToFile('intermediate-ca.key.pem', pem_buf);
    WriteLn('Private Key gespeichert: intermediate-ca.key.pem');
    WriteLn;
 
    // ═══════════════════════════════════════════════════════════
    // PHASE 2: Zertifikat durch Root-CA ausstellen
    // ═══════════════════════════════════════════════════════════
    WriteLn('=== PHASE 2: Zertifikat-Ausstellung ===');
    WriteLn;
 
    // 8. Root-CA laden
    WriteLn('Lade Root-CA...');
    rootCert := wvds_sec_crypto_x509_cert_load_pem(
      PAnsiChar(LoadFileToString('root-ca.crt.pem'))
    );
    if rootCert = nil then
      raise Exception.Create('Root-Zertifikat laden fehlgeschlagen');
 
    rootKeyPair := wvds_sec_crypto_x509_keypair_load_pem_encrypted(
      PAnsiChar(LoadFileToString('root-ca.key.pem')),
      ROOT_KEY_PASSWORD
    );
    if rootKeyPair = nil then
      raise Exception.Create('Root-Key laden fehlgeschlagen');
 
    // 9. CSR validieren
    WriteLn('Validiere CSR...');
    res := wvds_sec_crypto_x509_csr_verify(csr);
    if res <> WVDS_OK then
      raise Exception.Create('CSR-Signatur ungültig');
    WriteLn('CSR-Signatur: OK');
 
    // 10. Extensions für Zertifikat
    certExt := wvds_sec_crypto_x509_ext_create();
    wvds_sec_crypto_x509_ext_set_basic_constraints(certExt, True, 0);
    wvds_sec_crypto_x509_ext_set_key_usage(certExt, $0006);
    wvds_sec_crypto_x509_ext_set_ski_from_csr(certExt, csr);
    wvds_sec_crypto_x509_ext_set_aki_from_cert(certExt, rootCert);
    wvds_sec_crypto_x509_ext_set_crl_distribution_points(
      certExt, 
      PAnsiChar('http://crl.datecpro.de/root-ca.crl'),
      1
    );
    wvds_sec_crypto_x509_ext_set_aia(
      certExt,
      'http://ocsp.datecpro.de',
      'http://ca.datecpro.de/root-ca.crt'
    );
 
    // 11. Seriennummer und Gültigkeit
    wvds_sec_crypto_x509_serial_generate(@serial[0], 20);
    validity := wvds_sec_crypto_x509_validity_create(
      DateTimeToUnix(Now, False),
      DateTimeToUnix(IncYear(Now, 10), False)  // 10 Jahre
    );
 
    // 12. Zertifikat ausstellen
    WriteLn('Stelle Zertifikat aus...');
    intCert := wvds_sec_crypto_x509_cert_create_from_csr(
      csr,
      rootCert,
      rootKeyPair,
      @serial[0], 20,
      validity,
      certExt
    );
    if intCert = nil then
      raise Exception.Create('Zertifikat-Erstellung fehlgeschlagen');
 
    // 13. Zertifikat speichern
    pem_len := SizeOf(pem_buf);
    wvds_sec_crypto_x509_cert_to_pem(intCert, @pem_buf[0], @pem_len);
    SaveStringToFile('intermediate-ca.crt.pem', pem_buf);
    WriteLn('Zertifikat gespeichert: intermediate-ca.crt.pem');
    WriteLn;
 
    // ═══════════════════════════════════════════════════════════
    // PHASE 3: Chain erstellen
    // ═══════════════════════════════════════════════════════════
    WriteLn('=== PHASE 3: CA-Chain ===');
    WriteLn;
 
    chain := wvds_sec_crypto_x509_chain_create();
    wvds_sec_crypto_x509_chain_add_cert(chain, intCert);
    wvds_sec_crypto_x509_chain_add_cert(chain, rootCert);
 
    pem_len := SizeOf(pem_buf);
    wvds_sec_crypto_x509_chain_to_pem(chain, @pem_buf[0], @pem_len);
    SaveStringToFile('ca-chain.pem', pem_buf);
    WriteLn('CA-Chain gespeichert: ca-chain.pem');
 
    WriteLn;
    WriteLn('=== Intermediate-CA erfolgreich erstellt! ===');
 
  finally
    // Cleanup
    if chain <> nil then wvds_sec_crypto_x509_free_chain(chain);
    if intCert <> nil then wvds_sec_crypto_x509_free_cert(intCert);
    if certExt <> nil then wvds_sec_crypto_x509_free_ext(certExt);
    if validity <> nil then wvds_sec_crypto_x509_free_validity(validity);
    if rootKeyPair <> nil then wvds_sec_crypto_x509_free_keypair(rootKeyPair);
    if rootCert <> nil then wvds_sec_crypto_x509_free_cert(rootCert);
    if csr <> nil then wvds_sec_crypto_x509_free_csr(csr);
    if intExt <> nil then wvds_sec_crypto_x509_free_ext(intExt);
    if intDn <> nil then wvds_sec_crypto_x509_free_dn(intDn);
    if intKeyPair <> nil then wvds_sec_crypto_x509_free_keypair(intKeyPair);
    wvds_sec_crypto_x509_shutdown();
  end;
end.

Extensions für Intermediate-CA

Extension OID Wert Critical Beschreibung
Basic Constraints 2.5.29.19 CA=true, pathLen=0 ✅ Ja Keine weitere Sub-CA erlaubt
Key Usage 2.5.29.15 keyCertSign, cRLSign ✅ Ja Nur Zertifikat-/CRL-Signierung
Subject Key Identifier 2.5.29.14 SHA-256(publicKey) ❌ Nein Eindeutige Schlüssel-ID
Authority Key Identifier 2.5.29.35 Root-CA SKI ❌ Nein Verweis auf Issuer-Schlüssel
CRL Distribution Points 2.5.29.31 URL(s) ❌ Nein Wo CRL abrufbar ist
Authority Info Access 1.3.6.1.5.5.7.1.1 OCSP + caIssuers ❌ Nein OCSP-URL und CA-Zertifikat-URL
Certificate Policies 2.5.29.32 Policy OID(s) ❌ Nein Optional: Ausstellungsrichtlinien
Extended Key Usage 2.5.29.37 Optional ❌ Nein Optional: Einschränkung auf Verwendungszweck

Unterschiede zu Root-CA

Aspekt Root-CA Intermediate-CA
Issuer = Subject (selbstsigniert) = Root-CA Subject
Signatur durch Eigener Schlüssel Root-CA Schlüssel
pathLength 1 oder 2 0 (nur End-Entity)
Gültigkeit 15-25 Jahre 8-12 Jahre
AKI Nicht vorhanden Root-CA SKI
CDP Optional Erforderlich
AIA Nicht vorhanden OCSP + caIssuers
Speicherort Key Offline / HSM Online (geschützt)
Verwendung Nur Intermediate-CAs signieren End-Entity-Zertifikate signieren

Sicherheitshinweise

Intermediate-CA Schlüsselschutz:

  • Private Key verschlüsselt speichern (Argon2id + AES-256-GCM)
  • Idealerweise in HSM oder Hardware-Token
  • Zugriff beschränken (nur CA-Dienst)
  • Audit-Logging für alle Signieroperationen
  • Regelmäßige Schlüsselrotation planen

Best Practices:

  • pathLength=0 verhindert weitere Sub-CAs unter dieser Intermediate-CA
  • Separate Intermediate-CAs für verschiedene Zwecke (Server, Client, Code-Signing)
  • CDP und AIA immer setzen für Revocation-Prüfung
  • CSR offline übertragen (USB-Stick) zum Air-Gapped Root-CA-System
  • Zertifikat-Chain (ca-chain.pem) für einfaches Deployment bereitstellen

Häufige Fehler vermeiden:

  • Nicht die Intermediate-CA mit dem gleichen Schlüssel wie Root erstellen
  • Nicht pathLength > 0 setzen, wenn keine Sub-Sub-CA gewünscht
  • Nicht AKI vergessen (erschwert Chain-Building)
  • Nicht CDP/AIA vergessen (Revocation-Prüfung schlägt fehl)

Ausgabe-Dateien

Datei Typ Beschreibung
intermediate-ca.csr.pem PKCS#10 Certificate Signing Request
intermediate-ca.key.pem PKCS#8 (encrypted) Verschlüsselter Private Key
intermediate-ca.crt.pem X.509 Intermediate-CA Zertifikat
ca-chain.pem PEM Bundle Intermediate + Root (für Deployment)

Rückgabewerte

Code Konstante Bedeutung
0 WVDS_OK Erfolg
1 WVDS_ERROR_INVALID_PARAMETER Ungültiger Parameter
105 WVDS_ERROR_INVALID_CSR CSR-Format ungültig
202 WVDS_ERROR_SIGNATURE_INVALID CSR-Signatur ungültig
208 WVDS_ERROR_WRONG_PASSWORD Falsches Root-Key-Passwort

Verwandte Szenarien

Beziehung Szenario Beschreibung
Voraussetzung 1.1 Root-CA erstellen Root-CA muss existieren
Nächster Schritt 1.4 Trust Store Root-Zertifikat verteilen
Nächster Schritt 1.6 CRL/OCSP Revocation einrichten
Dann 3.1 Server-Zertifikat End-Entity-Zertifikate ausstellen
Alternativ 1.3 CA-Hierarchie Mehrere Intermediate-CAs

Referenzen

Zuletzt geändert: den 29.01.2026 um 15:13