Scenario 1.2: Creare Intermediate-CA

Categoria: Infrastruttura PKI
Complessità: Media
Prerequisiti: Scenario 1.1: Creare Root-CA
Tempo stimato: 20-40 minuti


Descrizione

Questo scenario descrive la creazione di una Intermediate-CA (chiamata anche Sub-CA o Issuing-CA), firmata dalla Root-CA. L'Intermediate-CA emette certificati end-entity e protegge la chiave Root-CA, che può rimanere offline.

Cosa viene creato:

  • Coppia di chiavi ML-DSA-65 per Intermediate-CA
  • Certificate Signing Request (CSR)
  • Certificato Intermediate firmato dalla Root-CA
  • Chiave privata crittografata

Vantaggi di una Intermediate-CA:

  • Sicurezza: Root-CA rimane offline, solo Intermediate-CA online
  • Flessibilità: Più Intermediate-CA per scopi diversi (Server, Client, Code-Signing)
  • Revoca: Intermediate-CA può essere revocata senza compromettere Root
  • Conformità: Corrisponde alle best practices (CA/Browser Forum, BSI)

Tipi tipici di Intermediate-CA:

  • Server-CA: Per certificati TLS server
  • Client-CA: Per certificati mTLS client
  • User-CA: Per certificati E-Mail/S-MIME
  • CodeSign-CA: Per certificati code-signing
  • Device-CA: Per certificati IoT/dispositivi

Diagramma di flusso

┌─────────────────────────────────────────────────────────────────────────┐
│                    CREAZIONE INTERMEDIATE-CA                            │
│                                                                         │
│   ┌─────────────────────────┐       ┌─────────────────────────┐        │
│   │   INTERMEDIATE-CA       │       │      ROOT-CA            │        │
│   │   (Sistema Online)      │       │   (Sistema Air-Gapped)  │        │
│   └─────────────────────────┘       └─────────────────────────┘        │
└─────────────────────────────────────────────────────────────────────────┘

     ┌──────────────┐
     │ 1. Init      │
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 2. KeyPair   │ ──────► ML-DSA-65 per Intermediate
     │ generare     │
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 3. DN        │ ──────► CN=WvdS Server CA
     │ creare       │         O=DATECpro GmbH
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 4. Extensions│ ──────► BasicConstraints, KeyUsage, EKU
     │ per CSR      │         (extensions desiderate)
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 5. CSR       │ ──────► PKCS#10 Request
     │ creare       │         Firmato con chiave Intermediate
     └──────┬───────┘
            │
            │  ══════════════════════════════════════════
            │  Trasferimento CSR (chiavetta USB, canale sicuro)
            ▼  ══════════════════════════════════════════

     ┌──────────────┐
     │ 6. Root-CA   │ ──────► Caricare root-ca.key.pem
     │ caricare     │         (Sistema Air-Gapped)
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 7. CSR       │ ──────► Verificare: DN, firma, extensions
     │ validare     │
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 8. Extensions│ ──────► BC, KU, SKI, AKI, CDP, AIA
     │ impostare    │         (CA determina extensions finali)
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 9. Certificato│ ──────► Root-CA firma
     │ emettere     │         certificato Intermediate
     └──────┬───────┘
            │
            │  ══════════════════════════════════════════
            │  Trasferimento certificato indietro
            ▼  ══════════════════════════════════════════

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

Funzioni coinvolte

Lato Intermediate-CA (Creazione CSR)

Passo Funzione FFI Crate Rust Descrizione
1 wvds_sec_crypto_x509_init() std::sync Inizializzare libreria
2 wvds_sec_crypto_x509_keypair_generate_mldsa(65) ml-dsa Coppia chiavi ML-DSA-65
3a wvds_sec_crypto_x509_dn_create() x509-cert Creare handle DN
3b wvds_sec_crypto_x509_dn_add_component() x509-cert Aggiungere CN, O, OU, C
4 wvds_sec_crypto_x509_ext_set_*() x509-cert Extensions desiderate
5a wvds_sec_crypto_x509_csr_create() x509-cert Creare CSR
5b wvds_sec_crypto_x509_csr_sign() ml-dsa Firmare CSR
5c wvds_sec_crypto_x509_csr_to_pem() pem-rfc7468 Esportare CSR come PEM

Lato Root-CA (Emissione certificato)

Passo Funzione FFI Crate Rust Descrizione
6a wvds_sec_crypto_x509_cert_load_pem() x509-parser Caricare certificato Root
6b wvds_sec_crypto_x509_keypair_load_pem_encrypted() pkcs8, argon2 Caricare chiave Root
7a wvds_sec_crypto_x509_csr_load_pem() x509-cert Caricare CSR
7b wvds_sec_crypto_x509_csr_verify() ml-dsa Verificare firma CSR
7c wvds_sec_crypto_x509_csr_get_subject() x509-cert Estrarre Subject
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 da PublicKey CSR
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 URL CRL
8f wvds_sec_crypto_x509_ext_set_aia() x509-cert URL OCSP + CA Issuers
9 wvds_sec_crypto_x509_cert_create_from_csr() x509-cert, ml-dsa Creare certificato
10 wvds_sec_crypto_x509_cert_to_pem() pem-rfc7468 Esportare certificato
11 wvds_sec_crypto_x509_chain_create() Creare handle Chain

Albero delle funzioni

═══════════════════════════════════════════════════════════════════════════
 FASE 1: Creazione CSR (Sistema Intermediate-CA)
═══════════════════════════════════════════════════════════════════════════

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")
│
├── // Extensions per CSR (opzionali, CA può sovrascrivere)
│   ├── 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
│   │
│   ├── // Costruire CertificationRequestInfo
│   │   └── cri = x509_cert::request::CertReqInfo {
│   │           version: 0,
│   │           subject: dn.clone(),
│   │           subject_public_key_info: keypair.to_spki(),
│   │           attributes: extensions.to_csr_attributes()
│   │       }
│   │
│   └── // Firmare CSR con chiave Intermediate
│       └── 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)
│
├── // Salvare CSR su chiavetta USB → Sistema Air-Gapped
│
└── wvds_sec_crypto_x509_keypair_to_pem_encrypted(keypair, password, ARGON2ID)
    └── // Salvare in sicurezza chiave privata Intermediate


═══════════════════════════════════════════════════════════════════════════
 FASE 2: Emissione certificato (Sistema Root-CA Air-Gapped)
═══════════════════════════════════════════════════════════════════════════

wvds_sec_crypto_x509_init()
│
├── // Caricare materiali Root-CA
│   │
│   ├── 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)
│
├── // Caricare e validare CSR
│   │
│   ├── 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
│   │   │
│   │   ├── // Verificare firma
│   │   │   ├── 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 o 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()
│
├── // Extensions per certificato Intermediate
│   │
│   ├── wvds_sec_crypto_x509_ext_create()  → *mut ExtHandle
│   │
│   ├── wvds_sec_crypto_x509_ext_set_basic_constraints(ext, ca=true, path_len=0)
│   │   │
│   │   └── // pathLen=0: Questa CA può emettere solo certificati End-Entity
│   │       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 della 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")
│   │           }
│   │       ])
│   │
│   └── // Opzionale: Certificate Policies, Name Constraints, ecc.
│
├── // Numero seriale e validità
│   │
│   ├── wvds_sec_crypto_x509_serial_generate(serial, 20)
│   │
│   └── wvds_sec_crypto_x509_validity_create(now, now + 10 years)
│       └── // Intermediate-CA: 8-12 anni (più breve di Root)
│
├── wvds_sec_crypto_x509_cert_create_from_csr(
│       csr,
│       issuer_cert: root_cert,
│       issuer_key: root_keypair,
│       serial,
│       validity,
│       extensions
│   )  → *mut CertHandle
│   │
│   ├── // Costruire 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 da CSR
│   │           subject_public_key_info: csr.info.subject_public_key_info,
│   │           extensions: Some(extensions)
│   │       }
│   │
│   ├── // Firmare con 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)
│
└── // Cleanup
    ├── 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()


═══════════════════════════════════════════════════════════════════════════
 FASE 3: Deployment (Sistema Intermediate-CA)
═══════════════════════════════════════════════════════════════════════════

wvds_sec_crypto_x509_init()
│
├── // Creare catena certificati
│   │
│   ├── 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-----
│
├── // Validazione
│   │
│   ├── wvds_sec_crypto_x509_validate_chain(chain, trust_store)
│   └── wvds_sec_crypto_x509_cert_verify_signature(intermediate, root)
│
└── wvds_sec_crypto_x509_shutdown()

