Scenarij 1.2: Kreiranje Intermediate-CA

Kategorija: PKI infrastruktura
Složenost: ⭐⭐⭐ (Srednja)
Preduvjeti: Scenarij 1.1: Kreiranje Root-CA
Procijenjeno vrijeme: 20-40 minuta


Opis

Ovaj scenarij opisuje kreiranje Intermediate-CA (također nazvanog Sub-CA ili Issuing-CA), koji je potpisan od Root-CA. Intermediate-CA izdaje certifikate krajnjih entiteta i štiti Root-CA ključ, koji može ostati offline.

Što se kreira:

  • ML-DSA-65 par ključeva za Intermediate-CA
  • Certificate Signing Request (CSR)
  • Intermediate certifikat potpisan od Root-CA
  • Šifrirani privatni ključ

Prednosti Intermediate-CA:

  • Sigurnost: Root-CA ostaje offline, samo Intermediate-CA online
  • Fleksibilnost: Više Intermediate-CA za različite namjene (Server, Client, Code-Signing)
  • Opoziv: Intermediate-CA može biti opozvan bez kompromitacije Root-a
  • Usklađenost: Odgovara best practices (CA/Browser Forum, BSI)

Tipični tipovi Intermediate-CA:

  • Server-CA: Za TLS server certifikate
  • Client-CA: Za mTLS klijentske certifikate
  • User-CA: Za E-Mail/S-MIME certifikate
  • CodeSign-CA: Za Code-Signing certifikate
  • Device-CA: Za IoT/uređajne certifikate

Dijagram tijeka

┌─────────────────────────────────────────────────────────────────────────┐
│                    KREIRANJE INTERMEDIATE-CA                            │
│                                                                         │
│   ┌─────────────────────────┐       ┌─────────────────────────┐        │
│   │   INTERMEDIATE-CA       │       │      ROOT-CA            │        │
│   │   (Online sustav)       │       │   (Air-Gapped sustav)   │        │
│   └─────────────────────────┘       └─────────────────────────┘        │
└─────────────────────────────────────────────────────────────────────────┘

     ┌──────────────┐
     │ 1. Init      │
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 2. KeyPair   │ ──────► ML-DSA-65 za Intermediate
     │ generiranje  │
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 3. DN        │ ──────► CN=WvdS Server CA
     │ kreiranje    │         O=DATECpro GmbH
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 4. Extensions│ ──────► BasicConstraints, KeyUsage, EKU
     │ za CSR       │         (željene ekstenzije)
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 5. CSR       │ ──────► PKCS#10 zahtjev
     │ kreiranje    │         Potpisan s Intermediate ključem
     └──────┬───────┘
            │
            │  ══════════════════════════════════════════
            │  Prijenos CSR-a (USB stick, siguran kanal)
            ▼  ══════════════════════════════════════════

     ┌──────────────┐
     │ 6. Root-CA   │ ──────► Učitavanje root-ca.key.pem
     │ učitavanje   │         (Air-Gapped sustav)
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 7. CSR       │ ──────► Provjera: DN, potpis, ekstenzije
     │ validacija   │
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 8. Extensions│ ──────► BC, KU, SKI, AKI, CDP, AIA
     │ postavljanje │         (CA određuje konačne ekstenzije)
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 9. Certifikat│ ──────► Root-CA potpisuje
     │ izdavanje    │         Intermediate certifikat
     └──────┬───────┘
            │
            │  ══════════════════════════════════════════
            │  Prijenos certifikata natrag
            ▼  ══════════════════════════════════════════

     ┌──────────────┐
     │ 10. Spremanje│ ──────► intermediate-ca.crt.pem
     │              │         intermediate-ca.key.pem
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 11. Chain    │ ──────► Kreiranje ca-chain.pem
     │ kreiranje    │         (Intermediate + Root)
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 12. Cleanup  │
     └──────────────┘

Uključene funkcije

Strana Intermediate-CA (Kreiranje CSR-a)

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
3a wvds_sec_crypto_x509_dn_create() x509-cert Kreiranje DN handle-a
3b wvds_sec_crypto_x509_dn_add_component() x509-cert Dodavanje CN, O, OU, C
4 wvds_sec_crypto_x509_ext_set_*() x509-cert Željene ekstenzije
5a wvds_sec_crypto_x509_csr_create() x509-cert Kreiranje CSR-a
5b wvds_sec_crypto_x509_csr_sign() ml-dsa Potpisivanje CSR-a
5c wvds_sec_crypto_x509_csr_to_pem() pem-rfc7468 Izvoz CSR-a kao PEM

Strana Root-CA (Izdavanje certifikata)

Korak FFI funkcija Rust Crate Opis
6a wvds_sec_crypto_x509_cert_load_pem() x509-parser Učitavanje Root certifikata
6b wvds_sec_crypto_x509_keypair_load_pem_encrypted() pkcs8, argon2 Učitavanje Root ključa
7a wvds_sec_crypto_x509_csr_load_pem() x509-cert Učitavanje CSR-a
7b wvds_sec_crypto_x509_csr_verify() ml-dsa Provjera potpisa CSR-a
7c wvds_sec_crypto_x509_csr_get_subject() x509-cert Čitanje Subject-a
8a wvds_sec_crypto_x509_ext_set_basic_constraints() x509-cert CA=true, pathLen=0
8b wvds_sec_crypto_x509_ext_set_key_usage() x509-cert keyCertSign, cRLSign
8c wvds_sec_crypto_x509_ext_set_ski_from_csr() sha2 SKI iz CSR PublicKey-a
8d wvds_sec_crypto_x509_ext_set_aki_from_cert() x509-parser AKI = Root-SKI
8e wvds_sec_crypto_x509_ext_set_crl_distribution_points() x509-cert CRL URL
8f wvds_sec_crypto_x509_ext_set_aia() x509-cert OCSP + CA Issuers URL
9 wvds_sec_crypto_x509_cert_create_from_csr() x509-cert, ml-dsa Kreiranje certifikata
10 wvds_sec_crypto_x509_cert_to_pem() pem-rfc7468 Izvoz certifikata
11 wvds_sec_crypto_x509_chain_create() Kreiranje Chain handle-a

Stablo funkcija

═══════════════════════════════════════════════════════════════════════════
 FAZA 1: Kreiranje CSR-a (Intermediate-CA sustav)
═══════════════════════════════════════════════════════════════════════════

wvds_sec_crypto_x509_init()
│
├── wvds_sec_crypto_x509_keypair_generate_mldsa(level: 65)  → *mut KeyPairHandle
│   └── ml_dsa::ml_dsa_65::KeyPair::generate(&mut OsRng)
│
├── wvds_sec_crypto_x509_dn_create()  → *mut DnHandle
│
├── wvds_sec_crypto_x509_dn_add_component(dn, OID_CN, "WvdS Server CA")
├── wvds_sec_crypto_x509_dn_add_component(dn, OID_O, "DATECpro GmbH")
├── wvds_sec_crypto_x509_dn_add_component(dn, OID_OU, "PKI Services")
├── wvds_sec_crypto_x509_dn_add_component(dn, OID_C, "DE")
│
├── // Ekstenzije za CSR (opcijski, CA može prepisati)
│   ├── wvds_sec_crypto_x509_ext_create()  → *mut ExtHandle
│   ├── wvds_sec_crypto_x509_ext_set_basic_constraints(ext, ca=true, path_len=0)
│   └── wvds_sec_crypto_x509_ext_set_key_usage(ext, KEY_CERT_SIGN | CRL_SIGN)
│
├── wvds_sec_crypto_x509_csr_create(subject: dn, keypair, extensions)  → *mut CsrHandle
│   │
│   ├── // Izgradnja CertificationRequestInfo
│   │   └── cri = x509_cert::request::CertReqInfo {
│   │           version: 0,
│   │           subject: dn.clone(),
│   │           subject_public_key_info: keypair.to_spki(),
│   │           attributes: extensions.to_csr_attributes()
│   │       }
│   │
│   └── // Potpisivanje CSR-a s Intermediate ključem
│       └── wvds_sec_crypto_x509_csr_sign(csr, keypair)
│           │
│           ├── cri_der = der::Encode::to_der(&cri)
│           ├── signature = ml_dsa::ml_dsa_65::SigningKey::sign(&cri_der)
│           │
│           └── csr = x509_cert::request::CertReq {
│                   info: cri,
│                   algorithm: OID_ML_DSA_65,
│                   signature: BitString::from_bytes(&signature)
│               }
│
├── wvds_sec_crypto_x509_csr_to_pem(csr, out, out_len)  → i32
│   └── pem_rfc7468::encode_string("CERTIFICATE REQUEST", &csr_der)
│
├── // Spremanje CSR-a na USB stick → Air-Gapped sustav
│
└── wvds_sec_crypto_x509_keypair_to_pem_encrypted(keypair, password, ARGON2ID)
    └── // Sigurno spremanje Intermediate privatnog ključa


═══════════════════════════════════════════════════════════════════════════
 FAZA 2: Izdavanje certifikata (Root-CA Air-Gapped sustav)
═══════════════════════════════════════════════════════════════════════════

wvds_sec_crypto_x509_init()
│
├── // Učitavanje Root-CA materijala
│   │
│   ├── wvds_sec_crypto_x509_cert_load_pem(root_cert_pem)  → *mut CertHandle
│   │   └── x509_parser::parse_x509_certificate(&pem_decode(pem))
│   │
│   └── wvds_sec_crypto_x509_keypair_load_pem_encrypted(root_key_pem, password)  → *mut KeyPairHandle
│       ├── pem_rfc7468::decode("ENCRYPTED PRIVATE KEY", pem)
│       ├── pkcs8::EncryptedPrivateKeyInfo::from_der(der)
│       ├── kek = argon2::hash_password_into(password, salt)
│       ├── private_key_der = aes_gcm::decrypt(kek, encrypted_data)
│       └── KeyPair::from_pkcs8_der(private_key_der)
│
├── // Učitavanje i validacija CSR-a
│   │
│   ├── wvds_sec_crypto_x509_csr_load_pem(csr_pem)  → *mut CsrHandle
│   │   └── x509_cert::request::CertReq::from_der(&pem_decode(pem))
│   │
│   ├── wvds_sec_crypto_x509_csr_verify(csr)  → i32
│   │   │
│   │   ├── // Verifikacija potpisa
│   │   │   ├── cri_der = der::Encode::to_der(&csr.info)
│   │   │   ├── public_key = csr.info.subject_public_key_info
│   │   │   └── ml_dsa::VerifyingKey::verify(&cri_der, &csr.signature)
│   │   │
│   │   └── return WVDS_OK ili WVDS_ERROR_SIGNATURE_INVALID
│   │
│   ├── wvds_sec_crypto_x509_csr_get_subject(csr)  → *mut DnHandle
│   │   └── csr.info.subject.clone()
│   │
│   └── wvds_sec_crypto_x509_csr_get_public_key(csr)  → *mut PublicKeyHandle
│       └── csr.info.subject_public_key_info.clone()
│
├── // Ekstenzije za Intermediate certifikat
│   │
│   ├── wvds_sec_crypto_x509_ext_create()  → *mut ExtHandle
│   │
│   ├── wvds_sec_crypto_x509_ext_set_basic_constraints(ext, ca=true, path_len=0)
│   │   │
│   │   └── // pathLen=0: Ovaj CA smije izdavati samo End-Entity certifikate
│   │       bc = BasicConstraints { ca: true, path_len_constraint: Some(0) }
│   │
│   ├── wvds_sec_crypto_x509_ext_set_key_usage(ext, KEY_CERT_SIGN | CRL_SIGN)
│   │
│   ├── wvds_sec_crypto_x509_ext_set_ski_from_public_key(ext, csr_public_key)
│   │   └── ski = sha2::Sha256::digest(&public_key_bytes)[0..20]
│   │
│   ├── wvds_sec_crypto_x509_ext_set_aki_from_cert(ext, root_cert)
│   │   │
│   │   ├── // Authority Key Identifier = SKI Root-CA
│   │   │   └── root_ski = root_cert.get_extension(OID_SKI)
│   │   │
│   │   └── aki = AuthorityKeyIdentifier {
│   │           key_identifier: Some(root_ski),
│   │           authority_cert_issuer: None,
│   │           authority_cert_serial: None
│   │       }
│   │
│   ├── wvds_sec_crypto_x509_ext_set_crl_distribution_points(ext, urls[], url_count)
│   │   │
│   │   └── cdp = CrlDistributionPoints([
│   │           DistributionPoint {
│   │               distribution_point: Some(FullName([
│   │                   GeneralName::Uri("http://crl.example.com/root.crl")
│   │               ])),
│   │               reasons: None,
│   │               crl_issuer: None
│   │           }
│   │       ])
│   │
│   ├── wvds_sec_crypto_x509_ext_set_aia(ext, ocsp_url, ca_issuers_url)
│   │   │
│   │   └── aia = AuthorityInfoAccess([
│   │           AccessDescription {
│   │               access_method: OID_OCSP,
│   │               access_location: GeneralName::Uri("http://ocsp.example.com")
│   │           },
│   │           AccessDescription {
│   │               access_method: OID_CA_ISSUERS,
│   │               access_location: GeneralName::Uri("http://ca.example.com/root.crt")
│   │           }
│   │       ])
│   │
│   └── // Opcijski: Certificate Policies, Name Constraints, itd.
│
├── // Serijski broj i valjanost
│   │
│   ├── wvds_sec_crypto_x509_serial_generate(serial, 20)
│   │
│   └── wvds_sec_crypto_x509_validity_create(now, now + 10 years)
│       └── // Intermediate-CA: 8-12 godina (kraće od Root)
│
├── wvds_sec_crypto_x509_cert_create_from_csr(
│       csr,
│       issuer_cert: root_cert,
│       issuer_key: root_keypair,
│       serial,
│       validity,
│       extensions
│   )  → *mut CertHandle
│   │
│   ├── // Izgradnja TBSCertificate
│   │   └── tbs = x509_cert::TbsCertificate {
│   │           version: Version::V3,
│   │           serial_number: SerialNumber::new(&serial),
│   │           signature: AlgorithmIdentifier { oid: OID_ML_DSA_65 },
│   │           issuer: root_cert.subject(),     // Issuer = Root-CA Subject
│   │           validity: validity,
│   │           subject: csr.info.subject,       // Subject iz CSR-a
│   │           subject_public_key_info: csr.info.subject_public_key_info,
│   │           extensions: Some(extensions)
│   │       }
│   │
│   ├── // Potpisivanje s Root-CA
│   │   └── signature = ml_dsa::SigningKey::sign(&tbs_der)
│   │
│   └── cert = x509_cert::Certificate { tbs, signature_algorithm, signature }
│
├── wvds_sec_crypto_x509_cert_to_pem(intermediate_cert, out, out_len)
│   └── pem_rfc7468::encode_string("CERTIFICATE", &cert_der)
│
└── // Čišćenje
    ├── wvds_sec_crypto_x509_free_csr(csr)
    ├── wvds_sec_crypto_x509_free_cert(root_cert)
    ├── wvds_sec_crypto_x509_free_cert(intermediate_cert)
    ├── wvds_sec_crypto_x509_free_keypair(root_keypair)
    └── wvds_sec_crypto_x509_shutdown()


