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:

Slučajevi uporabe:


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