Scenario 1.1: Create Root CA with PQ Keys

Category: PKI Infrastructure
Complexity: ⭐⭐⭐⭐ (High)
Prerequisites: None (Entry Point)
Estimated Time: 15-30 Minutes


Description

This scenario describes the creation of a self-signed Root CA with Post-Quantum keys. The Root CA forms the Trust Anchor for the entire PKI hierarchy and is the most security-critical element of the infrastructure.

What is created:

Use cases:


Flow Diagram

┌─────────────────────────────────────────────────────────────────┐
│                    ROOT CA CREATION                              │
└─────────────────────────────────────────────────────────────────┘

     ┌──────────────┐
     │ 1. Init      │
     │ Library      │
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 2. KeyPair   │ ──────► ML-DSA-65 (FIPS 204)
     │ generate     │         ~4KB Public Key
     └──────┬───────┘         ~2KB Private Key
            │
            ▼
     ┌──────────────┐
     │ 3. DN        │ ──────► CN=WvdS Root CA
     │ create       │         O=DATECpro GmbH
     └──────┬───────┘         C=DE
            │
            ▼
     ┌──────────────┐
     │ 4. Serial    │ ──────► 20 Bytes random (160 bit)
     │ generate     │
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 5. Validity  │ ──────► notBefore: now
     │ set          │         notAfter: +20 years
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 6. Extensions│ ──────► BasicConstraints: CA=true, pathLen=1
     │ set          │         KeyUsage: keyCertSign, cRLSign
     └──────┬───────┘         SKI: SHA-256(publicKey)
            │
            ▼
     ┌──────────────┐
     │ 7. Certificate│ ──────► Subject = Issuer (self-signed)
     │ create       │         Signature with ML-DSA-65
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 8. Export    │ ──────► root-ca.crt.pem (certificate)
     │ PEM          │         root-ca.key.pem (encrypted)
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ 9. Cleanup   │ ──────► Release handles
     │              │         Zeroize secrets
     └──────────────┘

Involved Functions

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
2a wvds_sec_crypto_x509_keypair_self_test() ml-dsa Self-test (sign/verify)
3a wvds_sec_crypto_x509_dn_create() x509-cert Create DN handle
3b wvds_sec_crypto_x509_dn_add_component() x509-cert, der Add CN, O, C
4 wvds_sec_crypto_x509_serial_generate() rand 160-bit serial number
5 wvds_sec_crypto_x509_validity_create() x509-cert Validity period
6a wvds_sec_crypto_x509_ext_set_basic_constraints() x509-cert, der CA=true, pathLen
6b wvds_sec_crypto_x509_ext_set_key_usage() x509-cert, der keyCertSign, cRLSign
6c wvds_sec_crypto_x509_ext_set_ski_from_keypair() sha2 Subject Key Identifier
7 wvds_sec_crypto_x509_cert_create_root() x509-cert, ml-dsa Create root certificate
8a wvds_sec_crypto_x509_cert_to_pem() pem-rfc7468 Certificate as PEM
8b wvds_sec_crypto_x509_keypair_to_pem_encrypted() argon2, aes-gcm Key encrypted
9a wvds_sec_crypto_x509_free_*() std::alloc Release handles
9b wvds_sec_crypto_x509_zeroize_keypair() zeroize Delete secrets

Function Tree