Esempi di codice

C# (.NET Wrapper)

using System;
using System.IO;
using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
 
namespace IntermediateCaExample
{
    class Program
    {
        // Password (in produzione: inserire in modo sicuro!)
        const string ROOT_KEY_PASSWORD = "MyStr0ng!RootCA#Password2024";
        const string INTERMEDIATE_KEY_PASSWORD = "MyStr0ng!IntermediateCA#2024";
 
        static void Main(string[] args)
        {
            // === FASE 1: Creare CSR (Sistema Intermediate-CA) ===
            Console.WriteLine("=== FASE 1: Creazione CSR ===\n");
 
            using var context = PqCryptoContext.Initialize();
 
            // 1. Generare coppia chiavi per Intermediate-CA
            Console.WriteLine("Generazione coppia chiavi ML-DSA-65 per 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. Extensions desiderate per CSR
            var csrExtensions = new X509ExtensionsBuilder()
                .AddBasicConstraints(isCa: true, pathLengthConstraint: 0, critical: true)
                .AddKeyUsage(KeyUsageFlags.KeyCertSign | KeyUsageFlags.CrlSign, critical: true)
                .Build();
 
            // 4. Creare e firmare CSR
            Console.WriteLine("Creazione Certificate Signing Request...");
            using var csr = context.CreateCertificateRequest(
                subject: intermediateDn,
                keyPair: intermediateKeyPair,
                extensions: csrExtensions
            );
 
            // 5. Salvare CSR come PEM
            string csrPem = csr.ExportToPem();
            File.WriteAllText("intermediate-ca.csr.pem", csrPem);
            Console.WriteLine("CSR salvato: intermediate-ca.csr.pem");
 
            // 6. Salvare chiave privata Intermediate crittografata
            File.WriteAllText("intermediate-ca.key.pem",
                intermediateKeyPair.ExportToEncryptedPem(INTERMEDIATE_KEY_PASSWORD));
            Console.WriteLine("Chiave privata salvata: intermediate-ca.key.pem\n");
 
            // === FASE 2: Emettere certificato (Sistema Root-CA) ===
            Console.WriteLine("=== FASE 2: Emissione certificato da Root-CA ===\n");
 
            // 7. Caricare materiali Root-CA
            Console.WriteLine("Caricamento 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 caricata: {rootCert.Subject}");
 
            // 8. Caricare e validare CSR
            Console.WriteLine("Caricamento e validazione CSR...");
            using var loadedCsr = context.LoadCertificateRequestFromPem(csrPem);
 
            if (!loadedCsr.VerifySignature())
                throw new CryptographicException("Firma CSR non valida!");
            Console.WriteLine("Firma CSR: OK");
 
            // 9. Extensions per certificato Intermediate (CA determina)
            var certExtensions = new X509ExtensionsBuilder()
                // BasicConstraints: CA, ma nessuna ulteriore Sub-CA consentita
                .AddBasicConstraints(isCa: true, pathLengthConstraint: 0, critical: true)
                // KeyUsage: Firma certificati e CRL
                .AddKeyUsage(KeyUsageFlags.KeyCertSign | KeyUsageFlags.CrlSign, critical: true)
                // Subject Key Identifier (da chiave pubblica CSR)
                .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. Validità: 10 anni (più breve di Root-CA)
            var validity = new CertificateValidity(
                notBefore: DateTime.UtcNow,
                notAfter: DateTime.UtcNow.AddYears(10)
            );
 
            // 11. Emettere certificato Intermediate
            Console.WriteLine("Emissione certificato Intermediate-CA...");
            using var intermediateCert = context.IssueCertificateFromRequest(
                request: loadedCsr,
                issuerCertificate: rootCert,
                issuerKeyPair: rootKeyPair,
                validity: validity,
                extensions: certExtensions
            );
 
            // 12. Visualizzare informazioni certificato
            Console.WriteLine("\n=== CERTIFICATO INTERMEDIATE-CA ===");
            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($"Algorithm:    {intermediateCert.SignatureAlgorithm}");
            Console.WriteLine($"Is CA:        {intermediateCert.IsCertificateAuthority}");
            Console.WriteLine($"Path Length:  {intermediateCert.PathLengthConstraint}");
            Console.WriteLine($"Thumbprint:   {intermediateCert.Thumbprint}");
 
            // 13. Salvare certificato
            File.WriteAllText("intermediate-ca.crt.pem", intermediateCert.ExportToPem());
            Console.WriteLine("\nCertificato salvato: intermediate-ca.crt.pem");
 
            // === FASE 3: Creare Chain ===
            Console.WriteLine("\n=== FASE 3: Certificate Chain ===\n");
 
            // 14. Creare CA-Chain (Intermediate + Root)
            using var chain = context.CreateCertificateChain();
            chain.Add(intermediateCert);
            chain.Add(rootCert);
 
            File.WriteAllText("ca-chain.pem", chain.ExportToPem());
            Console.WriteLine("CA-Chain salvata: ca-chain.pem");
 
            // 15. Validazione
            Console.WriteLine("\n=== VALIDAZIONE ===");
 
            // Verificare firma (Intermediate firmato da Root?)
            bool signatureValid = intermediateCert.VerifySignature(rootCert);
            Console.WriteLine($"Firma da Root-CA: {(signatureValid ? "OK" : "ERRORE")}");
 
            // Validazione Chain
            using var trustStore = context.CreateTrustStore();
            trustStore.AddCertificate(rootCert);
 
            var validationResult = context.ValidateChain(chain, trustStore);
            Console.WriteLine($"Validazione Chain:      {(validationResult.IsValid ? "OK" : "ERRORE")}");
 
            Console.WriteLine("\n✓ Intermediate-CA creata con successo!");
        }
    }
}

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;
  // Creazione certificato
  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 Creazione Intermediate-CA ===');
  WriteLn;
 
