====== Scenario 1.1: Create Root CA with PQ Keys ======
**Category:** [[.:start|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:**
* ML-DSA-65 key pair (Post-Quantum secure)
* Self-signed X.509 v3 Root certificate
* Encrypted private key (PKCS#8 with Argon2id)
**Use cases:**
* Building a new PQ-capable enterprise PKI
* Migrating existing PKI to Post-Quantum algorithms
* Test environment for PQ certificates
* Isolated PKI for special applications (IoT, code signing)
----
===== 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:// [[en:int:pqcrypt:referenz:error_codes_tabelle|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) |
----
===== Related Scenarios =====
^ Relationship ^ Scenario ^ Description ^
| **Next Step** | [[.:intermediate_ca_erstellen|1.2 Intermediate CA]] | Sign subordinate CA from Root |
| **Then** | [[.:trust_store_konfigurieren|1.4 Trust Store]] | Distribute root certificate |
| **Then** | [[.:crl_ocsp_infrastruktur|1.6 CRL/OCSP]] | Set up revocation services |
| **Alternative** | [[en:int:pqcrypt:szenarien:schluessel:schluesselpaar_generieren|11.1 Generate Keys]] | Keys only, no certificate |
| **Related** | [[en:int:pqcrypt:szenarien:schluessel:schluessel_speichern|11.2 Store Keys]] | More storage options |
----
===== References =====
=== API Documentation ===
* [[en:int:pqcrypt:api:module:init|Module: init]]
* [[en:int:pqcrypt:api:module:keypair|Module: keypair]]
* [[en:int:pqcrypt:api:module:dn|Module: dn]]
* [[en:int:pqcrypt:api:module:cert|Module: cert]]
* [[en:int:pqcrypt:api:module:ext|Module: ext]]
* [[en:int:pqcrypt:api:module:free|Module: free]]
=== Concepts ===
* [[en:int:pqcrypt:konzepte:fips_204_ml_dsa|ML-DSA (FIPS 204)]]
* [[en:int:pqcrypt:konzepte:pki_grundlagen|PKI Fundamentals]]
* [[en:int:pqcrypt:konzepte:x509_erweiterungen|X.509 Extensions]]
=== Security ===
* [[en:int:pqcrypt:sicherheit:schluessellaengen|Recommended Key Lengths]]
* [[en:int:pqcrypt:sicherheit:secure_memory|Secure Memory Management]]
=== Reference Tables ===
* [[en:int:pqcrypt:referenz:oid_tabelle|OID Table]]
* [[en:int:pqcrypt:referenz:error_codes_tabelle|Error Codes]]
* [[en:int:pqcrypt:referenz:schluesselgroessen|Key Sizes]]
* [[en:int:pqcrypt:referenz:signaturgroessen|Signature Sizes]]
=== External Standards ===
* [[https://csrc.nist.gov/pubs/fips/204/final|NIST FIPS 204: ML-DSA]]
* [[https://www.rfc-editor.org/rfc/rfc5280|RFC 5280: X.509 PKI]]
* [[https://www.rfc-editor.org/rfc/rfc5958|RFC 5958: PKCS#8]]
* [[https://datatracker.ietf.org/doc/draft-ietf-lamps-dilithium-certificates/|IETF: ML-DSA in X.509]]
----
<< [[.:start|← PKI Infrastructure]] | [[en:int:pqcrypt:szenarien:start|▲ Scenarios]] | [[.:intermediate_ca_erstellen|1.2 Intermediate CA →]] >>
{{tag>scenario pki root-ca ml-dsa-65 self-signed keypair certificate fips-204}}
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//
~~NOTOC~~