Module 1 of 7 ยท PKI Fundamentals

Creating a CA Root
Certificate

A hands-on guide to generating your own Certificate Authority root certificate using OpenSSL and installing it into the system trust store. Covers key generation, self-signed certificate creation, and cross-platform deployment.

โฑ ~45 minutes
๐ŸŽฏ Intermediate
๐Ÿ”ง OpenSSL 3.x
๐Ÿ“‹ 7 modules
01

Concepts & Overview

Before generating certificates, understand the trust hierarchy and what a CA root certificate actually does.

Certificate Trust Chain
Root CA
Your CA
โ†’
Intermediate CA
Signing CA
โ†’
Leaf Cert
server.example.com
๐Ÿ›๏ธ
Root CA
The trust anchor. Self-signed by definition โ€” it trusts itself. All certificates in the chain derive their trustworthiness from this root.
๐Ÿ”‘
Private Key
The root CA private key must be zealously protected. Any compromise allows forging trusted certificates. Keep it offline and encrypted.
๐Ÿ“œ
X.509 Certificate
The public-facing document. Contains the CA's public key, identity fields (Subject DN), validity period, and usage constraints.
๐Ÿช
Trust Store
The OS or browser's list of trusted root CAs. Adding your root here tells all applications on the system to trust certs signed by it.
โš ๏ธ
Scope of this Guide
This guide creates a root CA suitable for internal lab, development, or enterprise environments. Do not use a self-generated root CA for public internet services โ€” use an accredited public CA (e.g., Let's Encrypt) for those.
02

Prerequisites

Ensure your environment is ready before generating any key material.

1
Install OpenSSL 3.x
Required for all certificate operations
โ–ผ
bash
# Debian/Ubuntu
$ sudo apt-get update && sudo apt-get install -y openssl

# RHEL/CentOS/Fedora
$ sudo dnf install openssl

# Verify version
$ openssl version
OpenSSL 3.0.2 15 Mar 2022
zsh
# Install via Homebrew (recommended)
% brew install openssl@3

# Add to PATH
% echo 'export PATH="/opt/homebrew/opt/openssl@3/bin:$PATH"' >> ~/.zshrc
% source ~/.zshrc

% openssl version
OpenSSL 3.1.4
PowerShell (Admin)
# Option 1: via winget
PS> winget install ShiningLight.OpenSSL

# Option 2: via Chocolatey
PS> choco install openssl

# Restart terminal then verify
PS> openssl version
Mark as complete โ€” OpenSSL is installed and verified
2
Create Working Directory
Set up a secure folder structure for your CA files
โ–ผ

Create a dedicated directory with appropriate permissions. The root CA's private key should only be readable by its owner.

bash
$ mkdir -p ~/myCA/{certs,crl,newcerts,private,requests}
$ chmod 700 ~/myCA/private
$ touch ~/myCA/index.txt
$ echo 1000 > ~/myCA/serial
$ cd ~/myCA
zsh
% mkdir -p ~/myCA/{certs,crl,newcerts,private,requests}
% chmod 700 ~/myCA/private
% touch ~/myCA/index.txt
% echo 1000 > ~/myCA/serial
PowerShell
PS> New-Item -ItemType Directory -Path C:\myCA\certs, C:\myCA\private, C:\myCA\requests
PS> New-Item -ItemType File -Path C:\myCA\index.txt
PS> Set-Content -Path C:\myCA\serial -Value "1000"
PS> cd C:\myCA
๐Ÿ’ก
Directory Purpose
certs/ โ€” issued certificates ยท private/ โ€” private keys (700 perms) ยท crl/ โ€” revocation lists ยท index.txt โ€” certificate database ยท serial โ€” next serial number
Mark as complete โ€” directory structure created
03

Generate the Root Private Key

The private key is the most sensitive file in your CA. We generate a 4096-bit RSA key protected with AES-256 encryption.

๐Ÿ”’
Critical Security Warning
The root CA private key grants the ability to issue trusted certificates for any domain or user in your organization. Store it encrypted, back it up offline, and restrict access strictly. Never leave it unencrypted on disk.
1
Generate 4096-bit RSA Key with AES-256 Passphrase
Creates an encrypted private key file
โ–ผ

We use genrsa with the -aes256 flag to encrypt the key at rest. You'll be prompted for a strong passphrase โ€” this is required every time you use the key to sign certificates.

bash / zsh / PowerShell
# Generate the encrypted root CA private key
$ openssl genrsa -aes256 -out private/ca.key.pem 4096

Enter PEM pass phrase: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
Verifying - Enter PEM pass phrase: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ

# Lock down key permissions (Linux/macOS only)
$ chmod 400 private/ca.key.pem

# Verify key was created
$ ls -la private/
-r-------- 1 user user 3414 Jan 15 10:30 ca.key.pem
๐Ÿ’ก
Alternative: ECDSA Key
For modern deployments, an ECDSA P-384 key provides equivalent security with smaller size:
openssl ecparam -genkey -name secp384r1 | openssl ec -aes256 -out private/ca.key.pem
Mark as complete โ€” root key generated and permissions set
2
Verify the Key
Confirm key integrity before proceeding
โ–ผ

Inspect the key structure to confirm it was generated correctly and the encryption is in place.

bash / zsh / PowerShell
$ openssl rsa -in private/ca.key.pem -check -noout
Enter pass phrase for private/ca.key.pem:
RSA key ok
Mark as complete โ€” key verified successfully
04

Create the Root CA Certificate

Generate a self-signed X.509 certificate. This is the public-facing artifact that will be distributed and trusted.

1
Create OpenSSL Configuration File
Define CA extensions and Subject DN fields
โ–ผ

An openssl.cnf config file lets us bake in proper CA extensions โ€” specifically basicConstraints: CA:true and key usage flags that mark this as a signing authority.

openssl.cnf
[ req ]
default_bits = 4096
default_md = sha256
prompt = no
distinguished_name = req_distinguished_name
x509_extensions = v3_ca

[ req_distinguished_name ]
C = US
ST = California
L = San Francisco
O = My Organization
OU = IT Security
CN = My Organization Root CA
emailAddress = ca@myorg.example

[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
Config file created as openssl.cnf
2
Generate Self-Signed Root Certificate
Valid for 20 years with SHA-256
โ–ผ

Use req -x509 to generate a self-signed certificate directly from the private key and config. The -days 7300 flag sets a 20-year validity period, appropriate for root CAs.

bash / zsh / PowerShell
$ openssl req -new -x509 -sha256 \
-config openssl.cnf \
-key private/ca.key.pem \
-out certs/ca.cert.pem \
-days 7300

Enter pass phrase for private/ca.key.pem:
Certificate created successfully.

# Set read-only permissions
$ chmod 444 certs/ca.cert.pem
Root certificate generated at certs/ca.cert.pem
3
Inspect the Certificate
Verify Subject, Issuer, and extensions
โ–ผ
bash / zsh / PowerShell
$ openssl x509 -in certs/ca.cert.pem -noout -text

Certificate:
Data:
Version: 3 (0x2)
Subject: C=US, ST=California, O=My Organization, CN=My Organization Root CA
Issuer: C=US, ST=California, O=My Organization, CN=My Organization Root CA
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Key Usage: critical
Digital Signature, Certificate Sign, CRL Sign
โœ…
What to Look For
Confirm that Subject == Issuer (self-signed), CA:TRUE is present, and the Key Usage includes Certificate Sign. If any of these are missing, regenerate the certificate with the corrected config.
Certificate inspected and CA:TRUE confirmed
05

Install into Trusted Root Store

Add your root certificate to the system trust store so applications on this machine will trust certificates signed by your CA.

โš ๏ธ
System-Wide Trust
Installing a root CA into the system trust store affects all users and applications on the machine. Only install CAs you fully control and trust.
1
Install on Debian / Ubuntu
Uses update-ca-certificates
โ–ผ
bash (as root/sudo)
# Copy cert to system CA directory (must use .crt extension)
$ sudo cp certs/ca.cert.pem /usr/local/share/ca-certificates/my-org-ca.crt

# Update the CA trust bundle
$ sudo update-ca-certificates

Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
CA added to Debian/Ubuntu trust store
2
Install on RHEL / CentOS / Fedora
Uses update-ca-trust
โ–ผ
bash (as root/sudo)
# Copy cert to anchors directory
$ sudo cp certs/ca.cert.pem /etc/pki/ca-trust/source/anchors/my-org-ca.pem

# Rebuild trust bundle
$ sudo update-ca-trust extract
Trust store updated.
CA added to RHEL/Fedora trust store
1
Add to macOS Keychain (System)
Trusts for all users on the machine
โ–ผ
zsh (sudo required)
# Add to System keychain and always trust
% sudo security add-trusted-cert \
-d \
-r trustRoot \
-k /Library/Keychains/System.keychain \
certs/ca.cert.pem

# Verify it's in the store
% security find-certificate -c "My Organization Root CA" /Library/Keychains/System.keychain
๐Ÿ’ก
GUI Alternative
Open Keychain Access โ†’ Drag your .pem file into System keychain โ†’ Double-click the cert โ†’ Expand Trust โ†’ Set "When using this certificate" to Always Trust.
CA added to macOS System keychain
1
Import via PowerShell (Recommended)
Installs to the Local Machine Trusted Root store
โ–ผ
PowerShell (Admin)
# Import to LocalMachine Trusted Root Certification Authorities
PS> Import-Certificate \
-FilePath "C:\myCA\certs\ca.cert.pem" \
-CertStoreLocation Cert:\LocalMachine\Root

PSParentPath: Microsoft.PowerShell.Security\Certificate::LocalMachine\Root
Thumbprint Subject
A1B2C3D4... CN=My Organization Root CA

# Verify it's in the store
PS> Get-ChildItem Cert:\LocalMachine\Root | Where-Object { $_.Subject -like "*My Organization*" }
๐Ÿ’ก
MMC GUI Alternative
Run mmc.exe โ†’ File โ†’ Add Snap-in โ†’ Certificates โ†’ Computer Account โ†’ Local Computer โ†’ Expand Trusted Root Certification Authorities โ†’ Right-click Certificates โ†’ All Tasks โ†’ Import.
๐Ÿข
Enterprise Deployment via Group Policy
For domain-joined machines, deploy via GPO: Computer Configuration โ†’ Policies โ†’ Windows Settings โ†’ Security Settings โ†’ Public Key Policies โ†’ Trusted Root Certification Authorities โ†’ Import.
CA imported to Windows Trusted Root store
06

Verification & Testing

Confirm the CA is correctly installed and trusted by the system before issuing any certificates from it.

1
Verify CA is in System Trust Store
Confirm it's recognized by OpenSSL
โ–ผ
bash
# Test that OpenSSL recognizes the CA
$ openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt certs/ca.cert.pem
certs/ca.cert.pem: OK

# Or use update-ca-certificates bundle path directly
$ openssl x509 -in /etc/ssl/certs/my-org-ca.pem -noout -subject
subject=C=US, O=My Organization, CN=My Organization Root CA
zsh
# Find cert in system keychain
% security find-certificate -c "My Organization Root CA" -p /Library/Keychains/System.keychain | openssl x509 -noout -subject
subject=CN=My Organization Root CA, O=My Organization

# Test cert verification
% openssl verify certs/ca.cert.pem
certs/ca.cert.pem: OK
PowerShell
# List all root CAs containing "My Organization"
PS> Get-ChildItem Cert:\LocalMachine\Root |
Where-Object { $_.Subject -like "*My Organization*" } |
Select-Object Subject, Thumbprint, NotAfter

Subject Thumbprint NotAfter
CN=My Organization.. A1B2C3D4... 01/15/2045
CA verified in system trust store
2
Issue a Test Certificate & Verify Chain
End-to-end validation of your CA
โ–ผ

The true test: issue a leaf certificate from your root CA and verify the chain validates cleanly.

bash / zsh / PowerShell
# 1. Generate a test leaf key
$ openssl genrsa -out requests/test.key.pem 2048

# 2. Create a CSR for test.example.com
$ openssl req -new -sha256 \
-key requests/test.key.pem \
-subj "/CN=test.example.com/O=My Organization/C=US" \
-out requests/test.csr.pem

# 3. Sign the CSR with your root CA
$ openssl x509 -req -sha256 -days 365 \
-in requests/test.csr.pem \
-CA certs/ca.cert.pem \
-CAkey private/ca.key.pem \
-CAcreateserial \
-out certs/test.cert.pem

# 4. Verify the chain
$ openssl verify -CAfile certs/ca.cert.pem certs/test.cert.pem
certs/test.cert.pem: OK
โœ…
Chain Verified
If you see OK, your CA is correctly signing and the chain resolves. Your root CA is ready for production use within your environment.
Test certificate chain verified successfully
07

Knowledge Check

Test your understanding before completing the module.

1. What does basicConstraints: CA:true do in a root certificate?
A. Encrypts the certificate's private key with AES-256
B. Marks the certificate as a Certificate Authority, allowing it to sign other certificates
C. Sets the certificate validity period to unlimited
D. Enables CRL distribution point functionality
2. Why must Subject == Issuer in a root CA certificate?
A. OpenSSL requires it for SHA-256 hashing
B. It prevents the cert from expiring prematurely
C. Because root CAs are self-signed โ€” the issuer and subject are the same entity
D. To support OCSP stapling
3. On Ubuntu/Debian, what file extension must a CA certificate have when placed in /usr/local/share/ca-certificates/?
A. .pem
B. .crt
C. .cer
D. .der
4. Why should the root CA private key file be set to chmod 400 on Linux?
A. OpenSSL requires 400 permissions to read the file
B. It allows group members to read but not write the key
C. It restricts access to read-only by the owner only, preventing accidental overwrite or unauthorized access
D. It encrypts the file at the filesystem level