wvds_sec_crypto_x509_init()
│
├── wvds_sec_crypto_x509_keypair_generate_mldsa(level: u8)  → *mut KeyPairHandle
│   │
│   ├── // Parameter selection
│   │   ├── [ML_DSA_44] → ml_dsa::ml_dsa_44::KeyPair::generate(&mut OsRng)
│   │   ├── [ML_DSA_65] → ml_dsa::ml_dsa_65::KeyPair::generate(&mut OsRng)  ✓
│   │   └── [ML_DSA_87] → ml_dsa::ml_dsa_87::KeyPair::generate(&mut OsRng)
│   │
│   ├── // Self-test (FIPS requirement)
│   │   ├── test_msg = [0x00..0x20]  // 32 bytes test data
│   │   ├── signature = signing_key.sign(&test_msg)
│   │   └── assert!(verifying_key.verify(&test_msg, &signature).is_ok())
│   │
│   └── // Create handle
│       └── Box::into_raw(Box::new(KeyPairHandle {
│               algorithm: ML_DSA_65,
│               signing_key,
│               verifying_key
│           }))
│
├── wvds_sec_crypto_x509_dn_create()  → *mut DnHandle
│   └── x509_cert::name::Name::default()
│
├── wvds_sec_crypto_x509_dn_add_component(dn, oid, value)  → i32
│   │
│   ├── [OID_CN = "2.5.4.3"]
│   │   └── dn.0.push(RelativeDistinguishedName::from(
│   │           AttributeTypeAndValue { oid: OID_CN, value: "WvdS Root CA" }
│   │       ))
│   │
│   ├── [OID_O = "2.5.4.10"]
│   │   └── dn.0.push(..., value: "DATECpro GmbH")
│   │
│   └── [OID_C = "2.5.4.6"]
│       └── dn.0.push(..., value: "DE")
│
├── wvds_sec_crypto_x509_serial_generate(out: *mut u8, len: usize)  → i32
│   │
│   ├── // RFC 5280: max 20 bytes, positive
│   │   └── rand::rngs::OsRng.fill_bytes(&mut serial[0..len])
│   │
│   └── // Set highest bit to 0 (positive)
│       └── serial[0] &= 0x7F
│
├── wvds_sec_crypto_x509_validity_create(not_before, not_after)  → *mut ValidityHandle
│   │
│   ├── not_before = x509_cert::time::Time::UtcTime(now)
│   │
│   └── not_after = x509_cert::time::Time::GeneralizedTime(now + 20 years)
│       │
│       └── // GeneralizedTime for dates >= 2050
│
├── wvds_sec_crypto_x509_ext_set_basic_constraints(ext, ca: bool, path_len: i32)  → i32
│   │
│   ├── bc = x509_cert::ext::pkix::BasicConstraints {
│   │       ca: true,
│   │       path_len_constraint: Some(1)  // only 1 intermediate level
│   │   }
│   │
│   └── ext.add(Extension {
│           extn_id: OID_BASIC_CONSTRAINTS,
│           critical: true,  // MUST be critical for CA
│           extn_value: der::Encode::to_der(&bc)
│       })
│
├── wvds_sec_crypto_x509_ext_set_key_usage(ext, flags: u16)  → i32
│   │
│   ├── // For Root CA: keyCertSign (5) + cRLSign (6)
│   │   └── flags = 0x0006  // Bit 5 + Bit 6
│   │
│   ├── ku = x509_cert::ext::pkix::KeyUsage(flags)
│   │
│   └── ext.add(Extension {
│           extn_id: OID_KEY_USAGE,
│           critical: true,  // SHOULD be critical
│           extn_value: der::Encode::to_der(&ku)
│       })
│
├── wvds_sec_crypto_x509_ext_set_ski_from_keypair(ext, keypair)  → i32
│   │
│   ├── // Subject Key Identifier = SHA-256(SubjectPublicKeyInfo.subjectPublicKey)
│   │   ├── public_key_bytes = keypair.verifying_key.to_bytes()
│   │   └── ski = sha2::Sha256::digest(&public_key_bytes)[0..20]
│   │
│   └── ext.add(Extension {
│           extn_id: OID_SUBJECT_KEY_ID,
│           critical: false,
│           extn_value: der::Encode::to_der(&OctetString::new(ski))
│       })
│
├── wvds_sec_crypto_x509_cert_create_root(
│       keypair: *const KeyPairHandle,
│       subject: *const DnHandle,
│       serial: *const u8,
│       serial_len: usize,
│       validity: *const ValidityHandle,
│       extensions: *const ExtHandle
│   )  → *mut CertHandle
│   │
│   ├── // Build TBSCertificate
│   │   └── tbs = x509_cert::TbsCertificate {
│   │           version: Version::V3,
│   │           serial_number: SerialNumber::new(&serial[..serial_len]),
│   │           signature: AlgorithmIdentifier { oid: OID_ML_DSA_65, parameters: None },
│   │           issuer: subject.clone(),      // self-signed: issuer = subject
│   │           validity: validity.clone(),
│   │           subject: subject.clone(),
│   │           subject_public_key_info: keypair.to_spki(),
│   │           extensions: Some(extensions.clone())
│   │       }
│   │
│   ├── // DER-encode TBS certificate
│   │   └── tbs_der = der::Encode::to_der(&tbs)
│   │
│   ├── // Sign with ML-DSA-65
│   │   └── signature = ml_dsa::ml_dsa_65::SigningKey::sign(&tbs_der)
│   │       │
│   │       └── // Deterministic signature (FIPS 204)
│   │           // Signature length: 3293 bytes
│   │
│   └── // Assemble certificate
│       └── cert = x509_cert::Certificate {
│               tbs_certificate: tbs,
│               signature_algorithm: AlgorithmIdentifier { oid: OID_ML_DSA_65 },
│               signature: BitString::from_bytes(&signature)
│           }
│
├── wvds_sec_crypto_x509_cert_to_pem(cert, out, out_len)  → i32
│   │
│   ├── cert_der = der::Encode::to_der(&cert)
│   │
│   └── pem_rfc7468::encode_string("CERTIFICATE", &cert_der)
│       │
│       └── // Example output:
│           // -----BEGIN CERTIFICATE-----
│           // MIIxxxxxx...
│           // -----END CERTIFICATE-----
│
├── wvds_sec_crypto_x509_keypair_to_pem_encrypted(
│       keypair, password, kdf_algorithm, out, out_len
│   )  → i32
│   │
│   ├── // Private key as PKCS#8 DER
│   │   └── private_key_der = keypair.signing_key.to_pkcs8_der()
│   │
│   ├── // Generate salt and nonce
│   │   ├── salt = rand::OsRng.gen::<[u8; 16]>()
│   │   └── nonce = rand::OsRng.gen::<[u8; 12]>()
│   │
│   ├── // Derive Key Encryption Key
│   │   └── [ARGON2ID]
│   │       └── argon2::Argon2::new(
│   │               Algorithm::Argon2id,
│   │               Version::V0x13,
│   │               Params::new(65536, 3, 4, Some(32))  // 64MB, 3 iter, 4 lanes
│   │           ).hash_password_into(password.as_bytes(), &salt, &mut kek)
│   │
│   ├── // Encrypt private key
│   │   └── encrypted = aes_gcm::Aes256Gcm::new(&kek)
│   │           .encrypt(&nonce, private_key_der.as_ref())
│   │
│   ├── // Create EncryptedPrivateKeyInfo (PKCS#8)
│   │   └── epki = pkcs8::EncryptedPrivateKeyInfo {
│   │           encryption_algorithm: ...,
│   │           encrypted_data: encrypted
│   │       }
│   │
│   ├── // Encode as PEM
│   │   └── pem_rfc7468::encode_string("ENCRYPTED PRIVATE KEY", &epki_der)
│   │
│   └── // Zeroize KEK
│       └── zeroize::Zeroize::zeroize(&mut kek)
│
└── // Cleanup
    │
    ├── wvds_sec_crypto_x509_free_cert(cert)
    ├── wvds_sec_crypto_x509_free_keypair(keypair)
    │   └── // Automatic zeroize via Drop
    ├── wvds_sec_crypto_x509_free_dn(dn)
    ├── wvds_sec_crypto_x509_free_validity(validity)
    ├── wvds_sec_crypto_x509_free_ext(ext)
    └── wvds_sec_crypto_x509_shutdown()

Code Examples

C# (.NET Wrapper)

using System;
using System.IO;
using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
 
namespace RootCaExample
{
    class Program
    {
        static void Main(string[] args)
        {
            // Password for private key (in production: enter securely!)
            string keyPassword = "MyStr0ng!RootCA#Password2024";
 
            // 1. Initialize context
            using var context = PqCryptoContext.Initialize();
 
            // 2. Generate ML-DSA-65 key pair
            Console.WriteLine("Generating ML-DSA-65 key pair...");
            using var keyPair = context.GenerateKeyPair(PqAlgorithm.MlDsa65);
 
            // Perform self-test
            if (!keyPair.SelfTest())
                throw new CryptographicException("KeyPair self-test failed!");
 
            Console.WriteLine($"  Public Key:  {keyPair.PublicKeySize} Bytes");
            Console.WriteLine($"  Private Key: {keyPair.PrivateKeySize} Bytes");
 
            // 3. Create Distinguished Name
            var subjectDn = new DistinguishedNameBuilder()
                .AddCommonName("WvdS Root CA")
                .AddOrganization("DATECpro GmbH")
                .AddOrganizationalUnit("PQ-Security")
                .AddCountry("DE")
                .AddLocality("Munich")
                .Build();
 
            Console.WriteLine($"Subject DN: {subjectDn}");
 
            // 4. Validity period (20 years for Root CA)
            var validity = new CertificateValidity(
                notBefore: DateTime.UtcNow,
                notAfter: DateTime.UtcNow.AddYears(20)
            );
 
            // 5. Extensions for Root CA
            var extensions = new X509ExtensionsBuilder()
                // BasicConstraints: CA=true, max 1 intermediate level
                .AddBasicConstraints(isCa: true, pathLengthConstraint: 1, critical: true)
                // KeyUsage: only certificate and CRL signing
                .AddKeyUsage(
                    KeyUsageFlags.KeyCertSign | KeyUsageFlags.CrlSign,
                    critical: true
                )
                // Subject Key Identifier for later AKI reference
                .AddSubjectKeyIdentifier(keyPair)
                .Build();
 
            // 6. Create root certificate (self-signed)
            Console.WriteLine("Creating self-signed root certificate...");
            using var rootCert = context.CreateRootCertificate(
                keyPair: keyPair,
                subject: subjectDn,
                validity: validity,
                extensions: extensions
            );
 
            // 7. Output certificate information
            Console.WriteLine("\n=== ROOT CA CERTIFICATE ===");
            Console.WriteLine($"Subject:        {rootCert.Subject}");
            Console.WriteLine($"Issuer:         {rootCert.Issuer}");
            Console.WriteLine($"Serial:         {rootCert.SerialNumber}");
            Console.WriteLine($"Not Before:     {rootCert.NotBefore:yyyy-MM-dd HH:mm:ss} UTC");
            Console.WriteLine($"Not After:      {rootCert.NotAfter:yyyy-MM-dd HH:mm:ss} UTC");
            Console.WriteLine($"Algorithm:      {rootCert.SignatureAlgorithm}");
            Console.WriteLine($"Thumbprint:     {rootCert.Thumbprint}");
            Console.WriteLine($"Is CA:          {rootCert.IsCertificateAuthority}");
            Console.WriteLine($"Path Length:    {rootCert.PathLengthConstraint}");
 
            // 8. Save as PEM files
            string certPath = "root-ca.crt.pem";
            string keyPath = "root-ca.key.pem";
 
            // Certificate (public)
            File.WriteAllText(certPath, rootCert.ExportToPem());
            Console.WriteLine($"\nCertificate saved: {certPath}");
 
            // Private key (encrypted with Argon2id)
            File.WriteAllText(keyPath, keyPair.ExportToEncryptedPem(
                password: keyPassword,
                kdfAlgorithm: KeyDerivationAlgorithm.Argon2id
            ));
            Console.WriteLine($"Private key saved: {keyPath} (encrypted)");
 
            // 9. Validation: reload certificate and verify
            Console.WriteLine("\n=== VALIDATION ===");
            using var loadedCert = context.LoadCertificateFromPem(File.ReadAllText(certPath));
            using var loadedKey = context.LoadKeyPairFromEncryptedPem(
                File.ReadAllText(keyPath),
                keyPassword
            );
 
            // Verify self-signature
            bool signatureValid = loadedCert.VerifySignature(loadedCert);  // self-signed
            Console.WriteLine($"Self-signature valid: {signatureValid}");
 
            // Verify key pair match
            bool keyMatch = loadedCert.PublicKeyMatches(loadedKey);
            Console.WriteLine($"Public key match:     {keyMatch}");
 
            Console.WriteLine("\n✓ Root CA successfully created!");
        }
    }
}

Delphi (FFI)

program CreateRootCA;
 
{$APPTYPE CONSOLE}
 
uses
  SysUtils, DateUtils,
  WvdS.PqCrypto.FFI;  // FFI unit with declarations
 
const
  KEY_PASSWORD = 'MyStr0ng!RootCA#Password2024';
 
var
  res: Integer;
  keypair: Pointer;
  dn: Pointer;
  validity: Pointer;
  ext: Pointer;
  cert: Pointer;
  serial: array[0..19] of Byte;
  pem_buf: array[0..65535] of AnsiChar;
  pem_len: NativeUInt;
 
begin
  WriteLn('=== WvdS Root CA Creation ===');
  WriteLn;
 
  // 1. Initialize library
  res := wvds_sec_crypto_x509_init();
  if res <> WVDS_OK then
  begin
    WriteLn('ERROR: Init failed (', res, ')');
    Exit;
  end;
 
  try
    // 2. Generate ML-DSA-65 key pair
    WriteLn('Generating ML-DSA-65 key pair...');
    keypair := wvds_sec_crypto_x509_keypair_generate_mldsa(65);
    if keypair = nil then
      raise Exception.Create('KeyPair generation failed');
 
    // Self-test
    res := wvds_sec_crypto_x509_keypair_self_test(keypair);
    if res <> WVDS_OK then
      raise Exception.Create('KeyPair self-test failed');
    WriteLn('  Self-test: OK');
 
    // 3. Create Distinguished Name
    WriteLn('Creating Distinguished Name...');
    dn := wvds_sec_crypto_x509_dn_create();
    if dn = nil then
      raise Exception.Create('DN creation failed');
 
    wvds_sec_crypto_x509_dn_add_component(dn, PAnsiChar('2.5.4.3'),  PAnsiChar('WvdS Root CA'));      // CN
    wvds_sec_crypto_x509_dn_add_component(dn, PAnsiChar('2.5.4.10'), PAnsiChar('DATECpro GmbH'));    // O
    wvds_sec_crypto_x509_dn_add_component(dn, PAnsiChar('2.5.4.11'), PAnsiChar('PQ-Security'));      // OU
    wvds_sec_crypto_x509_dn_add_component(dn, PAnsiChar('2.5.4.6'),  PAnsiChar('DE'));               // C
 
    // 4. Generate serial number (20 bytes = 160 bit)
    WriteLn('Generating serial number...');
    res := wvds_sec_crypto_x509_serial_generate(@serial[0], 20);
    if res <> WVDS_OK then
      raise Exception.Create('Serial generation failed');
 
    // 5. Validity: 20 years
    WriteLn('Setting validity period (20 years)...');
    validity := wvds_sec_crypto_x509_validity_create(
      DateTimeToUnix(Now, False),                      // not_before: now
      DateTimeToUnix(IncYear(Now, 20), False)          // not_after: +20 years
    );
    if validity = nil then
      raise Exception.Create('Validity creation failed');
 
    // 6. Create extensions
    WriteLn('Setting extensions...');
    ext := wvds_sec_crypto_x509_ext_create();
    if ext = nil then
      raise Exception.Create('Extension creation failed');
 
    // BasicConstraints: CA=true, pathLen=1
    res := wvds_sec_crypto_x509_ext_set_basic_constraints(ext, True, 1);
    if res <> WVDS_OK then
      raise Exception.Create('BasicConstraints failed');
 
    // KeyUsage: keyCertSign (Bit 5) + cRLSign (Bit 6) = 0x0006
    res := wvds_sec_crypto_x509_ext_set_key_usage(ext, $0006);
    if res <> WVDS_OK then
      raise Exception.Create('KeyUsage failed');
 
    // Subject Key Identifier
    res := wvds_sec_crypto_x509_ext_set_ski_from_keypair(ext, keypair);
    if res <> WVDS_OK then
      raise Exception.Create('SKI failed');
 
    // 7. Create root certificate (self-signed)
    WriteLn('Creating self-signed root certificate...');
    cert := wvds_sec_crypto_x509_cert_create_root(
      keypair,          // Key pair
      dn,               // Subject (= Issuer for Root)
      @serial[0], 20,   // Serial number
      validity,         // Validity
      ext               // Extensions
    );
    if cert = nil then
      raise Exception.Create('Certificate creation failed');
 
    // 8. Save as PEM
    WriteLn;
    WriteLn('Saving files...');
 
    // Certificate
    pem_len := SizeOf(pem_buf);
    res := wvds_sec_crypto_x509_cert_to_pem(cert, @pem_buf[0], @pem_len);
    if res <> WVDS_OK then
      raise Exception.Create('Certificate export failed');
 
    with TFileStream.Create('root-ca.crt.pem', fmCreate) do
    try
      Write(pem_buf[0], pem_len);
    finally
      Free;
    end;
    WriteLn('  root-ca.crt.pem saved');
 
    // Private key (encrypted)
    pem_len := SizeOf(pem_buf);
    res := wvds_sec_crypto_x509_keypair_to_pem_encrypted(
      keypair,
      PAnsiChar(KEY_PASSWORD),
      WVDS_KDF_ARGON2ID,
      @pem_buf[0],
      @pem_len
    );
    if res <> WVDS_OK then
      raise Exception.Create('Key export failed');
 
    with TFileStream.Create('root-ca.key.pem', fmCreate) do
    try
      Write(pem_buf[0], pem_len);
    finally
      Free;
    end;
    WriteLn('  root-ca.key.pem saved (encrypted)');
 
    WriteLn;
    WriteLn('=== Root CA successfully created! ===');
 
  finally
    // 9. Cleanup
    if cert <> nil then wvds_sec_crypto_x509_free_cert(cert);
    if keypair <> nil then wvds_sec_crypto_x509_free_keypair(keypair);
    if dn <> nil then wvds_sec_crypto_x509_free_dn(dn);
    if validity <> nil then wvds_sec_crypto_x509_free_validity(validity);
    if ext <> nil then wvds_sec_crypto_x509_free_ext(ext);
    wvds_sec_crypto_x509_shutdown();
  end;
end.

Parameters

wvds_sec_crypto_x509_keypair_generate_mldsa

Parameter Type Description Valid Values
level u8 ML-DSA Security Level 44, 65, 87
Level NIST Security Public Key Private Key Signature
44 Level 2 (~128-bit) 1,312 Bytes 2,560 Bytes 2,420 Bytes
65 Level 3 (~192-bit) 1,952 Bytes 4,032 Bytes 3,293 Bytes
87 Level 5 (~256-bit) 2,592 Bytes 4,896 Bytes 4,595 Bytes

wvds_sec_crypto_x509_dn_add_component

Parameter Type Description Example
dn *mut DnHandle DN handle
oid *const c_char OID as string „2.5.4.3“
value *const c_char UTF-8 value „WvdS Root CA“
OID Constant Description Example Value
2.5.4.3 OID_CN Common Name WvdS Root CA
2.5.4.10 OID_O Organization DATECpro GmbH
2.5.4.11 OID_OU Organizational Unit PQ-Security
2.5.4.6 OID_C Country (2 characters) DE
2.5.4.8 OID_ST State/Province Bavaria
2.5.4.7 OID_L Locality Munich

wvds_sec_crypto_x509_ext_set_basic_constraints

Parameter Type Description Root CA Value
ext *mut ExtHandle Extension handle
ca bool Is CA certificate true
path_len i32 Max path length (-1 = unlimited) 1 or 2

