Scenario 1.2: Create Intermediate CA

Category: PKI Infrastructure
Complexity: ⭐⭐⭐ (Medium)
Prerequisites: 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:

Advantages of an Intermediate CA:

Typical Intermediate CA Types:


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

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

References


« ← 1.1 Root CA | ▲ PKI Infrastructure | 1.3 CA Hierarchy → »


Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional