Szenario 1.1: Root-CA mit PQ-Schlüsseln erstellen

Kategorie: PKI-Infrastruktur
Komplexität: ⭐⭐⭐⭐ (Hoch)
Voraussetzungen: Keine (Einstiegspunkt)
Geschätzte Zeit: 15-30 Minuten


Beschreibung

Dieses Szenario beschreibt die Erstellung einer selbstsignierten Root-CA mit Post-Quantum-Schlüsseln. Die Root-CA bildet den Vertrauensanker (Trust Anchor) für die gesamte PKI-Hierarchie und ist das sicherheitskritischste Element der Infrastruktur.

Was wird erstellt:

Anwendungsfälle:


Ablaufdiagramm

┌─────────────────────────────────────────────────────────────────┐
│                    ROOT-CA ERSTELLUNG                           │
└─────────────────────────────────────────────────────────────────┘

     ┌──────────────┐
     │ 1. Init      │
     │ Bibliothek   │
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 2. KeyPair   │ ──────► ML-DSA-65 (FIPS 204)
     │ generieren   │         ~4KB Public Key
     └──────┬───────┘         ~2KB Private Key
            │
            ▼
     ┌──────────────┐
     │ 3. DN        │ ──────► CN=WvdS Root CA
     │ erstellen    │         O=DATECpro GmbH
     └──────┬───────┘         C=DE
            │
            ▼
     ┌──────────────┐
     │ 4. Serial    │ ──────► 20 Bytes Zufall (160 bit)
     │ generieren   │
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 5. Validity  │ ──────► notBefore: jetzt
     │ festlegen    │         notAfter: +20 Jahre
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 6. Extensions│ ──────► BasicConstraints: CA=true, pathLen=1
     │ setzen       │         KeyUsage: keyCertSign, cRLSign
     └──────┬───────┘         SKI: SHA-256(publicKey)
            │
            ▼
     ┌──────────────┐
     │ 7. Zertifikat│ ──────► Subject = Issuer (selbstsigniert)
     │ erstellen    │         Signatur mit ML-DSA-65
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 8. Export    │ ──────► root-ca.crt.pem (Zertifikat)
     │ PEM          │         root-ca.key.pem (verschlüsselt)
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 9. Cleanup   │ ──────► Handles freigeben
     │              │         Secrets zeroizen
     └──────────────┘

Beteiligte Funktionen

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
2a wvds_sec_crypto_x509_keypair_self_test() ml-dsa Selbsttest (sign/verify)
3a wvds_sec_crypto_x509_dn_create() x509-cert DN-Handle erstellen
3b wvds_sec_crypto_x509_dn_add_component() x509-cert, der CN, O, C hinzufügen
4 wvds_sec_crypto_x509_serial_generate() rand 160-bit Seriennummer
5 wvds_sec_crypto_x509_validity_create() x509-cert Gültigkeitszeitraum
6a wvds_sec_crypto_x509_ext_set_basic_constraints() x509-cert, der CA=true, pathLen
6b wvds_sec_crypto_x509_ext_set_key_usage() x509-cert, der keyCertSign, cRLSign
6c wvds_sec_crypto_x509_ext_set_ski_from_keypair() sha2 Subject Key Identifier
7 wvds_sec_crypto_x509_cert_create_root() x509-cert, ml-dsa Root-Zertifikat erstellen
8a wvds_sec_crypto_x509_cert_to_pem() pem-rfc7468 Zertifikat als PEM
8b wvds_sec_crypto_x509_keypair_to_pem_encrypted() argon2, aes-gcm Key verschlüsselt
9a wvds_sec_crypto_x509_free_*() std::alloc Handles freigeben
9b wvds_sec_crypto_x509_zeroize_keypair() zeroize Secrets löschen

Funktionsbaum

wvds_sec_crypto_x509_init()
│
├── wvds_sec_crypto_x509_keypair_generate_mldsa(level: u8)  → *mut KeyPairHandle
│   │
│   ├── // Parameterwahl
│   │   ├── [ML_DSA_44] → ml_dsa::ml_dsa_44::KeyPair::generate(&mut OsRng)
│   │   ├── [ML_DSA_65] → ml_dsa::ml_dsa_65::KeyPair::generate(&mut OsRng)  ✓
│   │   └── [ML_DSA_87] → ml_dsa::ml_dsa_87::KeyPair::generate(&mut OsRng)
│   │
│   ├── // Selbsttest (FIPS-Anforderung)
│   │   ├── test_msg = [0x00..0x20]  // 32 Bytes Testdaten
│   │   ├── signature = signing_key.sign(&test_msg)
│   │   └── assert!(verifying_key.verify(&test_msg, &signature).is_ok())
│   │
│   └── // Handle erstellen
│       └── Box::into_raw(Box::new(KeyPairHandle { 
│               algorithm: ML_DSA_65,
│               signing_key,
│               verifying_key 
│           }))
│
├── wvds_sec_crypto_x509_dn_create()  → *mut DnHandle
│   └── x509_cert::name::Name::default()
│
├── wvds_sec_crypto_x509_dn_add_component(dn, oid, value)  → i32
│   │
│   ├── [OID_CN = "2.5.4.3"]
│   │   └── dn.0.push(RelativeDistinguishedName::from(
│   │           AttributeTypeAndValue { oid: OID_CN, value: "WvdS Root CA" }
│   │       ))
│   │
│   ├── [OID_O = "2.5.4.10"]
│   │   └── dn.0.push(..., value: "DATECpro GmbH")
│   │
│   └── [OID_C = "2.5.4.6"]
│       └── dn.0.push(..., value: "DE")
│
├── wvds_sec_crypto_x509_serial_generate(out: *mut u8, len: usize)  → i32
│   │
│   ├── // RFC 5280: max 20 Bytes, positiv
│   │   └── rand::rngs::OsRng.fill_bytes(&mut serial[0..len])
│   │
│   └── // Höchstes Bit auf 0 setzen (positiv)
│       └── serial[0] &= 0x7F
│
├── wvds_sec_crypto_x509_validity_create(not_before, not_after)  → *mut ValidityHandle
│   │
│   ├── not_before = x509_cert::time::Time::UtcTime(now)
│   │
│   └── not_after = x509_cert::time::Time::GeneralizedTime(now + 20 years)
│       │
│       └── // GeneralizedTime für Daten >= 2050
│
├── wvds_sec_crypto_x509_ext_set_basic_constraints(ext, ca: bool, path_len: i32)  → i32
│   │
│   ├── bc = x509_cert::ext::pkix::BasicConstraints {
│   │       ca: true,
│   │       path_len_constraint: Some(1)  // nur 1 Intermediate-Ebene
│   │   }
│   │
│   └── ext.add(Extension {
│           extn_id: OID_BASIC_CONSTRAINTS,
│           critical: true,  // MUSS critical sein für CA
│           extn_value: der::Encode::to_der(&bc)
│       })
│
├── wvds_sec_crypto_x509_ext_set_key_usage(ext, flags: u16)  → i32
│   │
│   ├── // Für Root-CA: keyCertSign (5) + cRLSign (6)
│   │   └── flags = 0x0006  // Bit 5 + Bit 6
│   │
│   ├── ku = x509_cert::ext::pkix::KeyUsage(flags)
│   │
│   └── ext.add(Extension {
│           extn_id: OID_KEY_USAGE,
│           critical: true,  // SOLLTE critical sein
│           extn_value: der::Encode::to_der(&ku)
│       })
│
├── wvds_sec_crypto_x509_ext_set_ski_from_keypair(ext, keypair)  → i32
│   │
│   ├── // Subject Key Identifier = SHA-256(SubjectPublicKeyInfo.subjectPublicKey)
│   │   ├── public_key_bytes = keypair.verifying_key.to_bytes()
│   │   └── ski = sha2::Sha256::digest(&public_key_bytes)[0..20]
│   │
│   └── ext.add(Extension {
│           extn_id: OID_SUBJECT_KEY_ID,
│           critical: false,
│           extn_value: der::Encode::to_der(&OctetString::new(ski))
│       })
│
├── wvds_sec_crypto_x509_cert_create_root(
│       keypair: *const KeyPairHandle,
│       subject: *const DnHandle,
│       serial: *const u8,
│       serial_len: usize,
│       validity: *const ValidityHandle,
│       extensions: *const ExtHandle
│   )  → *mut CertHandle
│   │
│   ├── // TBSCertificate aufbauen
│   │   └── tbs = x509_cert::TbsCertificate {
│   │           version: Version::V3,
│   │           serial_number: SerialNumber::new(&serial[..serial_len]),
│   │           signature: AlgorithmIdentifier { oid: OID_ML_DSA_65, parameters: None },
│   │           issuer: subject.clone(),      // selbstsigniert: issuer = subject
│   │           validity: validity.clone(),
│   │           subject: subject.clone(),
│   │           subject_public_key_info: keypair.to_spki(),
│   │           extensions: Some(extensions.clone())
│   │       }
│   │
│   ├── // TBS-Certificate DER-encodieren
│   │   └── tbs_der = der::Encode::to_der(&tbs)
│   │
│   ├── // Signieren mit ML-DSA-65
│   │   └── signature = ml_dsa::ml_dsa_65::SigningKey::sign(&tbs_der)
│   │       │
│   │       └── // Deterministische Signatur (FIPS 204)
│   │           // Signaturlänge: 3293 Bytes
│   │
│   └── // Certificate zusammenbauen
│       └── cert = x509_cert::Certificate {
│               tbs_certificate: tbs,
│               signature_algorithm: AlgorithmIdentifier { oid: OID_ML_DSA_65 },
│               signature: BitString::from_bytes(&signature)
│           }
│
├── wvds_sec_crypto_x509_cert_to_pem(cert, out, out_len)  → i32
│   │
│   ├── cert_der = der::Encode::to_der(&cert)
│   │
│   └── pem_rfc7468::encode_string("CERTIFICATE", &cert_der)
│       │
│       └── // Beispiel-Output:
│           // -----BEGIN CERTIFICATE-----
│           // MIIxxxxxx...
│           // -----END CERTIFICATE-----
│
├── wvds_sec_crypto_x509_keypair_to_pem_encrypted(
│       keypair, password, kdf_algorithm, out, out_len
│   )  → i32
│   │
│   ├── // Private Key als PKCS#8 DER
│   │   └── private_key_der = keypair.signing_key.to_pkcs8_der()
│   │
│   ├── // Salt und Nonce generieren
│   │   ├── salt = rand::OsRng.gen::<[u8; 16]>()
│   │   └── nonce = rand::OsRng.gen::<[u8; 12]>()
│   │
│   ├── // Key Encryption Key ableiten
│   │   └── [ARGON2ID]
│   │       └── argon2::Argon2::new(
│   │               Algorithm::Argon2id,
│   │               Version::V0x13,
│   │               Params::new(65536, 3, 4, Some(32))  // 64MB, 3 Iter, 4 Lanes
│   │           ).hash_password_into(password.as_bytes(), &salt, &mut kek)
│   │
│   ├── // Private Key verschlüsseln
│   │   └── encrypted = aes_gcm::Aes256Gcm::new(&kek)
│   │           .encrypt(&nonce, private_key_der.as_ref())
│   │
│   ├── // EncryptedPrivateKeyInfo erstellen (PKCS#8)
│   │   └── epki = pkcs8::EncryptedPrivateKeyInfo {
│   │           encryption_algorithm: ...,
│   │           encrypted_data: encrypted
│   │       }
│   │
│   ├── // Als PEM encodieren
│   │   └── pem_rfc7468::encode_string("ENCRYPTED PRIVATE KEY", &epki_der)
│   │
│   └── // KEK zeroizen
│       └── zeroize::Zeroize::zeroize(&mut kek)
│
└── // Cleanup
    │
    ├── wvds_sec_crypto_x509_free_cert(cert)
    ├── wvds_sec_crypto_x509_free_keypair(keypair)
    │   └── // Automatisches Zeroize via Drop
    ├── wvds_sec_crypto_x509_free_dn(dn)
    ├── wvds_sec_crypto_x509_free_validity(validity)
    ├── wvds_sec_crypto_x509_free_ext(ext)
    └── wvds_sec_crypto_x509_shutdown()

Code-Beispiele

C# (.NET Wrapper)

using System;
using System.IO;
using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
 
namespace RootCaExample
{
    class Program
    {
        static void Main(string[] args)
        {
            // Passwort für Private Key (in Produktion: sicher eingeben!)
            string keyPassword = "MyStr0ng!RootCA#Password2024";
 
            // 1. Kontext initialisieren
            using var context = PqCryptoContext.Initialize();
 
            // 2. ML-DSA-65 Schlüsselpaar generieren
            Console.WriteLine("Generiere ML-DSA-65 Schlüsselpaar...");
            using var keyPair = context.GenerateKeyPair(PqAlgorithm.MlDsa65);
 
            // Selbsttest durchführen
            if (!keyPair.SelfTest())
                throw new CryptographicException("KeyPair self-test failed!");
 
            Console.WriteLine($"  Public Key:  {keyPair.PublicKeySize} Bytes");
            Console.WriteLine($"  Private Key: {keyPair.PrivateKeySize} Bytes");
 
            // 3. Distinguished Name erstellen
            var subjectDn = new DistinguishedNameBuilder()
                .AddCommonName("WvdS Root CA")
                .AddOrganization("DATECpro GmbH")
                .AddOrganizationalUnit("PQ-Security")
                .AddCountry("DE")
                .AddLocality("München")
                .Build();
 
            Console.WriteLine($"Subject DN: {subjectDn}");
 
            // 4. Gültigkeitszeitraum (20 Jahre für Root-CA)
            var validity = new CertificateValidity(
                notBefore: DateTime.UtcNow,
                notAfter: DateTime.UtcNow.AddYears(20)
            );
 
            // 5. Extensions für Root-CA
            var extensions = new X509ExtensionsBuilder()
                // BasicConstraints: CA=true, max 1 Intermediate-Ebene
                .AddBasicConstraints(isCa: true, pathLengthConstraint: 1, critical: true)
                // KeyUsage: nur Zertifikat- und CRL-Signierung
                .AddKeyUsage(
                    KeyUsageFlags.KeyCertSign | KeyUsageFlags.CrlSign, 
                    critical: true
                )
                // Subject Key Identifier für spätere AKI-Referenzierung
                .AddSubjectKeyIdentifier(keyPair)
                .Build();
 
            // 6. Root-Zertifikat erstellen (selbstsigniert)
            Console.WriteLine("Erstelle selbstsigniertes Root-Zertifikat...");
            using var rootCert = context.CreateRootCertificate(
                keyPair: keyPair,
                subject: subjectDn,
                validity: validity,
                extensions: extensions
            );
 
            // 7. Zertifikat-Informationen ausgeben
            Console.WriteLine("\n=== ROOT-CA ZERTIFIKAT ===");
            Console.WriteLine($"Subject:        {rootCert.Subject}");
            Console.WriteLine($"Issuer:         {rootCert.Issuer}");
            Console.WriteLine($"Serial:         {rootCert.SerialNumber}");
            Console.WriteLine($"Not Before:     {rootCert.NotBefore:yyyy-MM-dd HH:mm:ss} UTC");
            Console.WriteLine($"Not After:      {rootCert.NotAfter:yyyy-MM-dd HH:mm:ss} UTC");
            Console.WriteLine($"Algorithm:      {rootCert.SignatureAlgorithm}");
            Console.WriteLine($"Thumbprint:     {rootCert.Thumbprint}");
            Console.WriteLine($"Is CA:          {rootCert.IsCertificateAuthority}");
            Console.WriteLine($"Path Length:    {rootCert.PathLengthConstraint}");
 
            // 8. Als PEM-Dateien speichern
            string certPath = "root-ca.crt.pem";
            string keyPath = "root-ca.key.pem";
 
            // Zertifikat (öffentlich)
            File.WriteAllText(certPath, rootCert.ExportToPem());
            Console.WriteLine($"\nZertifikat gespeichert: {certPath}");
 
            // Private Key (verschlüsselt mit Argon2id)
            File.WriteAllText(keyPath, keyPair.ExportToEncryptedPem(
                password: keyPassword,
                kdfAlgorithm: KeyDerivationAlgorithm.Argon2id
            ));
            Console.WriteLine($"Private Key gespeichert: {keyPath} (verschlüsselt)");
 
            // 9. Validierung: Zertifikat neu laden und prüfen
            Console.WriteLine("\n=== VALIDIERUNG ===");
            using var loadedCert = context.LoadCertificateFromPem(File.ReadAllText(certPath));
            using var loadedKey = context.LoadKeyPairFromEncryptedPem(
                File.ReadAllText(keyPath), 
                keyPassword
            );
 
            // Selbstsignatur verifizieren
            bool signatureValid = loadedCert.VerifySignature(loadedCert);  // self-signed
            Console.WriteLine($"Selbstsignatur gültig: {signatureValid}");
 
            // Key-Paar-Zugehörigkeit prüfen
            bool keyMatch = loadedCert.PublicKeyMatches(loadedKey);
            Console.WriteLine($"Public Key Match:      {keyMatch}");
 
            Console.WriteLine("\n✓ Root-CA erfolgreich erstellt!");
        }
    }
}

Delphi (FFI)

program CreateRootCA;
 
{$APPTYPE CONSOLE}
 
uses
  SysUtils, DateUtils,
  WvdS.PqCrypto.FFI;  // FFI-Unit mit Deklarationen
 
const
  KEY_PASSWORD = 'MyStr0ng!RootCA#Password2024';
 
var
  res: Integer;
  keypair: Pointer;
  dn: Pointer;
  validity: Pointer;
  ext: Pointer;
  cert: Pointer;
  serial: array[0..19] of Byte;
  pem_buf: array[0..65535] of AnsiChar;
  pem_len: NativeUInt;
 
begin
  WriteLn('=== WvdS Root-CA Erstellung ===');
  WriteLn;
 
  // 1. Bibliothek initialisieren
  res := wvds_sec_crypto_x509_init();
  if res <> WVDS_OK then
  begin
    WriteLn('FEHLER: Init fehlgeschlagen (', res, ')');
    Exit;
  end;
 
  try
    // 2. ML-DSA-65 Schlüsselpaar generieren
    WriteLn('Generiere ML-DSA-65 Schlüsselpaar...');
    keypair := wvds_sec_crypto_x509_keypair_generate_mldsa(65);
    if keypair = nil then
      raise Exception.Create('KeyPair-Generierung fehlgeschlagen');
 
    // Selbsttest
    res := wvds_sec_crypto_x509_keypair_self_test(keypair);
    if res <> WVDS_OK then
      raise Exception.Create('KeyPair-Selbsttest fehlgeschlagen');
    WriteLn('  Selbsttest: OK');
 
    // 3. Distinguished Name erstellen
    WriteLn('Erstelle Distinguished Name...');
    dn := wvds_sec_crypto_x509_dn_create();
    if dn = nil then
      raise Exception.Create('DN-Erstellung fehlgeschlagen');
 
    wvds_sec_crypto_x509_dn_add_component(dn, PAnsiChar('2.5.4.3'),  PAnsiChar('WvdS Root CA'));      // CN
    wvds_sec_crypto_x509_dn_add_component(dn, PAnsiChar('2.5.4.10'), PAnsiChar('DATECpro GmbH'));    // O
    wvds_sec_crypto_x509_dn_add_component(dn, PAnsiChar('2.5.4.11'), PAnsiChar('PQ-Security'));      // OU
    wvds_sec_crypto_x509_dn_add_component(dn, PAnsiChar('2.5.4.6'),  PAnsiChar('DE'));               // C
 
    // 4. Seriennummer generieren (20 Bytes = 160 Bit)
    WriteLn('Generiere Seriennummer...');
    res := wvds_sec_crypto_x509_serial_generate(@serial[0], 20);
    if res <> WVDS_OK then
      raise Exception.Create('Serial-Generierung fehlgeschlagen');
 
    // 5. Gültigkeit: 20 Jahre
    WriteLn('Setze Gültigkeitszeitraum (20 Jahre)...');
    validity := wvds_sec_crypto_x509_validity_create(
      DateTimeToUnix(Now, False),                      // not_before: jetzt
      DateTimeToUnix(IncYear(Now, 20), False)          // not_after: +20 Jahre
    );
    if validity = nil then
      raise Exception.Create('Validity-Erstellung fehlgeschlagen');
 
    // 6. Extensions erstellen
    WriteLn('Setze Extensions...');
    ext := wvds_sec_crypto_x509_ext_create();
    if ext = nil then
      raise Exception.Create('Extension-Erstellung fehlgeschlagen');
 
    // BasicConstraints: CA=true, pathLen=1
    res := wvds_sec_crypto_x509_ext_set_basic_constraints(ext, True, 1);
    if res <> WVDS_OK then
      raise Exception.Create('BasicConstraints fehlgeschlagen');
 
    // KeyUsage: keyCertSign (Bit 5) + cRLSign (Bit 6) = 0x0006
    res := wvds_sec_crypto_x509_ext_set_key_usage(ext, $0006);
    if res <> WVDS_OK then
      raise Exception.Create('KeyUsage fehlgeschlagen');
 
    // Subject Key Identifier
    res := wvds_sec_crypto_x509_ext_set_ski_from_keypair(ext, keypair);
    if res <> WVDS_OK then
      raise Exception.Create('SKI fehlgeschlagen');
 
    // 7. Root-Zertifikat erstellen (selbstsigniert)
    WriteLn('Erstelle selbstsigniertes Root-Zertifikat...');
    cert := wvds_sec_crypto_x509_cert_create_root(
      keypair,          // Schlüsselpaar
      dn,               // Subject (= Issuer bei Root)
      @serial[0], 20,   // Seriennummer
      validity,         // Gültigkeit
      ext               // Extensions
    );
    if cert = nil then
      raise Exception.Create('Zertifikat-Erstellung fehlgeschlagen');
 
    // 8. Als PEM speichern
    WriteLn;
    WriteLn('Speichere Dateien...');
 
    // Zertifikat
    pem_len := SizeOf(pem_buf);
    res := wvds_sec_crypto_x509_cert_to_pem(cert, @pem_buf[0], @pem_len);
    if res <> WVDS_OK then
      raise Exception.Create('Zertifikat-Export fehlgeschlagen');
 
    with TFileStream.Create('root-ca.crt.pem', fmCreate) do
    try
      Write(pem_buf[0], pem_len);
    finally
      Free;
    end;
    WriteLn('  root-ca.crt.pem gespeichert');
 
    // Private Key (verschlüsselt)
    pem_len := SizeOf(pem_buf);
    res := wvds_sec_crypto_x509_keypair_to_pem_encrypted(
      keypair, 
      PAnsiChar(KEY_PASSWORD),
      WVDS_KDF_ARGON2ID,
      @pem_buf[0], 
      @pem_len
    );
    if res <> WVDS_OK then
      raise Exception.Create('Key-Export fehlgeschlagen');
 
    with TFileStream.Create('root-ca.key.pem', fmCreate) do
    try
      Write(pem_buf[0], pem_len);
    finally
      Free;
    end;
    WriteLn('  root-ca.key.pem gespeichert (verschlüsselt)');
 
    WriteLn;
    WriteLn('=== Root-CA erfolgreich erstellt! ===');
 
  finally
    // 9. Cleanup
    if cert <> nil then wvds_sec_crypto_x509_free_cert(cert);
    if keypair <> nil then wvds_sec_crypto_x509_free_keypair(keypair);
    if dn <> nil then wvds_sec_crypto_x509_free_dn(dn);
    if validity <> nil then wvds_sec_crypto_x509_free_validity(validity);
    if ext <> nil then wvds_sec_crypto_x509_free_ext(ext);
    wvds_sec_crypto_x509_shutdown();
  end;
end.

Parameter

wvds_sec_crypto_x509_keypair_generate_mldsa

Parameter Typ Beschreibung Gültige Werte
level u8 ML-DSA Security Level 44, 65, 87
Level NIST-Sicherheit Public Key Private Key Signatur
44 Level 2 (~128-bit) 1.312 Bytes 2.560 Bytes 2.420 Bytes
65 Level 3 (~192-bit) 1.952 Bytes 4.032 Bytes 3.293 Bytes
87 Level 5 (~256-bit) 2.592 Bytes 4.896 Bytes 4.595 Bytes

wvds_sec_crypto_x509_dn_add_component

Parameter Typ Beschreibung Beispiel
dn *mut DnHandle DN-Handle
oid *const c_char OID als String „2.5.4.3“
value *const c_char UTF-8 Wert „WvdS Root CA“
OID Konstante Beschreibung Beispielwert
2.5.4.3 OID_CN Common Name WvdS Root CA
2.5.4.10 OID_O Organization DATECpro GmbH
2.5.4.11 OID_OU Organizational Unit PQ-Security
2.5.4.6 OID_C Country (2 Zeichen) DE
2.5.4.8 OID_ST State/Province Bayern
2.5.4.7 OID_L Locality München

wvds_sec_crypto_x509_ext_set_basic_constraints

