Overview
The openssl ca command can turn OpenSSL into a small certificate authority capable of doing considerably more than simply signing a certificate.
It can:
- sign certificate signing requests;
- apply certificate policies;
- assign serial numbers;
- add X.509 extensions;
- maintain a database of issued certificates;
- track certificate revocation;
- query certificate status;
- generate certificate revocation lists;
- process multiple CSRs.
That makes it useful for system administrators who need a lightweight CA for:
- test environments;
- development systems;
- isolated internal services;
- lab infrastructure;
- application testing;
- TLS interoperability work;
- learning certificate authority operations.
However, openssl ca should not be confused with a full enterprise PKI platform.
The official OpenSSL ca documentation describes it as a sample minimal CA application and warns that it was not designed as production-quality CA software.
This tutorial uses a practical scenario:
An administrator needs to issue internal TLS certificates for several infrastructure servers without using a public certificate authority.
We will create a small CA structure and use openssl ca to:
- create the CA database;
- configure certificate issuance;
- create a CA key and certificate;
- generate a server CSR;
- review the CSR before signing;
- issue a server certificate;
- verify the certificate;
- inspect the CA database;
- revoke the certificate;
- generate a CRL;
- verify revocation.
OpenSSL version compatibility
This guide applies to:
- OpenSSL 3.0;
- OpenSSL 3.1;
- OpenSSL 3.2;
- OpenSSL 3.3;
- OpenSSL 3.4;
- OpenSSL 3.5 LTS;
- OpenSSL 4.0.
The primary examples follow the official OpenSSL 3.5 ca documentation.
Check your installed version:
openssl version
For detailed build information:
openssl version -a
The core workflow remains the same across OpenSSL 3.0, 3.5, and 4.0:
openssl ca -in request.csr -out certificate.crt
openssl ca -revoke certificate.crt
openssl ca -gencrl -out ca.crl
There are some version differences to keep in mind.
OpenSSL 3.0
OpenSSL 3.0 introduced the modern provider architecture.
The -section option was also added in OpenSSL 3.0.
The older:
-engine
option was deprecated in OpenSSL 3.0.
For new OpenSSL deployments, provider-based cryptography should be preferred over ENGINE-based configurations.
OpenSSL 3.2 and later
Beginning with OpenSSL 3.2, certificates generated by openssl ca are X.509 version 3, and key identifier extensions are included by default unless configuration changes that behavior.
You should still define important certificate extensions explicitly rather than depend entirely on defaults.
OpenSSL 3.5 LTS
OpenSSL 3.5 is the primary documentation target for this series.
The common openssl ca workflow remains compatible with OpenSSL 3.0 while providing a current LTS baseline.
OpenSSL 4.0
The command remains available in OpenSSL 4.0.
A notable difference is that the deprecated ENGINE command-line support has been removed from the openssl ca syntax.
OpenSSL 4.0 expects provider-based cryptographic integration instead.
For normal RSA and elliptic-curve certificate issuance using the default provider, the workflow in this guide remains largely unchanged.
When should a system administrator use openssl ca?
openssl ca makes sense when you need more certificate-authority functionality than:
openssl x509 -req
or:
openssl req -x509
provide.
Use openssl ca when you need to maintain state about certificates you have issued.
For example:
Certificate 1001 = valid
Certificate 1002 = revoked
Certificate 1003 = expired
The command maintains that information in its CA database.
This lets you perform operations such as:
openssl ca -status SERIAL
and:
openssl ca -revoke certificate.crt
and then generate a CRL from the recorded certificate state.
For a simpler wrapper around this process, see How to Create a Private CA with OpenSSL CA.pl.
openssl ca versus CA.pl
These are related but not identical tools.
CA.pl is essentially a convenience wrapper around commands such as:
openssl req
openssl ca
openssl verify
openssl pkcs12
Using:
CA.pl -sign
is easier for learning.
Using:
openssl ca
gives you substantially more control over:
- configuration;
- certificate policies;
- certificate extensions;
- CA databases;
- serial numbers;
- revocation;
- CRLs;
- batch processing;
- certificate validity;
- multiple certificate profiles.
If you need to manage a real lab CA rather than simply demonstrate CA operations, openssl ca is the more useful tool to understand.
Real-world scenario
Assume you administer several internal services:
monitoring.example.internal
logs.example.internal
automation.example.internal
These servers are accessible only inside the organization.
You want to issue TLS certificates from a private CA and maintain enough state that certificates can later be:
- renewed;
- revoked;
- audited;
- checked through a CRL.
For this example, we will issue a certificate for:
monitoring.example.internal
The CA will use this directory:
/opt/openssl-ca
The final layout will resemble:
/opt/openssl-ca/
├── certs/
├── crl/
├── csr/
├── issued/
├── newcerts/
├── private/
├── index.txt
├── index.txt.attr
├── serial
├── crlnumber
├── openssl-ca.cnf
├── ca.crt
└── private/
└── ca.key
Step 1: Create the CA directory
Create the directory structure:
sudo mkdir -p /opt/openssl-ca/{certs,crl,csr,issued,newcerts,private}
Restrict the CA directory:
sudo chown -R root:root /opt/openssl-ca
sudo chmod 700 /opt/openssl-ca/private
Set restrictive permissions before creating private keys:
umask 077
The CA private key is the most sensitive file in this environment.
Anyone who obtains it can potentially issue certificates trusted as coming from this CA.
Step 2: Create the OpenSSL CA database
Unlike a simple openssl x509 -req signing operation, openssl ca maintains state.
Create the database:
sudo touch /opt/openssl-ca/index.txt
Create the serial-number file:
echo 1000 | sudo tee /opt/openssl-ca/serial
Create the CRL-number file:
echo 1000 | sudo tee /opt/openssl-ca/crlnumber
Create the database attribute file:
echo 'unique_subject = no' | \
sudo tee /opt/openssl-ca/index.txt.attr
The CA database starts empty:
cat /opt/openssl-ca/index.txt
No output is expected.
What is index.txt?
The file:
index.txt
is OpenSSL’s certificate database.
It records information including:
- certificate status;
- expiration date;
- revocation date;
- serial number;
- certificate subject.
This file becomes critical once certificates are issued.
Do not treat it as a disposable cache.
Back it up with the rest of the CA state.
Why use unique_subject = no?
By default, OpenSSL historically expects valid certificates to have unique subjects.
That can create problems during certificate renewal.
For example, you may need to issue a replacement certificate with the same subject:
CN=monitoring.example.internal
before the existing certificate expires.
Setting:
unique_subject = no
allows multiple valid certificates with the same subject.
This is generally more practical for certificate renewal.
Step 3: Create the CA configuration file
Create:
/opt/openssl-ca/openssl-ca.cnf
with the following contents:
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = /opt/openssl-ca
certificate = $dir/ca.crt
private_key = $dir/private/ca.key
database = $dir/index.txt
new_certs_dir = $dir/newcerts
serial = $dir/serial
crlnumber = $dir/crlnumber
default_days = 365
default_crl_days = 30
default_md = sha256
policy = policy_server
email_in_dn = no
unique_subject = no
copy_extensions = copy
x509_extensions = server_cert
name_opt = ca_default
cert_opt = ca_default
[ policy_server ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
[ server_cert ]
basicConstraints = critical, CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
This configuration defines:
- where CA state is stored;
- which private key performs signing;
- which certificate identifies the CA;
- where issued certificates are written;
- how serial numbers are managed;
- how long certificates remain valid;
- which certificate subject fields are accepted;
- which X.509 extensions are applied.
The official OpenSSL X.509 extension documentation describes the syntax used for extensions such as basicConstraints, keyUsage, extendedKeyUsage, and subjectAltName.
Why copy_extensions requires care
This configuration contains:
copy_extensions = copy
This lets requested extensions such as Subject Alternative Name be carried from the CSR into the issued certificate when the CA profile does not replace them.
Do not casually change this to:
copy_extensions = copyall
A CSR is supplied by the requester.
A malicious or incorrectly generated CSR could contain:
basicConstraints = CA:TRUE
Blindly copying that extension could result in issuing a CA certificate rather than an ordinary server certificate.
That is why our server profile explicitly defines:
basicConstraints = critical, CA:FALSE
and:
keyUsage = critical, digitalSignature, keyEncipherment
The CA controls these values rather than trusting the CSR.
Step 4: Generate the CA private key
Generate an encrypted RSA CA key:
sudo openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:4096 \
-aes-256-cbc \
-out /opt/openssl-ca/private/ca.key
You will be prompted for a passphrase.
Verify the permissions:
sudo chmod 600 /opt/openssl-ca/private/ca.key
Inspect the key without displaying private values:
sudo openssl pkey \
-in /opt/openssl-ca/private/ca.key \
-check \
-noout
Expected output should indicate that the key is valid.
Do not place the CA private key:
- on web servers;
- in source control;
- in public cloud-init data;
- in screenshots;
- in ticket attachments;
- in shell-history arguments;
- in shared directories.
For important PKI environments, CA signing keys should generally be protected by stronger controls such as an HSM or equivalent hardware-backed key storage.
Step 5: Create the CA certificate
Create a self-signed CA certificate:
sudo openssl req \
-new \
-x509 \
-key /opt/openssl-ca/private/ca.key \
-sha256 \
-days 3650 \
-out /opt/openssl-ca/ca.crt \
-subj "/CN=Example Internal Issuing CA" \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign,cRLSign" \
-addext "subjectKeyIdentifier=hash"
This produces:
/opt/openssl-ca/ca.crt
Inspect it:
openssl x509 \
-in /opt/openssl-ca/ca.crt \
-noout \
-subject \
-issuer \
-serial \
-dates
Inspect the CA extensions:
openssl x509 \
-in /opt/openssl-ca/ca.crt \
-noout \
-ext basicConstraints \
-ext keyUsage
You should see:
CA:TRUE
and key usage permitting:
Certificate Sign
CRL Sign
For a deeper explanation of these fields, see Understanding X.509 Certificates.
Root CA versus issuing CA
For simplicity, this example uses one CA certificate for both:
- the trust anchor;
- certificate issuance.
That is acceptable for a disposable lab.
A stronger PKI design usually separates these roles:
Offline Root CA
|
v
Issuing CA
|
v
Server Certificates
The root private key remains offline and is used only to sign issuing CA certificates.
The issuing CA performs normal certificate issuance.
For a small test environment, a single CA is easier to demonstrate.
For important production infrastructure, consider a properly designed root-and-intermediate architecture or a dedicated PKI platform.
Step 6: Generate a server private key
Create a private key for the monitoring server:
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:2048 \
-out monitoring.example.internal.key
Protect it:
chmod 600 monitoring.example.internal.key
Validate it:
openssl pkey \
-in monitoring.example.internal.key \
-check \
-noout
The server’s private key never needs to be given to the CA.
Only the certificate signing request is submitted.
Step 7: Create a CSR with SANs
Generate a CSR:
openssl req \
-new \
-key monitoring.example.internal.key \
-out monitoring.example.internal.csr \
-subj "/CN=monitoring.example.internal" \
-addext "subjectAltName=DNS:monitoring.example.internal"
Modern TLS hostname validation relies on Subject Alternative Name.
The certificate should therefore contain:
DNS:monitoring.example.internal
rather than relying only on the Common Name.
For more CSR examples, see Create a CSR with OpenSSL.
Step 8: Review the CSR before signing
Never sign a CSR without examining it.
Run:
openssl req \
-in monitoring.example.internal.csr \
-noout \
-text \
-verify
Check:
- the subject;
- public-key algorithm;
- key length;
- SAN entries;
- requested extensions;
- CSR signature.
Display only the SAN section if desired:
openssl req \
-in monitoring.example.internal.csr \
-noout \
-text |
grep -A 2 "Subject Alternative Name"
Confirm that the request does not contain unexpected values.
A CA administrator should specifically look for:
- unauthorized DNS names;
- unexpected IP addresses;
- CA-related extensions;
- unusual key usages;
- unfamiliar OIDs.
Step 9: Sign the CSR with openssl ca
Issue the certificate:
sudo openssl ca \
-config /opt/openssl-ca/openssl-ca.cnf \
-in monitoring.example.internal.csr \
-out /opt/openssl-ca/issued/monitoring.example.internal.crt
OpenSSL will:
- load the CA certificate;
- load the CA private key;
- verify the CSR signature;
- check the CA policy;
- display the certificate details;
- ask whether the certificate should be signed;
- assign a serial number;
- create the certificate;
- update the CA database.
You will be asked to confirm issuance.
For example:
Sign the certificate? [y/n]:
Enter:
y
Then confirm the database update when prompted.
Step 10: Inspect the issued certificate
Display the certificate summary:
openssl x509 \
-in /opt/openssl-ca/issued/monitoring.example.internal.crt \
-noout \
-subject \
-issuer \
-serial \
-dates
Check the SAN:
openssl x509 \
-in /opt/openssl-ca/issued/monitoring.example.internal.crt \
-noout \
-ext subjectAltName
Expected:
DNS:monitoring.example.internal
Check basic constraints:
openssl x509 \
-in /opt/openssl-ca/issued/monitoring.example.internal.crt \
-noout \
-ext basicConstraints
Expected:
CA:FALSE
Check extended key usage:
openssl x509 \
-in /opt/openssl-ca/issued/monitoring.example.internal.crt \
-noout \
-ext extendedKeyUsage
Expected:
TLS Web Server Authentication
Step 11: Verify the certificate
Verify it against the CA:
openssl verify \
-CAfile /opt/openssl-ca/ca.crt \
/opt/openssl-ca/issued/monitoring.example.internal.crt
Expected result:
/opt/openssl-ca/issued/monitoring.example.internal.crt: OK
Also verify the hostname:
openssl verify \
-CAfile /opt/openssl-ca/ca.crt \
-verify_hostname monitoring.example.internal \
/opt/openssl-ca/issued/monitoring.example.internal.crt
Expected:
OK
For more certificate-chain troubleshooting, see OpenSSL Verify: Certificate, Chain, CRL, and Key Examples.
Step 12: Inspect the CA database
Display:
sudo cat /opt/openssl-ca/index.txt
You should now see an entry for the issued certificate.
An entry beginning with:
V
means the certificate is valid.
Other common status characters include:
R
for revoked certificates and:
E
for expired certificates after the database is updated.
The entry also stores information such as:
- expiration date;
- serial number;
- distinguished name.
Step 13: Inspect the serial number
Display:
cat /opt/openssl-ca/serial
If the original value was:
1000
OpenSSL increments it after issuance.
The exact resulting value depends on the serial-number configuration.
Each issued certificate must have a serial number unique for that CA.
Random serial numbers
OpenSSL also supports:
openssl ca -rand_serial ...
This generates a large random certificate serial rather than relying on the serial counter.
For a simple lab either approach can work.
If your CA configuration depends on serial files, protect and back them up along with the database.
Step 14: Find the archived certificate
openssl ca also writes certificates into:
newcerts
using their serial number as the filename.
List:
sudo ls -l /opt/openssl-ca/newcerts
You may see something resembling:
1000.pem
That file provides an archived copy of the issued certificate.
Do not rely solely on filenames such as:
monitoring.example.internal.crt
for CA state.
The CA database and serial-number archive are part of the authoritative certificate history.
Step 15: Check certificate status by serial number
First display the certificate serial:
openssl x509 \
-in /opt/openssl-ca/issued/monitoring.example.internal.crt \
-noout \
-serial
Example:
serial=1000
Query the CA database:
sudo openssl ca \
-config /opt/openssl-ca/openssl-ca.cnf \
-status 1000
For a valid certificate, OpenSSL reports its status as valid.
This becomes useful when operating a CA with many issued certificates.
Step 16: Issue certificates without interactive prompts
The normal signing workflow asks the administrator to confirm issuance.
For automation, use:
sudo openssl ca \
-config /opt/openssl-ca/openssl-ca.cnf \
-batch \
-in monitoring.example.internal.csr \
-out monitoring.example.internal.crt
The:
-batch
option suppresses signing questions.
This is convenient for automation, but it also removes an important human review step.
Do not combine:
-batch
with an untrusted source of CSRs unless another process has already performed:
- authorization;
- hostname validation;
- extension validation;
- profile enforcement;
- approval.
Automating certificate signing without controlling who can submit requests can turn a convenient lab CA into a serious security problem.
Step 17: Sign multiple CSRs
openssl ca can process several requests at once:
sudo openssl ca \
-config /opt/openssl-ca/openssl-ca.cnf \
-infiles \
server1.csr \
server2.csr \
server3.csr
The:
-infiles
option must appear last because everything after it is treated as an input request filename.
This can be useful for:
- lab provisioning;
- application migrations;
- batch certificate replacement;
- test environments.
For large-scale automated certificate enrollment, however, use a proper enrollment protocol or PKI platform rather than building a production issuance system around shell scripts and openssl ca.
Step 18: Revoke a certificate
Assume the monitoring server’s private key has been exposed.
Revoke its certificate:
sudo openssl ca \
-config /opt/openssl-ca/openssl-ca.cnf \
-revoke /opt/openssl-ca/issued/monitoring.example.internal.crt \
-crl_reason keyCompromise
OpenSSL supports reasons including:
unspecified
keyCompromise
CACompromise
affiliationChanged
superseded
cessationOfOperation
certificateHold
removeFromCRL
For a compromised server private key:
keyCompromise
is appropriate.
Step 19: Confirm the revocation in index.txt
Run:
sudo cat /opt/openssl-ca/index.txt
The entry should now begin with:
R
instead of:
V
This shows that OpenSSL has updated the CA database.
However, changing index.txt does not automatically tell clients that the certificate has been revoked.
Clients need a revocation source such as a CRL.
Step 20: Generate a CRL
Generate a certificate revocation list:
sudo openssl ca \
-config /opt/openssl-ca/openssl-ca.cnf \
-gencrl \
-out /opt/openssl-ca/crl/ca.crl
The CRL is generated from the certificate status stored in:
index.txt
Inspect it:
openssl crl \
-in /opt/openssl-ca/crl/ca.crl \
-noout \
-issuer \
-lastupdate \
-nextupdate
Display the revoked certificates:
openssl crl \
-in /opt/openssl-ca/crl/ca.crl \
-noout \
-text
You should see the revoked certificate’s serial number.
Step 21: Verify revocation
Use:
openssl verify \
-CAfile /opt/openssl-ca/ca.crt \
-CRLfile /opt/openssl-ca/crl/ca.crl \
-crl_check \
/opt/openssl-ca/issued/monitoring.example.internal.crt
The revoked certificate should fail validation.
You should receive an error indicating:
certificate revoked
That completes the basic certificate lifecycle:
CSR
↓
Issue
↓
Valid
↓
Revoke
↓
CRL
↓
Verification failure
Step 22: Update expired certificate entries
Use:
sudo openssl ca \
-config /opt/openssl-ca/openssl-ca.cnf \
-updatedb
This updates the database so expired certificates are marked accordingly.
You may see expired entries begin with:
E
Periodic database maintenance makes it easier to distinguish:
- currently valid certificates;
- expired certificates;
- revoked certificates.
Understanding certificate policies
The configuration contains:
policy = policy_server
And:
[ policy_server ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
Policy values can be:
match
supplied
optional
match
The CSR value must match the value in the CA certificate.
For example:
organizationName = match
could require the CSR to use the same organization as the CA.
supplied
The field must exist.
For example:
commonName = supplied
requires the CSR to include a Common Name.
optional
The field may be present but is not required.
An important OpenSSL behavior is that CSR subject fields not included in the policy may be omitted from the issued certificate.
Therefore, define the policy intentionally rather than assuming every field in the CSR will automatically appear in the certificate.
Certificate extensions versus CA policy
Policy controls certificate subject fields.
Extensions control certificate capabilities and additional identity information.
For example:
[ server_cert ]
basicConstraints = critical, CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
These prevent a normal server certificate from being treated as a CA certificate.
A server’s SAN usually comes from the CSR:
DNS:monitoring.example.internal
with:
copy_extensions = copy
The CA should still control security-critical extensions.
Why copyall is dangerous
Avoid:
copy_extensions = copyall
unless you fully understand and validate every CSR extension.
Consider a malicious CSR containing:
basicConstraints = critical,CA:TRUE
If the CA blindly copies that extension, it could issue a certificate capable of signing additional certificates.
That could effectively create an unauthorized subordinate CA.
A safer pattern is:
copy_extensions = copy
combined with CA-defined values:
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth
Because these extensions are already specified by the CA, requester-supplied versions are not used in their place.
Server and client certificate profiles
You can create separate extension sections.
For servers:
[ server_cert ]
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
For clients:
[ client_cert ]
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature
extendedKeyUsage = clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
Sign a client certificate with:
sudo openssl ca \
-config /opt/openssl-ca/openssl-ca.cnf \
-extensions client_cert \
-in client.csr \
-out client.crt
This prevents every certificate issued by the CA from automatically receiving the same purpose.
Issuing an intermediate CA certificate
You can also define an intermediate CA profile:
[ intermediate_ca ]
basicConstraints = critical,CA:TRUE,pathlen:0
keyUsage = critical,keyCertSign,cRLSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
Then sign an intermediate CA request:
sudo openssl ca \
-config /opt/openssl-ca/openssl-ca.cnf \
-extensions intermediate_ca \
-in intermediate-ca.csr \
-out intermediate-ca.crt
The:
pathlen:0
constraint means that intermediate CA should not issue another subordinate CA beneath itself.
Do not casually issue certificates using:
CA:TRUE
because they delegate certificate-signing authority.
Using -extfile for a separate certificate profile
Instead of keeping every profile in the main CA configuration, use:
openssl ca \
-config /opt/openssl-ca/openssl-ca.cnf \
-extfile server-extensions.cnf \
-extensions server_cert \
-in request.csr \
-out certificate.crt
This is useful when:
- different teams own different profiles;
- server and client profiles are stored separately;
- profile configuration is managed through automation;
- certificate types have very different extension requirements.
Controlling certificate validity
The CA configuration specifies:
default_days = 365
Override it:
sudo openssl ca \
-config /opt/openssl-ca/openssl-ca.cnf \
-days 90 \
-in request.csr \
-out certificate.crt
You can also explicitly specify:
-startdate
-enddate
or their aliases:
-not_before
-not_after
For most administrator-managed server certificates, using a simple validity period such as:
-days 90
or:
-days 365
is easier to audit than hard-coded calendar timestamps.
Shorter certificate lifetimes reduce the exposure period for forgotten credentials but require reliable renewal automation.
For lifecycle planning, see Automate Certificate Renewal.
Using random serial numbers
Instead of a sequential serial file:
1000
1001
1002
you can use:
openssl ca \
-rand_serial \
...
The configuration equivalent is:
rand_serial = yes
This generates large random serial numbers.
If random serials are enabled, the normal serial-number file is not used to assign certificate serials.
Whichever model you choose, avoid changing serial state casually once the CA is issuing certificates.
Back up the CA state
The CA is more than:
ca.key
ca.crt
A complete backup should include at least:
ca.crt
private/ca.key
index.txt
index.txt.attr
serial
crlnumber
newcerts/
openssl-ca.cnf
You may also see automatically generated backups such as:
index.txt.old
serial.old
The OpenSSL database is central to:
- revocation;
- certificate status;
- serial tracking;
- CRL generation.
Losing it significantly complicates CA recovery.
Do not run multiple openssl ca processes against the same database
openssl ca does not provide database locking suitable for multiple simultaneous CA operators.
Do not run two processes such as:
openssl ca process 1
openssl ca process 2
against the same:
index.txt
serial
state at the same time.
This can produce unpredictable database results.
Serializing CA operations is important even when certificate requests are generated automatically.
If you need:
- concurrent issuance;
- multiple administrators;
- high availability;
- approval workflows;
- large certificate volumes;
use a dedicated PKI platform rather than attempting to add concurrency around the OpenSSL text database.
CA private-key security
The CA private key deserves stronger protection than normal server keys.
At minimum:
- encrypt it with a strong passphrase;
- restrict filesystem permissions;
- prevent unnecessary copying;
- keep backups encrypted;
- limit administrator access;
- audit its use;
- isolate the CA host.
For higher-value environments, consider:
- HSM-backed keys;
- smart cards;
- hardware-backed provider integrations;
- an offline root CA;
- an isolated issuing CA.
OpenSSL 3 and later are designed around cryptographic providers, which should be preferred for modern hardware integration.
openssl ca is not an enterprise CA
OpenSSL itself warns that this command was originally intended as an example CA application.
It has important limitations:
- a text database;
- no multi-user database locking;
- difficult recovery if the index is corrupted;
- no built-in high availability;
- no native approval workflow;
- no automatic enrollment service;
- no built-in web administration;
- limited CRL capabilities;
- no native certificate inventory dashboard;
- awkward scaling for large numbers of certificates.
Use it where its simplicity is an advantage:
Lab
Development
Testing
Small isolated infrastructure
Learning
Troubleshooting
Do not assume that because OpenSSL can issue certificates it is automatically the right architecture for an enterprise PKI.
Common openssl ca errors
Could not open file or uri for loading CA private key
Check the configured path:
grep private_key /opt/openssl-ca/openssl-ca.cnf
Verify the file:
sudo ls -l /opt/openssl-ca/private/ca.key
Test the key:
sudo openssl pkey \
-in /opt/openssl-ca/private/ca.key \
-check \
-noout
Unable to load number from serial
Check:
cat /opt/openssl-ca/serial
The serial file must contain a valid hexadecimal serial value.
For example:
1000
If the CA is already active, do not arbitrarily reset this file.
Problem with index file
Confirm that:
index.txt
exists:
ls -l /opt/openssl-ca/index.txt
It should initially be an empty file.
Do not create an empty directory named index.txt.
TXT_DB error number 2
This commonly occurs when OpenSSL believes a valid certificate already exists with the same subject.
Check:
grep monitoring.example.internal \
/opt/openssl-ca/index.txt
For normal renewal workflows, consider:
unique_subject = no
in:
index.txt.attr
or the corresponding CA configuration.
The organizationName field is different between CA certificate and request
Your CA policy probably contains:
organizationName = match
That requires the CSR organization to match the CA certificate.
Either:
- create the CSR using the required organization; or
- intentionally change the CA policy.
Do not weaken the policy simply to bypass an error unless the change fits the CA’s intended identity rules.
There needs to be defined a directory for new certificate to be placed in
Check:
new_certs_dir = $dir/newcerts
Confirm the directory exists:
sudo mkdir -p /opt/openssl-ca/newcerts
Error while loading CRL number
Inspect:
cat /opt/openssl-ca/crlnumber
If your CA uses CRL numbers, the file must contain valid hexadecimal data.
Do not reset it after CRLs have already been issued.
Certificate does not contain SAN
Check the CSR:
openssl req \
-in request.csr \
-noout \
-text
Check whether the request contains:
X509v3 Subject Alternative Name
Then inspect:
copy_extensions
If:
copy_extensions = none
the requested SAN will not automatically be copied.
A safer configuration for server CSRs is typically:
copy_extensions = copy
while explicitly defining security-critical extensions in the CA certificate profile.
Unable to get local issuer certificate
After issuing a certificate, verify:
openssl verify \
-CAfile /opt/openssl-ca/ca.crt \
certificate.crt
If an intermediate CA is involved, provide it separately:
openssl verify \
-CAfile root-ca.crt \
-untrusted intermediate-ca.crt \
server.crt
See the OpenSSL verify guide for more chain troubleshooting.
Important openssl ca options
| Option | Purpose |
|---|---|
-config file | Specifies the CA configuration file |
-in file | Specifies a CSR to sign |
-out file | Specifies certificate output |
-infiles | Signs multiple CSRs |
-cert file | Specifies the CA certificate |
-keyfile file | Specifies the CA private key |
-passin | Specifies the private-key password source |
-days | Sets certificate lifetime |
-md | Specifies the signing digest where applicable |
-policy | Selects the certificate subject policy |
-extensions | Selects the certificate extension profile |
-extfile | Loads extensions from another file |
-batch | Disables interactive approval prompts |
-rand_serial | Generates random certificate serial numbers |
-revoke | Revokes an issued certificate |
-crl_reason | Records the revocation reason |
-gencrl | Generates a CRL |
-status | Checks certificate status by serial |
-updatedb | Marks expired certificates in the database |
-verbose | Shows additional processing information |
-quiet | Reduces command output |
For the complete option list, refer to the official OpenSSL 3.5 ca manual.
Frequently asked questions
What does openssl ca do?
openssl ca is a minimal certificate-authority application. It signs CSRs, issues X.509 certificates, maintains certificate status in a database, revokes certificates, and generates CRLs.
How do I sign a CSR with openssl ca?
Use:
openssl ca \
-config openssl-ca.cnf \
-in request.csr \
-out certificate.crt
The CA configuration defines the signing key, CA certificate, database, policy, and certificate extensions.
What is the difference between openssl ca and openssl x509 -req?
openssl x509 -req can sign a CSR without maintaining CA state.
openssl ca maintains:
- serial numbers;
- an issued-certificate database;
- certificate status;
- revocation information.
Use openssl ca when you need basic CA lifecycle management.
What is index.txt in an OpenSSL CA?
index.txt is the OpenSSL CA certificate database.
It records issued certificates and their state, including whether certificates are valid, expired, or revoked.
Can I delete index.txt?
Not on an active CA.
The database is critical to certificate-status tracking and CRL generation.
If it becomes corrupted or lost, recovery can be difficult.
What is the serial file?
The serial file contains the next certificate serial number when sequential serials are used.
OpenSSL updates it as certificates are issued.
Can openssl ca create a CRL?
Yes.
Run:
openssl ca \
-config openssl-ca.cnf \
-gencrl \
-out ca.crl
How do I revoke a certificate?
Run:
openssl ca \
-config openssl-ca.cnf \
-revoke certificate.crt \
-crl_reason keyCompromise
Then generate a new CRL.
What does -batch do?
-batch disables the interactive questions that normally ask an administrator to approve certificate issuance.
Use it carefully in automation.
Should I use copy_extensions = copyall?
Generally, no.
Blindly copying all requested CSR extensions can create serious security problems.
Use carefully controlled CA certificate profiles and explicitly define critical extensions such as:
basicConstraints
keyUsage
extendedKeyUsage
Can openssl ca be used in production?
Technically it can issue real certificates, but OpenSSL explicitly warns that the command was designed as a sample minimal CA and is not production-quality CA software.
For important enterprise environments, use a dedicated PKI solution or carefully designed CA architecture with appropriate controls.
Does openssl ca work with OpenSSL 4.0?
Yes.
The command remains available.
Provider-based cryptographic integrations should be used in place of older ENGINE-based configurations.
Practical command summary
Create the database:
touch /opt/openssl-ca/index.txt
echo 1000 > /opt/openssl-ca/serial
echo 1000 > /opt/openssl-ca/crlnumber
Create the CA key:
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:4096 \
-aes-256-cbc \
-out /opt/openssl-ca/private/ca.key
Create the CA certificate:
openssl req \
-new \
-x509 \
-key /opt/openssl-ca/private/ca.key \
-days 3650 \
-sha256 \
-out /opt/openssl-ca/ca.crt \
-subj "/CN=Example Internal Issuing CA" \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign,cRLSign"
Create the server CSR:
openssl req \
-new \
-key monitoring.example.internal.key \
-out monitoring.example.internal.csr \
-subj "/CN=monitoring.example.internal" \
-addext "subjectAltName=DNS:monitoring.example.internal"
Review it:
openssl req \
-in monitoring.example.internal.csr \
-noout \
-text \
-verify
Sign it:
openssl ca \
-config /opt/openssl-ca/openssl-ca.cnf \
-in monitoring.example.internal.csr \
-out monitoring.example.internal.crt
Verify it:
openssl verify \
-CAfile /opt/openssl-ca/ca.crt \
monitoring.example.internal.crt
Revoke it:
openssl ca \
-config /opt/openssl-ca/openssl-ca.cnf \
-revoke monitoring.example.internal.crt \
-crl_reason keyCompromise
Generate a CRL:
openssl ca \
-config /opt/openssl-ca/openssl-ca.cnf \
-gencrl \
-out ca.crl
Verify revocation:
openssl verify \
-CAfile /opt/openssl-ca/ca.crt \
-CRLfile ca.crl \
-crl_check \
monitoring.example.internal.crt
Related OpenSSL guides
- Browse the full OpenSSL Commands Guide.
- Create requests with Create a CSR with OpenSSL.
- Validate issued certificates with OpenSSL verify.
- Learn the simplified CA workflow in OpenSSL CA.pl.
- Inspect certificate structures with OpenSSL asn1parse.
- Build hashed CA directories with OpenSSL rehash.
- Learn how certificate chains connect server, intermediate, and root certificates.
- Review X.509 certificate fields and extensions.
- Plan certificate lifecycle management with Automate Certificate Renewal.
External references
- OpenSSL 3.5 ca documentation
- OpenSSL 3.0 ca documentation
- OpenSSL 4.0 ca documentation
- OpenSSL req documentation
- OpenSSL verify documentation
- OpenSSL x509 configuration documentation
- OpenSSL crl documentation
Conclusion
The openssl ca command is useful because it handles more than certificate signing.
It provides a basic CA lifecycle:
Create CA
↓
Receive CSR
↓
Review CSR
↓
Apply certificate policy
↓
Issue certificate
↓
Record certificate in database
↓
Verify certificate
↓
Revoke when necessary
↓
Generate CRL
That makes it a valuable tool for system administrators managing labs, development systems, internal testing environments, and PKI troubleshooting.
The most important lesson is that an OpenSSL CA is stateful.
These files matter:
ca.key
ca.crt
index.txt
serial
crlnumber
newcerts/
openssl-ca.cnf
Protect and back up the CA state, not only the signing key.
Also remember that openssl ca is intentionally minimal. Its text database, lack of locking, limited scalability, and manual operational model make it appropriate for controlled environments—not a drop-in replacement for a production enterprise certificate authority.
Leave a Reply