═══════════════════════════════════════════════════════════════════════════
 FAZA 3: Deployment (Intermediate-CA sustav)
═══════════════════════════════════════════════════════════════════════════

wvds_sec_crypto_x509_init()
│
├── // Kreiranje lanca certifikata
│   │
│   ├── wvds_sec_crypto_x509_cert_load_pem(intermediate_cert_pem)  → intermediate
│   ├── wvds_sec_crypto_x509_cert_load_pem(root_cert_pem)  → root
│   │
│   ├── wvds_sec_crypto_x509_chain_create()  → *mut ChainHandle
│   ├── wvds_sec_crypto_x509_chain_add_cert(chain, intermediate)
│   ├── wvds_sec_crypto_x509_chain_add_cert(chain, root)
│   │
│   └── wvds_sec_crypto_x509_chain_to_pem(chain, out, out_len)
│       │
│       └── // ca-chain.pem:
│           // -----BEGIN CERTIFICATE-----
│           // (Intermediate-CA)
│           // -----END CERTIFICATE-----
│           // -----BEGIN CERTIFICATE-----
│           // (Root-CA)
│           // -----END CERTIFICATE-----
│
├── // Validacija
│   │
│   ├── wvds_sec_crypto_x509_validate_chain(chain, trust_store)
│   └── wvds_sec_crypto_x509_cert_verify_signature(intermediate, root)
│
└── wvds_sec_crypto_x509_shutdown()

Primjeri koda

C# (.NET Wrapper)

using System;
using System.IO;
using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
 
namespace IntermediateCaExample
{
    class Program
    {
        // Lozinke (u produkciji: sigurno unijeti!)
        const string ROOT_KEY_PASSWORD = "MyStr0ng!RootCA#Password2024";
        const string INTERMEDIATE_KEY_PASSWORD = "MyStr0ng!IntermediateCA#2024";
 