Parameter Typ Beschreibung Root-CA Wert
ext *mut ExtHandle Extension-Handle
ca bool Ist CA-Zertifikat true
path_len i32 Max. Pfadlänge (-1 = unbegrenzt) 1 oder 2

wvds_sec_crypto_x509_ext_set_key_usage

Flag Bit Wert Beschreibung
digitalSignature 0 0x0080 Digitale Signaturen
nonRepudiation 1 0x0040 Nichtabstreitbarkeit
keyEncipherment 2 0x0020 Schlüsselverschlüsselung
dataEncipherment 3 0x0010 Datenverschlüsselung
keyAgreement 4 0x0008 Schlüsselvereinbarung
keyCertSign 5 0x0004 Zertifikat-Signierung
cRLSign 6 0x0002 CRL-Signierung
encipherOnly 7 0x0001 Nur Verschlüsselung

Für Root-CA: flags = 0x0006 (keyCertSign + cRLSign)


Rückgabewerte

Code Konstante Bedeutung
0 WVDS_OK Erfolg
1 WVDS_ERROR_INVALID_PARAMETER Ungültiger Parameter (z.B. level ≠ 44/65/87)
2 WVDS_ERROR_OUT_OF_MEMORY Speicherallokation fehlgeschlagen
3 WVDS_ERROR_NOT_INITIALIZED init() nicht aufgerufen
200 WVDS_ERROR_KEY_GENERATION_FAILED Schlüsselgenerierung fehlgeschlagen
201 WVDS_ERROR_SIGNATURE_FAILED Signierung fehlgeschlagen
210 WVDS_ERROR_KEYPAIR_SELF_TEST_FAILED Selbsttest fehlgeschlagen

