====== 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~~