        static void Main(string[] args)
        {
            // === FAZA 1: Kreiranje CSR-a (Intermediate-CA sustav) ===
            Console.WriteLine("=== FAZA 1: Kreiranje CSR-a ===\n");
 
            using var context = PqCryptoContext.Initialize();
 
            // 1. Generiranje para ključeva za Intermediate-CA
            Console.WriteLine("Generiram ML-DSA-65 par ključeva za Intermediate-CA...");
            using var intermediateKeyPair = context.GenerateKeyPair(PqAlgorithm.MlDsa65);
            intermediateKeyPair.SelfTest();
 
            // 2. Distinguished Name
            var intermediateDn = new DistinguishedNameBuilder()
                .AddCommonName("WvdS Server CA")
                .AddOrganization("DATECpro GmbH")
                .AddOrganizationalUnit("PKI Services")
                .AddCountry("DE")
                .Build();
 
            Console.WriteLine($"Intermediate DN: {intermediateDn}");
 
            // 3. Željene ekstenzije za CSR
            var csrExtensions = new X509ExtensionsBuilder()
                .AddBasicConstraints(isCa: true, pathLengthConstraint: 0, critical: true)
                .AddKeyUsage(KeyUsageFlags.KeyCertSign | KeyUsageFlags.CrlSign, critical: true)
                .Build();
 
            // 4. Kreiranje i potpisivanje CSR-a
            Console.WriteLine("Kreiram Certificate Signing Request...");
            using var csr = context.CreateCertificateRequest(
                subject: intermediateDn,
                keyPair: intermediateKeyPair,
                extensions: csrExtensions
            );
 
            // 5. Spremanje CSR-a kao PEM
            string csrPem = csr.ExportToPem();
            File.WriteAllText("intermediate-ca.csr.pem", csrPem);
            Console.WriteLine("CSR spremljen: intermediate-ca.csr.pem");
 
            // 6. Šifrirano spremanje Intermediate privatnog ključa
            File.WriteAllText("intermediate-ca.key.pem",
                intermediateKeyPair.ExportToEncryptedPem(INTERMEDIATE_KEY_PASSWORD));
            Console.WriteLine("Privatni ključ spremljen: intermediate-ca.key.pem\n");
 
            // === FAZA 2: Izdavanje certifikata (Root-CA sustav) ===
            Console.WriteLine("=== FAZA 2: Izdavanje certifikata od Root-CA ===\n");
 
            // 7. Učitavanje Root-CA materijala
            Console.WriteLine("Učitavam Root-CA...");
            using var rootCert = context.LoadCertificateFromPem(
                File.ReadAllText("root-ca.crt.pem")
            );
            using var rootKeyPair = context.LoadKeyPairFromEncryptedPem(
                File.ReadAllText("root-ca.key.pem"),
                ROOT_KEY_PASSWORD
            );
 
            Console.WriteLine($"Root-CA učitan: {rootCert.Subject}");
 
            // 8. Učitavanje i validacija CSR-a
            Console.WriteLine("Učitavam i validiram CSR...");
            using var loadedCsr = context.LoadCertificateRequestFromPem(csrPem);
 
            if (!loadedCsr.VerifySignature())
                throw new CryptographicException("Potpis CSR-a nevažeći!");
            Console.WriteLine("Potpis CSR-a: OK");
 
            // 9. Ekstenzije za Intermediate certifikat (CA određuje)
            var certExtensions = new X509ExtensionsBuilder()
                // BasicConstraints: CA, ali bez daljnje Sub-CA
                .AddBasicConstraints(isCa: true, pathLengthConstraint: 0, critical: true)
                // KeyUsage: Potpisivanje certifikata i CRL-a
                .AddKeyUsage(KeyUsageFlags.KeyCertSign | KeyUsageFlags.CrlSign, critical: true)
                // Subject Key Identifier (iz CSR javnog ključa)
                .AddSubjectKeyIdentifier(loadedCsr.PublicKey)
                // Authority Key Identifier (= Root-CA SKI)
                .AddAuthorityKeyIdentifier(rootCert)
                // CRL Distribution Point
                .AddCrlDistributionPoint("http://crl.datecpro.de/root-ca.crl")
                // Authority Info Access (OCSP + CA Issuers)
                .AddAuthorityInfoAccess(
                    ocspUri: "http://ocsp.datecpro.de",
                    caIssuersUri: "http://ca.datecpro.de/root-ca.crt"
                )
                .Build();
 
            // 10. Valjanost: 10 godina (kraće od Root-CA)
            var validity = new CertificateValidity(
                notBefore: DateTime.UtcNow,
                notAfter: DateTime.UtcNow.AddYears(10)
            );
 
            // 11. Izdavanje Intermediate-CA certifikata
            Console.WriteLine("Izdajem Intermediate-CA certifikat...");
            using var intermediateCert = context.IssueCertificateFromRequest(
                request: loadedCsr,
                issuerCertificate: rootCert,
                issuerKeyPair: rootKeyPair,
                validity: validity,
                extensions: certExtensions
            );
 
            // 12. Ispis informacija o certifikatu
            Console.WriteLine("\n=== INTERMEDIATE-CA CERTIFIKAT ===");
            Console.WriteLine($"Subject:      {intermediateCert.Subject}");
            Console.WriteLine($"Issuer:       {intermediateCert.Issuer}");
            Console.WriteLine($"Serial:       {intermediateCert.SerialNumber}");
            Console.WriteLine($"Not Before:   {intermediateCert.NotBefore:yyyy-MM-dd}");
            Console.WriteLine($"Not After:    {intermediateCert.NotAfter:yyyy-MM-dd}");
            Console.WriteLine($"Algoritam:    {intermediateCert.SignatureAlgorithm}");
            Console.WriteLine($"Is CA:        {intermediateCert.IsCertificateAuthority}");
            Console.WriteLine($"Path Length:  {intermediateCert.PathLengthConstraint}");
            Console.WriteLine($"Thumbprint:   {intermediateCert.Thumbprint}");
 
            // 13. Spremanje certifikata
            File.WriteAllText("intermediate-ca.crt.pem", intermediateCert.ExportToPem());
            Console.WriteLine("\nCertifikat spremljen: intermediate-ca.crt.pem");
 
            // === FAZA 3: Kreiranje lanca ===
            Console.WriteLine("\n=== FAZA 3: Lanac certifikata ===\n");
 
            // 14. Kreiranje CA-Chain-a (Intermediate + Root)
            using var chain = context.CreateCertificateChain();
            chain.Add(intermediateCert);
            chain.Add(rootCert);
 
            File.WriteAllText("ca-chain.pem", chain.ExportToPem());
            Console.WriteLine("CA-Chain spremljen: ca-chain.pem");
 
            // 15. Validacija
            Console.WriteLine("\n=== VALIDACIJA ===");
 
            // Provjera potpisa (Intermediate potpisan od Root?)
            bool signatureValid = intermediateCert.VerifySignature(rootCert);
            Console.WriteLine($"Potpis od Root-CA: {(signatureValid ? "OK" : "GREŠKA")}");
 
            // Validacija lanca
            using var trustStore = context.CreateTrustStore();
            trustStore.AddCertificate(rootCert);
 
            var validationResult = context.ValidateChain(chain, trustStore);
            Console.WriteLine($"Validacija lanca:      {(validationResult.IsValid ? "OK" : "GREŠKA")}");
 
            Console.WriteLine("\n✓ Intermediate-CA uspješno kreiran!");
        }
    }
}

