Category: PKI Infrastructure
Complexity: ⭐⭐⭐ (Medium)
Prerequisites: Scenario 1.1: Create Root CA
Estimated Time: 20-40 Minutes
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:
Advantages of an Intermediate CA:
Typical Intermediate CA Types:
┌─────────────────────────────────────────────────────────────────────────┐
│ 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 │
└──────────────┘
| 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 |
| 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 |
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!"); } } }
| 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 |
| 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 |
Intermediate CA Key Protection:
Best Practices:
Avoid Common Mistakes:
| 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) |
| 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 |
| Relationship | Scenario | Description |
|---|---|---|
| Prerequisite | 1.1 Create Root CA | Root CA must exist |
| Next Step | 1.4 Trust Store | Distribute root certificate |
| Next Step | 1.6 CRL/OCSP | Set up revocation |
| Then | 3.1 Server Certificate | Issue end-entity certificates |
| Alternative | 1.3 CA Hierarchy | Multiple Intermediate CAs |
« ← 1.1 Root CA | ▲ PKI Infrastructure | 1.3 CA Hierarchy → »
Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional