Scenarij 1.1: Kreiranje Root-CA s PQ ključevima
Kategorija: PKI infrastruktura
Složenost: ⭐⭐⭐⭐ (Visoka)
Preduvjeti: Nema (ulazna točka)
Procijenjeno vrijeme: 15-30 minuta
Opis
Ovaj scenarij opisuje kreiranje samopotpisanog Root-CA s Post-Quantum ključevima. Root-CA čini sidrište povjerenja (Trust Anchor) za cijelu PKI hijerarhiju i najkritičniji je sigurnosni element infrastrukture.
Što se kreira:
- ML-DSA-65 par ključeva (Post-Quantum siguran)
- Samopotpisani X.509 v3 Root certifikat
- Šifrirani privatni ključ (PKCS#8 s Argon2id)
Slučajevi uporabe:
- Izgradnja nove PQ-spremne Enterprise PKI
- Migracija postojeće PKI na Post-Quantum algoritme
- Testno okruženje za PQ certifikate
- Izolirana PKI za posebne primjene (IoT, Code-Signing)
Dijagram tijeka
┌─────────────────────────────────────────────────────────────────┐
│ KREIRANJE ROOT-CA │
└─────────────────────────────────────────────────────────────────┘
┌──────────────┐
│ 1. Init │
│ Biblioteka │
└──────┬───────┘
│
▼
┌──────────────┐
│ 2. KeyPair │ ──────► ML-DSA-65 (FIPS 204)
│ generiranje │ ~4KB javni ključ
└──────┬───────┘ ~2KB privatni ključ
│
▼
┌──────────────┐
│ 3. DN │ ──────► CN=WvdS Root CA
│ kreiranje │ O=DATECpro GmbH
└──────┬───────┘ C=DE
│
▼
┌──────────────┐
│ 4. Serial │ ──────► 20 bajtova slučajnih (160 bit)
│ generiranje │
└──────┬───────┘
│
▼
┌──────────────┐
│ 5. Validity │ ──────► notBefore: sada
│ postavljanje │ notAfter: +20 godina
└──────┬───────┘
│
▼
┌──────────────┐
│ 6. Extensions│ ──────► BasicConstraints: CA=true, pathLen=1
│ postavljanje │ KeyUsage: keyCertSign, cRLSign
└──────┬───────┘ SKI: SHA-256(publicKey)
│
▼
┌──────────────┐
│ 7. Certifikat│ ──────► Subject = Issuer (samopotpisan)
│ kreiranje │ Potpis s ML-DSA-65
└──────┬───────┘
│
▼
┌──────────────┐
│ 8. Export │ ──────► root-ca.crt.pem (certifikat)
│ PEM │ root-ca.key.pem (šifrirano)
└──────┬───────┘
│
▼
┌──────────────┐
│ 9. Cleanup │ ──────► Oslobađanje handle-a
│ │ Brisanje tajni (zeroize)
└──────────────┘
Uključene funkcije
| Korak | FFI funkcija | Rust Crate | Opis |
|---|---|---|---|
| 1 | wvds_sec_crypto_x509_init() | std::sync | Inicijalizacija biblioteke |
| 2 | wvds_sec_crypto_x509_keypair_generate_mldsa(65) | ml-dsa | ML-DSA-65 par ključeva |
| 2a | wvds_sec_crypto_x509_keypair_self_test() | ml-dsa | Samotest (sign/verify) |
| 3a | wvds_sec_crypto_x509_dn_create() | x509-cert | Kreiranje DN handle-a |
| 3b | wvds_sec_crypto_x509_dn_add_component() | x509-cert, der | Dodavanje CN, O, C |
| 4 | wvds_sec_crypto_x509_serial_generate() | rand | 160-bitni serijski broj |
| 5 | wvds_sec_crypto_x509_validity_create() | x509-cert | Razdoblje valjanosti |
| 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 | Kreiranje Root certifikata |
| 8a | wvds_sec_crypto_x509_cert_to_pem() | pem-rfc7468 | Certifikat kao PEM |
| 8b | wvds_sec_crypto_x509_keypair_to_pem_encrypted() | argon2, aes-gcm | Šifrirani ključ |
| 9a | wvds_sec_crypto_x509_free_*() | std::alloc | Oslobađanje handle-a |
| 9b | wvds_sec_crypto_x509_zeroize_keypair() | zeroize | Brisanje tajni |
Stablo funkcija
wvds_sec_crypto_x509_init()
│
├── wvds_sec_crypto_x509_keypair_generate_mldsa(level: u8) → *mut KeyPairHandle
│ │
│ ├── // Odabir parametara
│ │ ├── [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)
│ │
│ ├── // Samotest (FIPS zahtjev)
│ │ ├── test_msg = [0x00..0x20] // 32 bajta testnih podataka
│ │ ├── signature = signing_key.sign(&test_msg)
│ │ └── assert!(verifying_key.verify(&test_msg, &signature).is_ok())
│ │
│ └── // Kreiranje handle-a
│ └── 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 bajtova, pozitivan
│ │ └── rand::rngs::OsRng.fill_bytes(&mut serial[0..len])
│ │
│ └── // Najviši bit na 0 (pozitivan)
│ └── 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 za datume >= 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) // samo 1 Intermediate razina
│ │ }
│ │
│ └── ext.add(Extension {
│ extn_id: OID_BASIC_CONSTRAINTS,
│ critical: true, // MORA biti critical za CA
│ extn_value: der::Encode::to_der(&bc)
│ })
│
├── wvds_sec_crypto_x509_ext_set_key_usage(ext, flags: u16) → i32
│ │
│ ├── // Za 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, // TREBALO BI biti critical
│ 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
│ │
│ ├── // Izgradnja TBSCertificate
│ │ └── 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(), // samopotpisan: issuer = subject
│ │ validity: validity.clone(),
│ │ subject: subject.clone(),
│ │ subject_public_key_info: keypair.to_spki(),
│ │ extensions: Some(extensions.clone())
│ │ }
│ │
│ ├── // DER kodiranje TBS-Certificate
│ │ └── tbs_der = der::Encode::to_der(&tbs)
│ │
│ ├── // Potpisivanje s ML-DSA-65
│ │ └── signature = ml_dsa::ml_dsa_65::SigningKey::sign(&tbs_der)
│ │ │
│ │ └── // Deterministički potpis (FIPS 204)
│ │ // Duljina potpisa: 3293 bajta
│ │
│ └── // Sastavljanje Certificate
│ └── 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)
│ │
│ └── // Primjer izlaza:
│ // -----BEGIN CERTIFICATE-----
│ // MIIxxxxxx...
│ // -----END CERTIFICATE-----
│
├── wvds_sec_crypto_x509_keypair_to_pem_encrypted(
│ keypair, password, kdf_algorithm, out, out_len
│ ) → i32
│ │
│ ├── // Privatni ključ kao PKCS#8 DER
│ │ └── private_key_der = keypair.signing_key.to_pkcs8_der()
│ │
│ ├── // Generiranje salta i nonce-a
│ │ ├── salt = rand::OsRng.gen::<[u8; 16]>()
│ │ └── nonce = rand::OsRng.gen::<[u8; 12]>()
│ │
│ ├── // Derivacija Key Encryption Key-a
│ │ └── [ARGON2ID]
│ │ └── argon2::Argon2::new(
│ │ Algorithm::Argon2id,
│ │ Version::V0x13,
│ │ Params::new(65536, 3, 4, Some(32)) // 64MB, 3 iter., 4 trake
│ │ ).hash_password_into(password.as_bytes(), &salt, &mut kek)
│ │
│ ├── // Šifriranje privatnog ključa
│ │ └── encrypted = aes_gcm::Aes256Gcm::new(&kek)
│ │ .encrypt(&nonce, private_key_der.as_ref())
│ │
│ ├── // Kreiranje EncryptedPrivateKeyInfo (PKCS#8)
│ │ └── epki = pkcs8::EncryptedPrivateKeyInfo {
│ │ encryption_algorithm: ...,
│ │ encrypted_data: encrypted
│ │ }
│ │
│ ├── // PEM kodiranje
│ │ └── pem_rfc7468::encode_string("ENCRYPTED PRIVATE KEY", &epki_der)
│ │
│ └── // KEK brisanje
│ └── zeroize::Zeroize::zeroize(&mut kek)
│
└── // Čišćenje
│
├── wvds_sec_crypto_x509_free_cert(cert)
├── wvds_sec_crypto_x509_free_keypair(keypair)
│ └── // Automatski Zeroize putem 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()
Primjeri koda
C# (.NET Wrapper)
using System; using System.IO; using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ; namespace RootCaExample { class Program { static void Main(string[] args) { // Lozinka za privatni ključ (u produkciji: sigurno unijeti!) string keyPassword = "MyStr0ng!RootCA#Password2024"; // 1. Inicijalizacija konteksta using var context = PqCryptoContext.Initialize(); // 2. Generiranje ML-DSA-65 para ključeva Console.WriteLine("Generiram ML-DSA-65 par ključeva..."); using var keyPair = context.GenerateKeyPair(PqAlgorithm.MlDsa65); // Izvršavanje samotesta if (!keyPair.SelfTest()) throw new CryptographicException("KeyPair self-test failed!"); Console.WriteLine($" Javni ključ: {keyPair.PublicKeySize} bajtova"); Console.WriteLine($" Privatni ključ: {keyPair.PrivateKeySize} bajtova"); // 3. Kreiranje Distinguished Name 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. Razdoblje valjanosti (20 godina za Root-CA) var validity = new CertificateValidity( notBefore: DateTime.UtcNow, notAfter: DateTime.UtcNow.AddYears(20) ); // 5. Ekstenzije za Root-CA var extensions = new X509ExtensionsBuilder() // BasicConstraints: CA=true, max 1 Intermediate razina .AddBasicConstraints(isCa: true, pathLengthConstraint: 1, critical: true) // KeyUsage: samo potpisivanje certifikata i CRL-a .AddKeyUsage( KeyUsageFlags.KeyCertSign | KeyUsageFlags.CrlSign, critical: true ) // Subject Key Identifier za kasniju AKI referencu .AddSubjectKeyIdentifier(keyPair) .Build(); // 6. Kreiranje Root certifikata (samopotpisan) Console.WriteLine("Kreiram samopotpisani Root certifikat..."); using var rootCert = context.CreateRootCertificate( keyPair: keyPair, subject: subjectDn, validity: validity, extensions: extensions ); // 7. Ispis informacija o certifikatu Console.WriteLine("\n=== ROOT-CA CERTIFIKAT ==="); 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($"Algoritam: {rootCert.SignatureAlgorithm}"); Console.WriteLine($"Thumbprint: {rootCert.Thumbprint}"); Console.WriteLine($"Is CA: {rootCert.IsCertificateAuthority}"); Console.WriteLine($"Path Length: {rootCert.PathLengthConstraint}"); // 8. Spremanje kao PEM datoteke string certPath = "root-ca.crt.pem"; string keyPath = "root-ca.key.pem"; // Certifikat (javni) File.WriteAllText(certPath, rootCert.ExportToPem()); Console.WriteLine($"\nCertifikat spremljen: {certPath}"); // Privatni ključ (šifriran s Argon2id) File.WriteAllText(keyPath, keyPair.ExportToEncryptedPem( password: keyPassword, kdfAlgorithm: KeyDerivationAlgorithm.Argon2id )); Console.WriteLine($"Privatni ključ spremljen: {keyPath} (šifrirano)"); // 9. Validacija: Ponovno učitavanje certifikata i provjera Console.WriteLine("\n=== VALIDACIJA ==="); using var loadedCert = context.LoadCertificateFromPem(File.ReadAllText(certPath)); using var loadedKey = context.LoadKeyPairFromEncryptedPem( File.ReadAllText(keyPath), keyPassword ); // Verifikacija samopotpisa bool signatureValid = loadedCert.VerifySignature(loadedCert); // self-signed Console.WriteLine($"Samopotpis valjan: {signatureValid}"); // Provjera pripadnosti para ključeva bool keyMatch = loadedCert.PublicKeyMatches(loadedKey); Console.WriteLine($"Public Key Match: {keyMatch}"); Console.WriteLine("\n✓ Root-CA uspješno kreiran!"); } } }
Delphi (FFI)
program CreateRootCA; {$APPTYPE CONSOLE} uses SysUtils, DateUtils, WvdS.PqCrypto.FFI; // FFI unit s deklaracijama 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 kreiranje ==='); WriteLn; // 1. Inicijalizacija biblioteke res := wvds_sec_crypto_x509_init(); if res <> WVDS_OK then begin WriteLn('GREŠKA: Inicijalizacija neuspješna (', res, ')'); Exit; end; try // 2. Generiranje ML-DSA-65 para ključeva WriteLn('Generiram ML-DSA-65 par ključeva...'); keypair := wvds_sec_crypto_x509_keypair_generate_mldsa(65); if keypair = nil then raise Exception.Create('Generiranje para ključeva neuspješno'); // Samotest res := wvds_sec_crypto_x509_keypair_self_test(keypair); if res <> WVDS_OK then raise Exception.Create('Samotest para ključeva neuspješan'); WriteLn(' Samotest: OK'); // 3. Kreiranje Distinguished Name WriteLn('Kreiram Distinguished Name...'); dn := wvds_sec_crypto_x509_dn_create(); if dn = nil then raise Exception.Create('Kreiranje DN-a neuspješno'); 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. Generiranje serijskog broja (20 bajtova = 160 bita) WriteLn('Generiram serijski broj...'); res := wvds_sec_crypto_x509_serial_generate(@serial[0], 20); if res <> WVDS_OK then raise Exception.Create('Generiranje serijskog broja neuspješno'); // 5. Valjanost: 20 godina WriteLn('Postavljam razdoblje valjanosti (20 godina)...'); validity := wvds_sec_crypto_x509_validity_create( DateTimeToUnix(Now, False), // not_before: sada DateTimeToUnix(IncYear(Now, 20), False) // not_after: +20 godina ); if validity = nil then raise Exception.Create('Kreiranje valjanosti neuspješno'); // 6. Kreiranje ekstenzija WriteLn('Postavljam ekstenzije...'); ext := wvds_sec_crypto_x509_ext_create(); if ext = nil then raise Exception.Create('Kreiranje ekstenzija neuspješno'); // 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 neuspješno'); // 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 neuspješno'); // Subject Key Identifier res := wvds_sec_crypto_x509_ext_set_ski_from_keypair(ext, keypair); if res <> WVDS_OK then raise Exception.Create('SKI neuspješno'); // 7. Kreiranje Root certifikata (samopotpisan) WriteLn('Kreiram samopotpisani Root certifikat...'); cert := wvds_sec_crypto_x509_cert_create_root( keypair, // Par ključeva dn, // Subject (= Issuer kod Root) @serial[0], 20, // Serijski broj validity, // Valjanost ext // Ekstenzije ); if cert = nil then raise Exception.Create('Kreiranje certifikata neuspješno'); // 8. Spremanje kao PEM WriteLn; WriteLn('Spremam datoteke...'); // Certifikat 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('Izvoz certifikata neuspješan'); with TFileStream.Create('root-ca.crt.pem', fmCreate) do try Write(pem_buf[0], pem_len); finally Free; end; WriteLn(' root-ca.crt.pem spremljen'); // Privatni ključ (šifrirano) 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('Izvoz ključa neuspješan'); with TFileStream.Create('root-ca.key.pem', fmCreate) do try Write(pem_buf[0], pem_len); finally Free; end; WriteLn(' root-ca.key.pem spremljen (šifrirano)'); WriteLn; WriteLn('=== Root-CA uspješno kreiran! ==='); finally // 9. Čišćenje 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.
Parametri
wvds_sec_crypto_x509_keypair_generate_mldsa
| Parametar | Tip | Opis | Valjane vrijednosti |
|---|---|---|---|
level | u8 | ML-DSA sigurnosna razina | 44, 65, 87 |
| Razina | NIST sigurnost | Javni ključ | Privatni ključ | Potpis |
|---|---|---|---|---|
| 44 | Razina 2 (~128-bit) | 1.312 bajtova | 2.560 bajtova | 2.420 bajtova |
| 65 | Razina 3 (~192-bit) | 1.952 bajtova | 4.032 bajtova | 3.293 bajtova |
| 87 | Razina 5 (~256-bit) | 2.592 bajtova | 4.896 bajtova | 4.595 bajtova |
wvds_sec_crypto_x509_dn_add_component
| Parametar | Tip | Opis | Primjer |
|---|---|---|---|
dn | *mut DnHandle | DN handle | – |
oid | *const c_char | OID kao string | „2.5.4.3“ |
value | *const c_char | UTF-8 vrijednost | „WvdS Root CA“ |
| OID | Konstanta | Opis | Primjer vrijednosti |
|---|---|---|---|
| 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 znaka) | 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
| Parametar | Tip | Opis | Vrijednost za Root-CA |
|---|---|---|---|
ext | *mut ExtHandle | Extension handle | – |
ca | bool | Je li CA certifikat | true |
path_len | i32 | Max. duljina putanje (-1 = neograničeno) | 1 ili 2 |
wvds_sec_crypto_x509_ext_set_key_usage
| Flag | Bit | Vrijednost | Opis |
|---|---|---|---|
| digitalSignature | 0 | 0x0080 | Digitalni potpisi |
| nonRepudiation | 1 | 0x0040 | Neporecivost |
| keyEncipherment | 2 | 0x0020 | Šifriranje ključeva |
| dataEncipherment | 3 | 0x0010 | Šifriranje podataka |
| keyAgreement | 4 | 0x0008 | Dogovor ključeva |
| keyCertSign | 5 | 0x0004 | Potpisivanje certifikata ✓ |
| cRLSign | 6 | 0x0002 | Potpisivanje CRL-a ✓ |
| encipherOnly | 7 | 0x0001 | Samo šifriranje |
Za Root-CA: flags = 0x0006 (keyCertSign + cRLSign)
Povratne vrijednosti
| Kod | Konstanta | Značenje |
|---|---|---|
| 0 | WVDS_OK | Uspjeh |
| 1 | WVDS_ERROR_INVALID_PARAMETER | Nevaljani parametar (npr. level ≠ 44/65/87) |
| 2 | WVDS_ERROR_OUT_OF_MEMORY | Alokacija memorije neuspješna |
| 3 | WVDS_ERROR_NOT_INITIALIZED | init() nije pozvan |
| 200 | WVDS_ERROR_KEY_GENERATION_FAILED | Generiranje ključa neuspješno |
| 201 | WVDS_ERROR_SIGNATURE_FAILED | Potpisivanje neuspješno |
| 210 | WVDS_ERROR_KEYPAIR_SELF_TEST_FAILED | Samotest neuspješan |
→ Potpuni popis: Kodovi grešaka
Izlazne datoteke
root-ca.crt.pem
-----BEGIN CERTIFICATE----- MIIHxjCCBiagAwIBAgIUP7J2kM9x... (Base64-kodirani DER) ... -----END CERTIFICATE-----
| Polje | Vrijednost |
|---|---|
| Version | 3 (0x02) |
| Serial | 20 bajtova slučajnih |
| 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 godina |
| Public Key | ML-DSA-65 (~1.952 bajtova) |
| Extensions | BasicConstraints, KeyUsage, SKI |
| Signature | ML-DSA-65 (~3.293 bajtova) |
root-ca.key.pem
-----BEGIN ENCRYPTED PRIVATE KEY----- MIIHbTBXBgkqhkiG9w0BBQ0w... (PKCS#8 EncryptedPrivateKeyInfo) ... -----END ENCRYPTED PRIVATE KEY-----
| Polje | Vrijednost |
|---|---|
| Format | PKCS#8 EncryptedPrivateKeyInfo |
| KDF | Argon2id (64MB, 3 iteracije, 4 trake) |
| Cipher | AES-256-GCM |
| Key Size | ~4.032 bajtova (ML-DSA-65 privatni ključ) |
Sigurnosne napomene
KRITIČNO – Root-CA privatni ključ:
Privatni ključ Root-CA je najkritičniji sigurnosni element cijele PKI. Kompromitacija znači potpuni gubitak povjerenja!
- Nikada ne generirati ili pohranjivati na mrežno povezanim sustavima
- Nikada ne pohranjivati nešifrirano
- Nikada ne prenositi e-mailom ili nesigurnim kanalima
Preporučene zaštitne mjere:
- Air-Gapped sustav: Root-CA operacije na izoliranom računalu bez mreže
- HSM: Hardware Security Module za pohranu ključeva (npr. YubiHSM, Thales Luna)
- Šifriranje: Minimalno AES-256 s jakom lozinkom (≥20 znakova)
- Fizička sigurnost: Šifrirani USB stick u sefu ili bankovnom sefu
- Backup: Minimalno 2 kopije na odvojenim lokacijama
- Key Ceremony: Dokumentiran proces sa svjedocima za sve Root-CA operacije
- Audit log: Protokolirati sve pristupe
Best Practices:
- ML-DSA-65 pruža dobar omjer sigurnosti i performansi za Root-CA
- pathLength=1 ograničava hijerarhiju na Root → Intermediate → End-Entity
- 20 godina valjanosti je uobičajeno za Root-CA (max. 25 godina preporučeno)
- SKI postaviti omogućuje kasniju AKI referencu u Intermediate certifikatima
- Samotest nakon generiranja osigurava ispravnu implementaciju
Česte greške
| Problem | Uzrok | Rješenje |
|---|---|---|
KEY_GENERATION_FAILED | Nedovoljno entropije | Provjeriti OS izvor slučajnosti, /dev/urandom dostupan? |
KEYPAIR_SELF_TEST_FAILED | Neispravna implementacija | Provjeriti verziju biblioteke, ponovno kompilirati |
| Certifikat nevažeći | DN prazan | Minimalno postaviti CN |
| Ključ nečitljiv | Pogrešna lozinka | Provjeriti lozinku, encoding (UTF-8) |
| Greška pathLength | Vrijednost < -1 | -1 za neograničeno, 0, 1, 2, … za ograničenje |
NOT_INITIALIZED | init() zaboravljen | Prvo pozvati wvds_sec_crypto_x509_init() |
| Curenje memorije | Handle-i nisu oslobođeni | Pozvati sve free_*() funkcije |
| PEM prevelik | Buffer premalen | Prethodno upitati veličinu (out_len = 0) |
Povezani scenariji
| Odnos | Scenarij | Opis |
|---|---|---|
| Sljedeći korak | 1.2 Intermediate-CA | Potpisivanje podređenog CA od Root-a |
| Zatim | 1.4 Trust Store | Distribucija Root certifikata |
| Zatim | 1.6 CRL/OCSP | Postavljanje servisa za opoziv |
| Alternativno | 11.1 Generiranje ključeva | Samo ključevi, bez certifikata |
| Povezano | 11.2 Pohrana ključeva | Dodatne opcije pohrane |
Reference
API dokumentacija
Koncepti
Sigurnost
Referentne tablice
Vanjski standardi
« ← PKI infrastruktura | ▲ Scenariji | 1.2 Intermediate-CA → »
Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional