• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar
  • Skip to footer

Mister PKI

SSL Certificates * SSL Tools * Certificate Decoder

  • Home
  • OpenSSL
  • Keytool
  • SSL Tools
  • Donate
  • Cookie Policy (EU)
  • Contribute to Mister PKI (Cybersecurity Guest Posts)
  • PKI for DevOps Engineers (Free Training)

OpenSSL ca: Create and Manage an Internal Certificate Authority

August 12, 2026 by Mister PKI Leave a Comment

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:

  1. create the CA database;
  2. configure certificate issuance;
  3. create a CA key and certificate;
  4. generate a server CSR;
  5. review the CSR before signing;
  6. issue a server certificate;
  7. verify the certificate;
  8. inspect the CA database;
  9. revoke the certificate;
  10. generate a CRL;
  11. 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:

  1. load the CA certificate;
  2. load the CA private key;
  3. verify the CSR signature;
  4. check the CA policy;
  5. display the certificate details;
  6. ask whether the certificate should be signed;
  7. assign a serial number;
  8. create the certificate;
  9. 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

OptionPurpose
-config fileSpecifies the CA configuration file
-in fileSpecifies a CSR to sign
-out fileSpecifies certificate output
-infilesSigns multiple CSRs
-cert fileSpecifies the CA certificate
-keyfile fileSpecifies the CA private key
-passinSpecifies the private-key password source
-daysSets certificate lifetime
-mdSpecifies the signing digest where applicable
-policySelects the certificate subject policy
-extensionsSelects the certificate extension profile
-extfileLoads extensions from another file
-batchDisables interactive approval prompts
-rand_serialGenerates random certificate serial numbers
-revokeRevokes an issued certificate
-crl_reasonRecords the revocation reason
-gencrlGenerates a CRL
-statusChecks certificate status by serial
-updatedbMarks expired certificates in the database
-verboseShows additional processing information
-quietReduces 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.

linux,  openssl,  SSL Certificates

Reader Interactions

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Primary Sidebar

Popular Posts

PKCS12

openssl s_client

Keytool

Keytool list

ECDSA vs RSA

OpenSSL

PKCS7

Certificate Decoder

PKI for DevOps Engineers – Free Training Series

PKI for DevOps Training Hub

Lesson 1 – How TLS and PKI Work

Lesson 2 – Understanding X.509 Certificates

Lesson 3 – Certificate Chains Explained

Lesson 4 – Debug TLS with OpenSSL

Lesson 5 – Verify Certificate Chains

Lesson 6 – Creating CSRs with OpenSSL

Lesson 7 – Working with PKCS12 Certificates

Lesson 8 – Java Keystores and keytool

Lesson 9 – Certificate Expiration Monitoring

Lesson 10 – Automating Certificate Renewal

Lesson 11 – Common TLS Errors

Lesson 12 – PKI Architecture for DevOps

Recent Posts

  • OpenSSL ca: Create and Manage an Internal Certificate Authority
  • How to List OpenSSL Commands and Get Command Help
  • OpenSSL rehash: Create a Hashed CA Certificate Directory
  • OpenSSL asn1parse: Decode Certificates and ASN.1 Data
  • How to Create a Private CA with OpenSSL CA.pl

Footer

  • Twitter
  • YouTube

Copyright © 2026