Delphi (FFI)

program CreateIntermediateCA;
 
{$APPTYPE CONSOLE}
 
uses
  SysUtils, DateUtils, Classes,
  WvdS.PqCrypto.FFI;
 
const
  ROOT_KEY_PASSWORD = 'MyStr0ng!RootCA#Password2024';
  INTERMEDIATE_KEY_PASSWORD = 'MyStr0ng!IntermediateCA#2024';
 
var
  res: Integer;
  // Intermediate-CA
  intKeyPair: Pointer;
  intDn: Pointer;
  intExt: Pointer;
  csr: Pointer;
  intCert: Pointer;
  // Root-CA
  rootCert: Pointer;
  rootKeyPair: Pointer;
  // Kreiranje certifikata
  certExt: Pointer;
  validity: Pointer;
  serial: array[0..19] of Byte;
  chain: Pointer;
  // Buffer
  pem_buf: array[0..65535] of AnsiChar;
  pem_len: NativeUInt;
 
  function LoadFileToString(const FileName: string): AnsiString;
  begin
    with TStringList.Create do
    try
      LoadFromFile(FileName);
      Result := AnsiString(Text);
    finally
      Free;
    end;
  end;
 
  procedure SaveStringToFile(const FileName: string; const Content: AnsiString);
  begin
    with TFileStream.Create(FileName, fmCreate) do
    try
      Write(Content[1], Length(Content));
    finally
      Free;
    end;
  end;
 