  res := wvds_sec_crypto_x509_init();
  if res <> WVDS_OK then
  begin
    WriteLn('ERRORE: Init fallito');
    Exit;
  end;
 
  try
    // ═══════════════════════════════════════════════════════════
    // FASE 1: Creare CSR
    // ═══════════════════════════════════════════════════════════
    WriteLn('=== FASE 1: Creazione CSR ===');
    WriteLn;
 
    // 1. Generare coppia chiavi
    WriteLn('Generazione coppia chiavi ML-DSA-65...');
    intKeyPair := wvds_sec_crypto_x509_keypair_generate_mldsa(65);
    if intKeyPair = nil then
      raise Exception.Create('Generazione KeyPair fallita');
 
    // 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. Extensions per 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. Creare CSR
    WriteLn('Creazione CSR...');
    csr := wvds_sec_crypto_x509_csr_create(intDn, intKeyPair, intExt);
    if csr = nil then
      raise Exception.Create('Creazione CSR fallita');
 
    // 5. Firmare CSR
    res := wvds_sec_crypto_x509_csr_sign(csr, intKeyPair);
    if res <> WVDS_OK then
      raise Exception.Create('Firma CSR fallita');
 
    // 6. Salvare CSR come 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 salvato: intermediate-ca.csr.pem');
 
    // 7. Salvare chiave privata
    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('Chiave privata salvata: intermediate-ca.key.pem');
    WriteLn;
 
    // ═══════════════════════════════════════════════════════════
    // FASE 2: Emettere certificato da Root-CA
    // ═══════════════════════════════════════════════════════════
    WriteLn('=== FASE 2: Emissione certificato ===');
    WriteLn;
 
    // 8. Caricare Root-CA
    WriteLn('Caricamento Root-CA...');
    rootCert := wvds_sec_crypto_x509_cert_load_pem(
      PAnsiChar(LoadFileToString('root-ca.crt.pem'))
    );
    if rootCert = nil then
      raise Exception.Create('Caricamento certificato Root fallito');
 
    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('Caricamento chiave Root fallito');
 
    // 9. Validare CSR
    WriteLn('Validazione CSR...');
    res := wvds_sec_crypto_x509_csr_verify(csr);
    if res <> WVDS_OK then
      raise Exception.Create('Firma CSR non valida');
    WriteLn('Firma CSR: OK');
 