Vollständige Liste: Error Codes


Ausgabe-Dateien

root-ca.crt.pem

-----BEGIN CERTIFICATE-----
MIIHxjCCBiagAwIBAgIUP7J2kM9x... (Base64-encoded DER)
...
-----END CERTIFICATE-----
Feld Wert
Version 3 (0x02)
Serial 20 Bytes Zufall
Signature Algorithm ML-DSA-65 (OID: 2.16.840.1.101.3.4.3.18)
Issuer CN=WvdS Root CA, O=DATECpro GmbH, C=DE
Subject CN=WvdS Root CA, O=DATECpro GmbH, C=DE
Validity 20 Jahre
Public Key ML-DSA-65 (~1.952 Bytes)
Extensions BasicConstraints, KeyUsage, SKI
Signature ML-DSA-65 (~3.293 Bytes)

root-ca.key.pem

-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIHbTBXBgkqhkiG9w0BBQ0w... (PKCS#8 EncryptedPrivateKeyInfo)
...
-----END ENCRYPTED PRIVATE KEY-----
Feld Wert
Format PKCS#8 EncryptedPrivateKeyInfo
KDF Argon2id (64MB, 3 Iterationen, 4 Lanes)
Cipher AES-256-GCM
Key Size ~4.032 Bytes (ML-DSA-65 Private Key)

Sicherheitshinweise

KRITISCH – Root-CA Private Key:

Der Private Key der Root-CA ist das sicherheitskritischste Element der gesamten PKI. Kompromittierung bedeutet vollständigen Vertrauensverlust!

  • Niemals auf vernetzten Systemen generieren oder speichern
  • Niemals unverschlüsselt speichern
  • Niemals per E-Mail oder unsichere Kanäle übertragen

Empfohlene Schutzmaßnahmen:

  • Air-Gapped System: Root-CA-Operationen auf isoliertem Computer ohne Netzwerk
  • HSM: Hardware Security Module für Schlüsselspeicherung (z.B. YubiHSM, Thales Luna)
  • Verschlüsselung: Mindestens AES-256 mit starkem Passwort (≥20 Zeichen)
  • Physische Sicherheit: Verschlüsselter USB-Stick im Tresor oder Bankschließfach
  • Backup: Mindestens 2 Kopien an getrennten Standorten
  • Key Ceremony: Dokumentierter Prozess mit Zeugen für alle Root-CA-Operationen
  • Audit-Log: Alle Zugriffe protokollieren

Best Practices:

  • ML-DSA-65 bietet gutes Verhältnis von Sicherheit und Performance für Root-CAs
  • pathLength=1 begrenzt die Hierarchie auf Root → Intermediate → End-Entity
  • 20 Jahre Gültigkeit ist üblich für Root-CAs (max. 25 Jahre empfohlen)
  • SKI setzen ermöglicht spätere AKI-Referenzierung in Intermediate-Zertifikaten
  • Selbsttest nach Generierung stellt korrekte Implementierung sicher

Häufige Fehler

Problem Ursache Lösung
KEY_GENERATION_FAILED Nicht genug Entropie OS-Zufallsquelle prüfen, /dev/urandom verfügbar?
KEYPAIR_SELF_TEST_FAILED Defekte Implementierung Bibliotheksversion prüfen, neu kompilieren
Zertifikat ungültig DN leer Mindestens CN setzen
Key nicht lesbar Falsches Passwort Passwort prüfen, Encoding (UTF-8)
pathLength-Fehler Wert < -1 -1 für unbegrenzt, 0, 1, 2, … für Limit
NOT_INITIALIZED init() vergessen wvds_sec_crypto_x509_init() zuerst aufrufen
Speicherleck Handles nicht freigegeben Alle free_*() Funktionen aufrufen
PEM zu groß Buffer zu klein Größe vorher abfragen (out_len = 0)

Verwandte Szenarien

Beziehung Szenario Beschreibung
Nächster Schritt 1.2 Intermediate-CA Untergeordnete CA von Root signieren
Dann 1.4 Trust Store Root-Zertifikat verteilen
Dann 1.6 CRL/OCSP Revocation-Dienste einrichten
Alternativ 11.1 Schlüssel generieren Nur Keys, kein Zertifikat
Verwandt 11.2 Schlüssel speichern Weitere Speicheroptionen

Referenzen

API-Dokumentation

Konzepte

Sicherheit

Referenztabellen

Externe Standards


« ← PKI-Infrastruktur | ▲ Szenarien | 1.2 Intermediate-CA → »


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