====== Scenario 1.2: Create Intermediate CA ====== **Category:** [[.:start|PKI Infrastructure]] \\ **Complexity:** ⭐⭐⭐ (Medium) \\ **Prerequisites:** [[.:root_ca_erstellen|Scenario 1.1: Create Root CA]] \\ **Estimated Time:** 20-40 Minutes ---- ===== Description ===== This scenario describes the creation of an **Intermediate CA** (also called Sub-CA or Issuing CA), which is signed by the Root CA. The Intermediate CA issues end-entity certificates and protects the Root CA key, which can remain offline. **What is created:** * ML-DSA-65 key pair for Intermediate CA * Certificate Signing Request (CSR) * Intermediate certificate signed by Root CA * Encrypted private key **Advantages of an Intermediate CA:** * **Security:** Root CA stays offline, only Intermediate CA online * **Flexibility:** Multiple Intermediate CAs for different purposes (Server, Client, Code Signing) * **Revocation:** Intermediate CA can be revoked without compromising Root * **Compliance:** Follows best practices (CA/Browser Forum, BSI) **Typical Intermediate CA Types:** * **Server CA:** For TLS server certificates * **Client CA:** For mTLS client certificates * **User CA:** For email/S-MIME certificates * **CodeSign CA:** For code signing certificates * **Device CA:** For IoT/device certificates ---- ===== Flow Diagram ===== ┌─────────────────────────────────────────────────────────────────────────┐ │ INTERMEDIATE CA CREATION │ │ │ │ ┌─────────────────────────┐ ┌─────────────────────────┐ │ │ │ INTERMEDIATE CA │ │ ROOT CA │ │ │ │ (Online System) │ │ (Air-Gapped System) │ │ │ └─────────────────────────┘ └─────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────┘ ┌──────────────┐ │ 1. Init │ └──────┬───────┘ │ ▼ ┌──────────────┐ │ 2. KeyPair │ ──────► ML-DSA-65 for Intermediate │ generate │ └──────┬───────┘ │ ▼ ┌──────────────┐ │ 3. DN │ ──────► CN=WvdS Server CA │ create │ O=DATECpro GmbH └──────┬───────┘ │ ▼ ┌──────────────┐ │ 4. Extensions│ ──────► BasicConstraints, KeyUsage, EKU │ for CSR │ (requested extensions) └──────┬───────┘ │ ▼ ┌──────────────┐ │ 5. CSR │ ──────► PKCS#10 Request │ create │ Signed with Intermediate key └──────┬───────┘ │ │ ══════════════════════════════════════════ │ CSR Transfer (USB stick, secure channel) ▼ ══════════════════════════════════════════ ┌──────────────┐ │ 6. Root CA │ ──────► Load root-ca.key.pem │ load │ (Air-Gapped System) └──────┬───────┘ │ ▼ ┌──────────────┐ │ 7. CSR │ ──────► Verify: DN, signature, extensions │ validate │ └──────┬───────┘ │ ▼ ┌──────────────┐ │ 8. Extensions│ ──────► BC, KU, SKI, AKI, CDP, AIA │ set │ (CA determines final extensions) └──────┬───────┘ │ ▼ ┌──────────────┐ │ 9. Certificate│ ──────► Root CA signs │ issue │ Intermediate certificate └──────┬───────┘ │ │ ══════════════════════════════════════════ │ Certificate transfer back ▼ ══════════════════════════════════════════ ┌──────────────┐ │ 10. Save │ ──────► intermediate-ca.crt.pem │ │ intermediate-ca.key.pem └──────┬───────┘ │ ▼ ┌──────────────┐ │ 11. Chain │ ──────► Create ca-chain.pem │ create │ (Intermediate + Root) └──────┬───────┘ │ ▼ ┌──────────────┐ │ 12. Cleanup │ └──────────────┘ ---- ===== Involved Functions ===== ==== Intermediate CA Side (CSR Creation) ==== ^ Step ^ FFI Function ^ Rust Crate ^ Description ^ | 1 | ''wvds_sec_crypto_x509_init()'' | ''std::sync'' | Initialize library | | 2 | ''wvds_sec_crypto_x509_keypair_generate_mldsa(65)'' | ''ml-dsa'' | ML-DSA-65 key pair | | 3a | ''wvds_sec_crypto_x509_dn_create()'' | ''x509-cert'' | Create DN handle | | 3b | ''wvds_sec_crypto_x509_dn_add_component()'' | ''x509-cert'' | Add CN, O, OU, C | | 4 | ''wvds_sec_crypto_x509_ext_set_*()'' | ''x509-cert'' | Requested extensions | | 5a | ''wvds_sec_crypto_x509_csr_create()'' | ''x509-cert'' | Create CSR | | 5b | ''wvds_sec_crypto_x509_csr_sign()'' | ''ml-dsa'' | Sign CSR | | 5c | ''wvds_sec_crypto_x509_csr_to_pem()'' | ''pem-rfc7468'' | Export CSR as PEM | ==== Root CA Side (Certificate Issuance) ==== ^ Step ^ FFI Function ^ Rust Crate ^ Description ^ | 6a | ''wvds_sec_crypto_x509_cert_load_pem()'' | ''x509-parser'' | Load root certificate | | 6b | ''wvds_sec_crypto_x509_keypair_load_pem_encrypted()'' | ''pkcs8'', ''argon2'' | Load root key | | 7a | ''wvds_sec_crypto_x509_csr_load_pem()'' | ''x509-cert'' | Load CSR | | 7b | ''wvds_sec_crypto_x509_csr_verify()'' | ''ml-dsa'' | Verify CSR signature | | 7c | ''wvds_sec_crypto_x509_csr_get_subject()'' | ''x509-cert'' | Extract 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 from CSR public key | | 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'' | Create certificate | | 10 | ''wvds_sec_crypto_x509_cert_to_pem()'' | ''pem-rfc7468'' | Export certificate | | 11 | ''wvds_sec_crypto_x509_chain_create()'' | – | Create chain handle | ---- ===== Code Examples ===== === C# (.NET Wrapper) === using System; using System.IO; using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ; namespace IntermediateCaExample { class Program { // Passwords (in production: enter securely!) const string ROOT_KEY_PASSWORD = "MyStr0ng!RootCA#Password2024"; const string INTERMEDIATE_KEY_PASSWORD = "MyStr0ng!IntermediateCA#2024"; static void Main(string[] args) { // === PHASE 1: Create CSR (Intermediate CA System) === Console.WriteLine("=== PHASE 1: CSR Creation ===\n"); using var context = PqCryptoContext.Initialize(); // 1. Generate key pair for Intermediate CA Console.WriteLine("Generating ML-DSA-65 key pair for 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. Requested extensions for CSR var csrExtensions = new X509ExtensionsBuilder() .AddBasicConstraints(isCa: true, pathLengthConstraint: 0, critical: true) .AddKeyUsage(KeyUsageFlags.KeyCertSign | KeyUsageFlags.CrlSign, critical: true) .Build(); // 4. Create and sign CSR Console.WriteLine("Creating Certificate Signing Request..."); using var csr = context.CreateCertificateRequest( subject: intermediateDn, keyPair: intermediateKeyPair, extensions: csrExtensions ); // 5. Save CSR as PEM string csrPem = csr.ExportToPem(); File.WriteAllText("intermediate-ca.csr.pem", csrPem); Console.WriteLine("CSR saved: intermediate-ca.csr.pem"); // 6. Save intermediate private key encrypted File.WriteAllText("intermediate-ca.key.pem", intermediateKeyPair.ExportToEncryptedPem(INTERMEDIATE_KEY_PASSWORD)); Console.WriteLine("Private key saved: intermediate-ca.key.pem\n"); // === PHASE 2: Issue certificate (Root CA System) === Console.WriteLine("=== PHASE 2: Certificate Issuance by Root CA ===\n"); // 7. Load Root CA materials Console.WriteLine("Loading 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 loaded: {rootCert.Subject}"); // 8. Load and validate CSR Console.WriteLine("Loading and validating CSR..."); using var loadedCsr = context.LoadCertificateRequestFromPem(csrPem); if (!loadedCsr.VerifySignature()) throw new CryptographicException("CSR signature invalid!"); Console.WriteLine("CSR signature: OK"); // 9. Extensions for intermediate certificate (CA determines) var certExtensions = new X509ExtensionsBuilder() // BasicConstraints: CA, but no further Sub-CA allowed .AddBasicConstraints(isCa: true, pathLengthConstraint: 0, critical: true) // KeyUsage: Certificate and CRL signing .AddKeyUsage(KeyUsageFlags.KeyCertSign | KeyUsageFlags.CrlSign, critical: true) // Subject Key Identifier (from CSR public key) .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. Validity: 10 years (shorter than Root CA) var validity = new CertificateValidity( notBefore: DateTime.UtcNow, notAfter: DateTime.UtcNow.AddYears(10) ); // 11. Issue Intermediate CA certificate Console.WriteLine("Issuing Intermediate CA certificate..."); using var intermediateCert = context.IssueCertificateFromRequest( request: loadedCsr, issuerCertificate: rootCert, issuerKeyPair: rootKeyPair, validity: validity, extensions: certExtensions ); // 12. Output certificate information Console.WriteLine("\n=== INTERMEDIATE CA CERTIFICATE ==="); 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. Save certificate File.WriteAllText("intermediate-ca.crt.pem", intermediateCert.ExportToPem()); Console.WriteLine("\nCertificate saved: intermediate-ca.crt.pem"); // === PHASE 3: Create chain === Console.WriteLine("\n=== PHASE 3: Certificate Chain ===\n"); // 14. Create 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 saved: ca-chain.pem"); // 15. Validation Console.WriteLine("\n=== VALIDATION ==="); // Verify signature (Intermediate signed by Root?) bool signatureValid = intermediateCert.VerifySignature(rootCert); Console.WriteLine($"Signature by Root CA: {(signatureValid ? "OK" : "ERROR")}"); // Chain validation using var trustStore = context.CreateTrustStore(); trustStore.AddCertificate(rootCert); var validationResult = context.ValidateChain(chain, trustStore); Console.WriteLine($"Chain validation: {(validationResult.IsValid ? "OK" : "ERROR")}"); Console.WriteLine("\n✓ Intermediate CA successfully created!"); } } } ---- ===== Extensions for Intermediate CA ===== ^ Extension ^ OID ^ Value ^ Critical ^ Description ^ | Basic Constraints | 2.5.29.19 | CA=true, pathLen=0 | ✅ Yes | No further Sub-CA allowed | | Key Usage | 2.5.29.15 | keyCertSign, cRLSign | ✅ Yes | Only certificate/CRL signing | | Subject Key Identifier | 2.5.29.14 | SHA-256(publicKey) | ❌ No | Unique key ID | | Authority Key Identifier | 2.5.29.35 | Root CA SKI | ❌ No | Reference to issuer key | | CRL Distribution Points | 2.5.29.31 | URL(s) | ❌ No | Where CRL is available | | Authority Info Access | 1.3.6.1.5.5.7.1.1 | OCSP + caIssuers | ❌ No | OCSP URL and CA certificate URL | | Certificate Policies | 2.5.29.32 | Policy OID(s) | ❌ No | Optional: Issuance policies | | Extended Key Usage | 2.5.29.37 | Optional | ❌ No | Optional: Restrict usage purpose | ---- ===== Differences from Root CA ===== ^ Aspect ^ Root CA ^ Intermediate CA ^ | **Issuer** | = Subject (self-signed) | = Root CA Subject | | **Signed by** | Own key | Root CA key | | **pathLength** | 1 or 2 | 0 (only end-entity) | | **Validity** | 15-25 years | 8-12 years | | **AKI** | Not present | Root CA SKI | | **CDP** | Optional | Required | | **AIA** | Not present | OCSP + caIssuers | | **Key storage** | Offline / HSM | Online (protected) | | **Usage** | Only sign Intermediate CAs | Sign end-entity certificates | ---- ===== Security Notes ===== **Intermediate CA Key Protection:** * Store private key **encrypted** (Argon2id + AES-256-GCM) * Ideally in **HSM** or hardware token * **Restrict** access (only CA service) * **Audit logging** for all signing operations * Plan regular **key rotation** **Best Practices:** * **pathLength=0** prevents further Sub-CAs under this Intermediate CA * **Separate Intermediate CAs** for different purposes (Server, Client, Code Signing) * **Always set CDP and AIA** for revocation checking * **Transfer CSR offline** (USB stick) to air-gapped Root CA system * **Provide certificate chain** (ca-chain.pem) for easy deployment **Avoid Common Mistakes:** * **Do not** create Intermediate CA with same key as Root * **Do not** set pathLength > 0 if no Sub-Sub-CA is desired * **Do not** forget AKI (makes chain building difficult) * **Do not** forget CDP/AIA (revocation checking fails) ---- ===== Output Files ===== ^ File ^ Type ^ Description ^ | ''intermediate-ca.csr.pem'' | PKCS#10 | Certificate Signing Request | | ''intermediate-ca.key.pem'' | PKCS#8 (encrypted) | Encrypted private key | | ''intermediate-ca.crt.pem'' | X.509 | Intermediate CA certificate | | ''ca-chain.pem'' | PEM Bundle | Intermediate + Root (for deployment) | ---- ===== Return Values ===== ^ Code ^ Constant ^ Meaning ^ | 0 | ''WVDS_OK'' | Success | | 1 | ''WVDS_ERROR_INVALID_PARAMETER'' | Invalid parameter | | 105 | ''WVDS_ERROR_INVALID_CSR'' | CSR format invalid | | 202 | ''WVDS_ERROR_SIGNATURE_INVALID'' | CSR signature invalid | | 208 | ''WVDS_ERROR_WRONG_PASSWORD'' | Wrong Root key password | ---- ===== Related Scenarios ===== ^ Relationship ^ Scenario ^ Description ^ | **Prerequisite** | [[.:root_ca_erstellen|1.1 Create Root CA]] | Root CA must exist | | **Next Step** | [[.:trust_store_konfigurieren|1.4 Trust Store]] | Distribute root certificate | | **Next Step** | [[.:crl_ocsp_infrastruktur|1.6 CRL/OCSP]] | Set up revocation | | **Then** | [[en:int:pqcrypt:szenarien:zertifikate:server_zertifikat|3.1 Server Certificate]] | Issue end-entity certificates | | **Alternative** | [[.:ca_hierarchie_aufbauen|1.3 CA Hierarchy]] | Multiple Intermediate CAs | ---- ===== References ===== * [[en:int:pqcrypt:api:module:csr|Module: csr]] * [[en:int:pqcrypt:api:module:cert|Module: cert]] * [[en:int:pqcrypt:api:module:ext|Module: ext]] * [[en:int:pqcrypt:api:module:chain|Module: chain]] * [[en:int:pqcrypt:konzepte:pki_grundlagen|Concept: PKI Fundamentals]] * [[en:int:pqcrypt:konzepte:x509_erweiterungen|Concept: X.509 Extensions]] * [[https://www.rfc-editor.org/rfc/rfc5280|RFC 5280: X.509 PKI]] * [[https://cabforum.org/baseline-requirements/|CA/Browser Forum: Baseline Requirements]] ---- << [[.:root_ca_erstellen|← 1.1 Root CA]] | [[.:start|▲ PKI Infrastructure]] | [[.:ca_hierarchie_aufbauen|1.3 CA Hierarchy →]] >> {{tag>scenario pki intermediate-ca csr ml-dsa-65 chain aki cdp aia}} ---- //Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional// ~~NOTOC~~