begin
  WriteLn('=== WvdS Kreiranje Intermediate-CA ===');
  WriteLn;
 
  res := wvds_sec_crypto_x509_init();
  if res <> WVDS_OK then
  begin
    WriteLn('GREŠKA: Inicijalizacija neuspješna');
    Exit;
  end;
 
  try
    // ═══════════════════════════════════════════════════════════
    // FAZA 1: Kreiranje CSR-a
    // ═══════════════════════════════════════════════════════════
    WriteLn('=== FAZA 1: Kreiranje CSR-a ===');
    WriteLn;
 
    // 1. Generiranje para ključeva
    WriteLn('Generiram ML-DSA-65 par ključeva...');
    intKeyPair := wvds_sec_crypto_x509_keypair_generate_mldsa(65);
    if intKeyPair = nil then
      raise Exception.Create('Generiranje para ključeva neuspješno');
 
    // 2. Distinguished Name
    intDn := wvds_sec_crypto_x509_dn_create();
    wvds_sec_crypto_x509_dn_add_component(intDn, '2.5.4.3', 'WvdS Server CA');
    wvds_sec_crypto_x509_dn_add_component(intDn, '2.5.4.10', 'DATECpro GmbH');
    wvds_sec_crypto_x509_dn_add_component(intDn, '2.5.4.11', 'PKI Services');
    wvds_sec_crypto_x509_dn_add_component(intDn, '2.5.4.6', 'DE');
 
    // 3. Ekstenzije za CSR
    intExt := wvds_sec_crypto_x509_ext_create();
    wvds_sec_crypto_x509_ext_set_basic_constraints(intExt, True, 0);
    wvds_sec_crypto_x509_ext_set_key_usage(intExt, $0006);  // keyCertSign + cRLSign
 
    // 4. Kreiranje CSR-a
    WriteLn('Kreiram CSR...');
    csr := wvds_sec_crypto_x509_csr_create(intDn, intKeyPair, intExt);
    if csr = nil then
      raise Exception.Create('Kreiranje CSR-a neuspješno');
 
    // 5. Potpisivanje CSR-a
    res := wvds_sec_crypto_x509_csr_sign(csr, intKeyPair);
    if res <> WVDS_OK then
      raise Exception.Create('Potpisivanje CSR-a neuspješno');
 
    // 6. Spremanje CSR-a kao PEM
    pem_len := SizeOf(pem_buf);
    wvds_sec_crypto_x509_csr_to_pem(csr, @pem_buf[0], @pem_len);
    SaveStringToFile('intermediate-ca.csr.pem', pem_buf);
    WriteLn('CSR spremljen: intermediate-ca.csr.pem');
 
    // 7. Spremanje privatnog ključa
    pem_len := SizeOf(pem_buf);
    wvds_sec_crypto_x509_keypair_to_pem_encrypted(
      intKeyPair, INTERMEDIATE_KEY_PASSWORD, WVDS_KDF_ARGON2ID, @pem_buf[0], @pem_len
    );
    SaveStringToFile('intermediate-ca.key.pem', pem_buf);
    WriteLn('Privatni ključ spremljen: intermediate-ca.key.pem');
    WriteLn;
 
    // ═══════════════════════════════════════════════════════════
    // FAZA 2: Izdavanje certifikata od Root-CA
    // ═══════════════════════════════════════════════════════════
    WriteLn('=== FAZA 2: Izdavanje certifikata ===');
    WriteLn;
 
    // 8. Učitavanje Root-CA
    WriteLn('Učitavam Root-CA...');
    rootCert := wvds_sec_crypto_x509_cert_load_pem(
      PAnsiChar(LoadFileToString('root-ca.crt.pem'))
    );
    if rootCert = nil then
      raise Exception.Create('Učitavanje Root certifikata neuspješno');
 
    rootKeyPair := wvds_sec_crypto_x509_keypair_load_pem_encrypted(
      PAnsiChar(LoadFileToString('root-ca.key.pem')),
      ROOT_KEY_PASSWORD
    );
    if rootKeyPair = nil then
      raise Exception.Create('Učitavanje Root ključa neuspješno');
 
    // 9. Validacija CSR-a
    WriteLn('Validiram CSR...');
    res := wvds_sec_crypto_x509_csr_verify(csr);
    if res <> WVDS_OK then
      raise Exception.Create('Potpis CSR-a nevažeći');
    WriteLn('Potpis CSR-a: OK');
 
    // 10. Ekstenzije za certifikat
    certExt := wvds_sec_crypto_x509_ext_create();
    wvds_sec_crypto_x509_ext_set_basic_constraints(certExt, True, 0);
    wvds_sec_crypto_x509_ext_set_key_usage(certExt, $0006);
    wvds_sec_crypto_x509_ext_set_ski_from_csr(certExt, csr);
    wvds_sec_crypto_x509_ext_set_aki_from_cert(certExt, rootCert);
    wvds_sec_crypto_x509_ext_set_crl_distribution_points(
      certExt,
      PAnsiChar('http://crl.datecpro.de/root-ca.crl'),
      1
    );
    wvds_sec_crypto_x509_ext_set_aia(
      certExt,
      'http://ocsp.datecpro.de',
      'http://ca.datecpro.de/root-ca.crt'
    );
 
    // 11. Serijski broj i valjanost
    wvds_sec_crypto_x509_serial_generate(@serial[0], 20);
    validity := wvds_sec_crypto_x509_validity_create(
      DateTimeToUnix(Now, False),
      DateTimeToUnix(IncYear(Now, 10), False)  // 10 godina
    );
 
    // 12. Izdavanje certifikata
    WriteLn('Izdajem certifikat...');
    intCert := wvds_sec_crypto_x509_cert_create_from_csr(
      csr,
      rootCert,
      rootKeyPair,
      @serial[0], 20,
      validity,
      certExt
    );
    if intCert = nil then
      raise Exception.Create('Kreiranje certifikata neuspješno');
 
    // 13. Spremanje certifikata
    pem_len := SizeOf(pem_buf);
    wvds_sec_crypto_x509_cert_to_pem(intCert, @pem_buf[0], @pem_len);
    SaveStringToFile('intermediate-ca.crt.pem', pem_buf);
    WriteLn('Certifikat spremljen: intermediate-ca.crt.pem');
    WriteLn;
 
    // ═══════════════════════════════════════════════════════════
    // FAZA 3: Kreiranje lanca
    // ═══════════════════════════════════════════════════════════
    WriteLn('=== FAZA 3: CA-Chain ===');
    WriteLn;
 
    chain := wvds_sec_crypto_x509_chain_create();
    wvds_sec_crypto_x509_chain_add_cert(chain, intCert);
    wvds_sec_crypto_x509_chain_add_cert(chain, rootCert);
 
    pem_len := SizeOf(pem_buf);
    wvds_sec_crypto_x509_chain_to_pem(chain, @pem_buf[0], @pem_len);
    SaveStringToFile('ca-chain.pem', pem_buf);
    WriteLn('CA-Chain spremljen: ca-chain.pem');
 
    WriteLn;
    WriteLn('=== Intermediate-CA uspješno kreiran! ===');
 
  finally
    // Čišćenje
    if chain <> nil then wvds_sec_crypto_x509_free_chain(chain);
    if intCert <> nil then wvds_sec_crypto_x509_free_cert(intCert);
    if certExt <> nil then wvds_sec_crypto_x509_free_ext(certExt);
    if validity <> nil then wvds_sec_crypto_x509_free_validity(validity);
    if rootKeyPair <> nil then wvds_sec_crypto_x509_free_keypair(rootKeyPair);
    if rootCert <> nil then wvds_sec_crypto_x509_free_cert(rootCert);
    if csr <> nil then wvds_sec_crypto_x509_free_csr(csr);
    if intExt <> nil then wvds_sec_crypto_x509_free_ext(intExt);
    if intDn <> nil then wvds_sec_crypto_x509_free_dn(intDn);
    if intKeyPair <> nil then wvds_sec_crypto_x509_free_keypair(intKeyPair);
    wvds_sec_crypto_x509_shutdown();
  end;
end.

Ekstenzije za Intermediate-CA

Ekstenzija OID Vrijednost Critical Opis
Basic Constraints 2.5.29.19 CA=true, pathLen=0 ✅ Da Bez daljnje Sub-CA
Key Usage 2.5.29.15 keyCertSign, cRLSign ✅ Da Samo potpisivanje certifikata/CRL-a
Subject Key Identifier 2.5.29.14 SHA-256(publicKey) ❌ Ne Jedinstveni ID ključa
Authority Key Identifier 2.5.29.35 Root-CA SKI ❌ Ne Referenca na Issuer ključ
CRL Distribution Points 2.5.29.31 URL(s) ❌ Ne Gdje je CRL dostupan
Authority Info Access 1.3.6.1.5.5.7.1.1 OCSP + caIssuers ❌ Ne OCSP URL i URL CA certifikata
Certificate Policies 2.5.29.32 Policy OID(s) ❌ Ne Opcijski: Pravila izdavanja
Extended Key Usage 2.5.29.37 Opcijski ❌ Ne Opcijski: Ograničenje namjene

Razlike u odnosu na Root-CA

Aspekt Root-CA Intermediate-CA
Issuer = Subject (samopotpisan) = Root-CA Subject
Potpisuje Vlastiti ključ Root-CA ključ
pathLength 1 ili 2 0 (samo End-Entity)
Valjanost 15-25 godina 8-12 godina
AKI Nije prisutan Root-CA SKI
CDP Opcijski Obavezan
AIA Nije prisutan OCSP + caIssuers
Pohrana ključa Offline / HSM Online (zaštićeno)
Uporaba Samo potpisivanje Intermediate-CA Potpisivanje End-Entity certifikata

Sigurnosne napomene

Zaštita ključa Intermediate-CA:

  • Privatni ključ šifrirano pohraniti (Argon2id + AES-256-GCM)
  • Idealno u HSM-u ili hardverskom tokenu
  • Ograničiti pristup (samo CA servis)
  • Audit logging za sve operacije potpisivanja
  • Planirati redovitu rotaciju ključeva

Best Practices:

  • pathLength=0 sprječava daljnje Sub-CA ispod ovog Intermediate-CA
  • Odvojeni Intermediate-CA za različite namjene (Server, Client, Code-Signing)
  • CDP i AIA uvijek postaviti za provjeru opoziva
  • CSR prenijeti offline (USB stick) na Air-Gapped Root-CA sustav
  • Lanac certifikata (ca-chain.pem) pripremiti za jednostavan deployment

Izbjegavajte česte greške:

  • Ne kreirati Intermediate-CA s istim ključem kao Root
  • Ne postavljati pathLength > 0 ako nije potrebna Sub-Sub-CA
  • Ne zaboraviti AKI (otežava izgradnju lanca)
  • Ne zaboraviti CDP/AIA (provjera opoziva neće raditi)

Izlazne datoteke

Datoteka Tip Opis
intermediate-ca.csr.pem PKCS#10 Certificate Signing Request
intermediate-ca.key.pem PKCS#8 (encrypted) Šifrirani privatni ključ
intermediate-ca.crt.pem X.509 Intermediate-CA certifikat
ca-chain.pem PEM Bundle Intermediate + Root (za deployment)

Povratne vrijednosti

Kod Konstanta Značenje
0 WVDS_OK Uspjeh
1 WVDS_ERROR_INVALID_PARAMETER Nevaljani parametar
105 WVDS_ERROR_INVALID_CSR Nevažeći format CSR-a
202 WVDS_ERROR_SIGNATURE_INVALID Nevažeći potpis CSR-a
208 WVDS_ERROR_WRONG_PASSWORD Pogrešna lozinka Root ključa

Povezani scenariji

Odnos Scenarij Opis
Preduvjet 1.1 Kreiranje Root-CA Root-CA mora postojati
Sljedeći korak 1.4 Trust Store Distribucija Root certifikata
Sljedeći korak 1.6 CRL/OCSP Postavljanje opoziva
Zatim 3.1 Server certifikat Izdavanje End-Entity certifikata
Alternativno 1.3 CA hijerarhija Više Intermediate-CA

Reference

Zuletzt geändert: 30.01.2026. u 06:46