wvds_sec_crypto_x509_ext_set_key_usage

Flag Bit Value Description
digitalSignature 0 0x0080 Digital signatures
nonRepudiation 1 0x0040 Non-repudiation
keyEncipherment 2 0x0020 Key encipherment
dataEncipherment 3 0x0010 Data encipherment
keyAgreement 4 0x0008 Key agreement
keyCertSign 5 0x0004 Certificate signing
cRLSign 6 0x0002 CRL signing
encipherOnly 7 0x0001 Encipher only

For Root CA: flags = 0x0006 (keyCertSign + cRLSign)


Return Values

Code Constant Meaning
0 WVDS_OK Success
1 WVDS_ERROR_INVALID_PARAMETER Invalid parameter (e.g., level ≠ 44/65/87)
2 WVDS_ERROR_OUT_OF_MEMORY Memory allocation failed
3 WVDS_ERROR_NOT_INITIALIZED init() not called
200 WVDS_ERROR_KEY_GENERATION_FAILED Key generation failed
201 WVDS_ERROR_SIGNATURE_FAILED Signing failed
210 WVDS_ERROR_KEYPAIR_SELF_TEST_FAILED Self-test failed

Complete list: Error Codes


Output Files

root-ca.crt.pem

-----BEGIN CERTIFICATE-----
MIIHxjCCBiagAwIBAgIUP7J2kM9x... (Base64-encoded DER)
...
-----END CERTIFICATE-----
Field Value
Version 3 (0x02)
Serial 20 bytes random
Signature Algorithm ML-DSA-65 (OID: 2.16.840.1.101.3.4.3.18)
Issuer CN=WvdS Root CA, O=DATECpro GmbH, C=DE
Subject CN=WvdS Root CA, O=DATECpro GmbH, C=DE
Validity 20 years
Public Key ML-DSA-65 (~1,952 bytes)
Extensions BasicConstraints, KeyUsage, SKI
Signature ML-DSA-65 (~3,293 bytes)

