====== Szenario 1.1: Root-CA mit PQ-Schlüsseln erstellen ====== **Kategorie:** [[.:start|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:** * ML-DSA-65 Schlüsselpaar (Post-Quantum-sicher) * Selbstsigniertes X.509 v3 Root-Zertifikat * Verschlüsselter Private Key (PKCS#8 mit Argon2id) **Anwendungsfälle:** * Aufbau einer neuen PQ-fähigen Enterprise-PKI * Migration bestehender PKI zu Post-Quantum-Algorithmen * Testumgebung für PQ-Zertifikate * Isolierte PKI für spezielle Anwendungen (IoT, Code-Signing) ---- ===== 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:// [[de:int:pqcrypt:referenz:error_codes_tabelle|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** | [[.:intermediate_ca_erstellen|1.2 Intermediate-CA]] | Untergeordnete CA von Root signieren | | **Dann** | [[.:trust_store_konfigurieren|1.4 Trust Store]] | Root-Zertifikat verteilen | | **Dann** | [[.:crl_ocsp_infrastruktur|1.6 CRL/OCSP]] | Revocation-Dienste einrichten | | **Alternativ** | [[de:int:pqcrypt:szenarien:schluessel:schluesselpaar_generieren|11.1 Schlüssel generieren]] | Nur Keys, kein Zertifikat | | **Verwandt** | [[de:int:pqcrypt:szenarien:schluessel:schluessel_speichern|11.2 Schlüssel speichern]] | Weitere Speicheroptionen | ---- ===== Referenzen ===== === API-Dokumentation === * [[de:int:pqcrypt:api:module:init|Modul: init]] * [[de:int:pqcrypt:api:module:keypair|Modul: keypair]] * [[de:int:pqcrypt:api:module:dn|Modul: dn]] * [[de:int:pqcrypt:api:module:cert|Modul: cert]] * [[de:int:pqcrypt:api:module:ext|Modul: ext]] * [[de:int:pqcrypt:api:module:free|Modul: free]] === Konzepte === * [[de:int:pqcrypt:konzepte:fips_204_ml_dsa|ML-DSA (FIPS 204)]] * [[de:int:pqcrypt:konzepte:pki_grundlagen|PKI-Grundlagen]] * [[de:int:pqcrypt:konzepte:x509_erweiterungen|X.509 Extensions]] === Sicherheit === * [[de:int:pqcrypt:sicherheit:schluessellaengen|Empfohlene Schlüssellängen]] * [[de:int:pqcrypt:sicherheit:secure_memory|Sichere Speicherverwaltung]] === Referenztabellen === * [[de:int:pqcrypt:referenz:oid_tabelle|OID-Tabelle]] * [[de:int:pqcrypt:referenz:error_codes_tabelle|Error Codes]] * [[de:int:pqcrypt:referenz:schluesselgroessen|Schlüsselgrößen]] * [[de:int:pqcrypt:referenz:signaturgroessen|Signaturgrößen]] === Externe Standards === * [[https://csrc.nist.gov/pubs/fips/204/final|NIST FIPS 204: ML-DSA]] * [[https://www.rfc-editor.org/rfc/rfc5280|RFC 5280: X.509 PKI]] * [[https://www.rfc-editor.org/rfc/rfc5958|RFC 5958: PKCS#8]] * [[https://datatracker.ietf.org/doc/draft-ietf-lamps-dilithium-certificates/|IETF: ML-DSA in X.509]] ---- << [[.:start|← PKI-Infrastruktur]] | [[de:int:pqcrypt:szenarien:start|▲ Szenarien]] | [[.:intermediate_ca_erstellen|1.2 Intermediate-CA →]] >> {{tag>szenario pki root-ca ml-dsa-65 selbstsigniert keypair zertifikat fips-204}} ---- //Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional// ~~NOTOC~~