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

Zuletzt geändert: 30.01.2026. u 06:43