    // 10. Extensions per certificato
    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. Numero seriale e validità
    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 anni
    );
 
    // 12. Emettere certificato
    WriteLn('Emissione certificato...');
    intCert := wvds_sec_crypto_x509_cert_create_from_csr(
      csr,
      rootCert,
      rootKeyPair,
      @serial[0], 20,
      validity,
      certExt
    );
    if intCert = nil then
      raise Exception.Create('Creazione certificato fallita');
 
    // 13. Salvare certificato
    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('Certificato salvato: intermediate-ca.crt.pem');
    WriteLn;
 
    // ═══════════════════════════════════════════════════════════
    // FASE 3: Creare CA-Chain
    // ═══════════════════════════════════════════════════════════
    WriteLn('=== FASE 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 salvata: ca-chain.pem');
 
    WriteLn;
    WriteLn('=== Intermediate-CA creata con successo! ===');
 
  finally
    // Cleanup
    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.

Extensions per Intermediate-CA

Extension OID Valore Critical Descrizione
Basic Constraints 2.5.29.19 CA=true, pathLen=0 Si Nessuna ulteriore Sub-CA consentita
Key Usage 2.5.29.15 keyCertSign, cRLSign Si Solo firma certificati/CRL
Subject Key Identifier 2.5.29.14 SHA-256(publicKey) No ID univoco chiave
Authority Key Identifier 2.5.29.35 Root-CA SKI No Riferimento a chiave Issuer
CRL Distribution Points 2.5.29.31 URL(s) No Dove la CRL è disponibile
Authority Info Access 1.3.6.1.5.5.7.1.1 OCSP + caIssuers No URL OCSP e URL certificato CA
Certificate Policies 2.5.29.32 Policy OID(s) No Opzionale: Policy di emissione
Extended Key Usage 2.5.29.37 Opzionale No Opzionale: Limitazione a scopo d'uso

Differenze rispetto a Root-CA

Aspetto Root-CA Intermediate-CA
Issuer = Subject (autofirmato) = Root-CA Subject
Firmato da Propria chiave Chiave Root-CA
pathLength 1 o 2 0 (solo End-Entity)
Validità 15-25 anni 8-12 anni
AKI Non presente Root-CA SKI
CDP Opzionale Obbligatorio
AIA Non presente OCSP + caIssuers
Ubicazione chiave Offline / HSM Online (protetta)
Utilizzo Solo firmare Intermediate-CA Firmare certificati End-Entity

Avvisi di sicurezza

Protezione chiave Intermediate-CA:

  • Salvare chiave privata crittografata (Argon2id + AES-256-GCM)
  • Idealmente in HSM o hardware token
  • Limitare l'accesso (solo servizio CA)
  • Audit-Logging per tutte le operazioni di firma
  • Pianificare rotazione chiavi regolare

Best Practices:

  • pathLength=0 impedisce ulteriori Sub-CA sotto questa Intermediate-CA
  • Intermediate-CA separate per scopi diversi (Server, Client, Code-Signing)
  • CDP e AIA impostare sempre per verifica revoca
  • Trasferire CSR offline (chiavetta USB) al sistema Root-CA Air-Gapped
  • Catena certificati (ca-chain.pem) fornire per deployment semplice

Evitare errori comuni:

  • Non creare Intermediate-CA con la stessa chiave di Root
  • Non impostare pathLength > 0 se non si desidera Sub-Sub-CA
  • Non dimenticare AKI (complica Chain-Building)
  • Non dimenticare CDP/AIA (verifica revoca fallisce)

File di output

File Tipo Descrizione
intermediate-ca.csr.pem PKCS#10 Certificate Signing Request
intermediate-ca.key.pem PKCS#8 (encrypted) Chiave privata crittografata
intermediate-ca.crt.pem X.509 Certificato Intermediate-CA
ca-chain.pem PEM Bundle Intermediate + Root (per deployment)

Valori di ritorno

Codice Costante Significato
0 WVDS_OK Successo
1 WVDS_ERROR_INVALID_PARAMETER Parametro non valido
105 WVDS_ERROR_INVALID_CSR Formato CSR non valido
202 WVDS_ERROR_SIGNATURE_INVALID Firma CSR non valida
208 WVDS_ERROR_WRONG_PASSWORD Password chiave Root errata

Scenari correlati

Relazione Scenario Descrizione
Prerequisito 1.1 Creare Root-CA Root-CA deve esistere
Passo successivo 1.4 Trust Store Distribuire certificato Root
Passo successivo 1.6 CRL/OCSP Configurare revoca
Poi 3.1 Certificato Server Emettere certificati End-Entity
Alternativa 1.3 Gerarchia CA Più Intermediate-CA

Riferimenti

Zuletzt geändert: il 30/01/2026 alle 06:49