root-ca.key.pem

-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIHbTBXBgkqhkiG9w0BBQ0w... (PKCS#8 EncryptedPrivateKeyInfo)
...
-----END ENCRYPTED PRIVATE KEY-----
Field Value
Format PKCS#8 EncryptedPrivateKeyInfo
KDF Argon2id (64MB, 3 iterations, 4 lanes)
Cipher AES-256-GCM
Key Size ~4,032 bytes (ML-DSA-65 private key)

Security Notes

CRITICAL – Root CA Private Key:

The Root CA private key is the most security-critical element of the entire PKI. Compromise means complete loss of trust!

  • Never generate or store on networked systems
  • Never store unencrypted
  • Never transmit via email or insecure channels

Recommended Protection Measures:

  • Air-gapped system: Root CA operations on isolated computer without network
  • HSM: Hardware Security Module for key storage (e.g., YubiHSM, Thales Luna)
  • Encryption: At least AES-256 with strong password (>=20 characters)
  • Physical security: Encrypted USB stick in safe or bank vault
  • Backup: At least 2 copies at separate locations
  • Key ceremony: Documented process with witnesses for all Root CA operations
  • Audit log: Log all access

Best Practices:

  • ML-DSA-65 offers good ratio of security and performance for Root CAs
  • pathLength=1 limits hierarchy to Root → Intermediate → End-Entity
  • 20 years validity is common for Root CAs (max 25 years recommended)
  • Set SKI enables later AKI reference in intermediate certificates
  • Self-test after generation ensures correct implementation

Common Errors

Problem Cause Solution
KEY_GENERATION_FAILED Not enough entropy Check OS random source, /dev/urandom available?
KEYPAIR_SELF_TEST_FAILED Defective implementation Check library version, recompile
Certificate invalid DN empty Set at least CN
Key not readable Wrong password Check password, encoding (UTF-8)
pathLength error Value < -1 -1 for unlimited, 0, 1, 2, … for limit
NOT_INITIALIZED init() forgotten Call wvds_sec_crypto_x509_init() first
Memory leak Handles not released Call all free_*() functions
PEM too large Buffer too small Query size first (out_len = 0)

Relationship Scenario Description
Next Step 1.2 Intermediate CA Sign subordinate CA from Root
Then 1.4 Trust Store Distribute root certificate
Then 1.6 CRL/OCSP Set up revocation services
Alternative 11.1 Generate Keys Keys only, no certificate
Related 11.2 Store Keys More storage options

References

API Documentation

Concepts

Security

Reference Tables

External Standards


« ← PKI Infrastructure | ▲ Scenarios | 1.2 Intermediate CA → »


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