Home / Alt manpages / openssl-ca(1ssl)

  • openssl-ca(1ssl)
  • OpenSSL command
  • linux

Issue and Revoke Test Certificates with OpenSSL ca

You will finish with a small OpenSSL CA directory that can sign a certificate signing request, record the certificate, revoke it and publish a certificate revocation list (CRL). The examples use the local openssl ca command, from OpenSSL 3.6.1, with the Ubuntu openssl package version 3.0.13-0ubuntu3.15 also installed. The manpage in this system is for OpenSSL 3.0.13, so check your own binary before relying on version-specific help output.

Allow 20 to 30 minutes. You need a shell, OpenSSL, and a private working directory. This is a learning and test workflow, not a production CA design. Do not point it at a live database or private key until you have backups, exclusive access and an operational recovery plan.

1. Check the binary and make a private workspace

Run these ordinary, read-only checks first:

$ command -v openssl
/home/linuxbrew/.linuxbrew/bin/openssl
$ openssl version
OpenSSL 3.6.1 27 Jan 2026

Your path and version may differ. The command is openssl ca, not a separate openssl-ca executable. It maintains a text database of issued certificates and their status, and can also generate CRLs.

Make a directory that only this test owns:

$ umask 077
$ mkdir -p "$HOME/openssl-ca-demo"/ca/{private,newcerts,certs}
$ touch "$HOME/openssl-ca-demo/ca/index.txt"
$ printf '%s\n' 1000 > "$HOME/openssl-ca-demo/ca/serial"

The CA private key will be written below. Keep the directory private. If you are adapting the example for a shared host, use an explicitly protected path instead of copying these permissions blindly.

2. Create a disposable CA certificate

This command creates a self-signed CA certificate and an unencrypted private key for the test. It changes state and creates signing authority, so do not use these exact options for a real CA:

$ cd "$HOME/openssl-ca-demo"
$ openssl req -x509 -newkey rsa:2048 -nodes \
    -keyout ca/private/cakey.pem \
    -out ca/cacert.pem \
    -days 30 \
    -subj '/CN=Example Test CA'

Expected output includes key-generation progress and a new ca/cacert.pem. The -nodes option leaves this test key unencrypted. For a real CA, use an encrypted key or hardware-backed storage, and arrange how the signer will be unlocked without exposing a password in the process list. To undo this disposable setup, stop using it and remove the test directory after checking its path carefully.

Checkpoint: verify the certificate subject and issuer without changing anything:

$ openssl x509 -in ca/cacert.pem -noout -subject -issuer
subject=CN = Example Test CA
issuer=CN = Example Test CA

3. Add the CA configuration

Create openssl.cnf in the workspace with the files that openssl ca requires:

[ ca ]
default_ca = CA_default

[ CA_default ]
dir = ./ca
database = $dir/index.txt
new_certs_dir = $dir/newcerts
certificate = $dir/cacert.pem
private_key = $dir/private/cakey.pem
serial = $dir/serial
default_days = 7
default_crl_days = 30
default_md = sha256
policy = policy_any
copy_extensions = none
name_opt = ca_default
cert_opt = ca_default

[ policy_any ]
commonName = supplied

The database and serial files are not optional in this example. default_days supplies the issued certificate lifetime, while default_crl_days supplies the CRL renewal interval. The policy says that the request must contain a common name. Fields not named by the policy are silently removed unless you use -preserveDN, so add policy entries deliberately rather than assuming every CSR field survives.

copy_extensions = none is a conservative starting point. Request extensions are ignored, including a requested subject alternative name. If you need SANs, define the extensions in CA configuration and inspect the result. Never enable copyall casually: a requester could supply basicConstraints = CA:TRUE and receive a certificate that can act as a CA.

4. Create and sign a CSR

Generate a leaf key and CSR. This is ordinary user-level work in the test directory:

$ openssl req -new -newkey rsa:2048 -nodes \
    -keyout leaf.key \
    -out leaf.csr \
    -subj '/CN=leaf.example.test'

Review the request before signing:

$ openssl req -in leaf.csr -noout -subject -verify
subject=CN = leaf.example.test
Certificate request self-signature verify OK

Signing changes the CA database and consumes the next serial number. Run it only when the request and output path are correct:

$ openssl ca -batch -config openssl.cnf \
    -in leaf.csr -out leaf.crt -notext
Using configuration from openssl.cnf
Check that the request matches the signature
Signature ok
Write out database with 1 new entries
Database updated

-batch suppresses the confirmation prompt and certifies automatically. Omit it for an interactive review when you are issuing a certificate manually. If you refuse a prompt, the manpage warns that an empty output file can remain, so check the file before treating a failed run as harmless.

Checkpoint: verify the chain and the recorded subject:

$ openssl verify -CAfile ca/cacert.pem leaf.crt
leaf.crt: OK
$ openssl x509 -in leaf.crt -noout -serial -subject -issuer
serial=1000
subject=CN = leaf.example.test
issuer=CN = Example Test CA

5. Inspect status before revoking

Use the serial from the certificate, not a filename guess:

$ openssl ca -config openssl.cnf -status 1000
1000=Valid (V)

There is a scripting trap here. This command prints the status, but on the installed OpenSSL it returns exit status 1 for a valid certificate. Capture and interpret the printed result rather than using a zero exit status as proof that the certificate is valid. The index file is the source of the state:

$ grep '	1000	' ca/index.txt
V	261002...Z		1000	unknown	/CN=leaf.example.test

The date is host-specific. Do not edit index.txt by hand. It is a critical database, and the manual says that rebuilding it is not supported.

6. Revoke the certificate and generate a CRL

Revocation is irreversible within this simple workflow. Before running it, confirm that you have selected the right certificate and that relying parties are prepared to consume the new CRL:

$ openssl ca -batch -config openssl.cnf \
    -revoke leaf.crt -crl_reason keyCompromise
Revoking Certificate 1000.
Database updated
$ grep '	1000	' ca/index.txt
R	261002...Z	260925...Z,keyCompromise	1000	unknown	/CN=leaf.example.test

Generate the CRL from the updated database:

$ openssl ca -config openssl.cnf -gencrl -out crl.pem
$ openssl crl -in crl.pem -noout -issuer -lastupdate -nextupdate
issuer=CN = Example Test CA

The exact dates vary. For a stronger check, inspect the revoked serial:

$ openssl crl -in crl.pem -noout -text | grep -A4 'Serial Number: 1000'
Serial Number: 1000
    Revocation Date: ...
    CRL entry extensions:
        X509v3 CRL Reason Code: keyCompromise

To test a different certificate, create a fresh disposable workspace or issue a new test certificate. Do not try to undo a real revocation by changing the database. The command has no locking, so never run two openssl ca processes against the same database concurrently.

7. Keep production boundaries clear

openssl ca is a sample, minimal CA application. The local manual explicitly warns that its code is not production quality. It keeps the database in memory, has no file locking and can produce unpredictable results when shared by concurrent runs. Protect the private key, restrict access to the CA directory, back up the database and serial files together, and arrange an operator-approved CRL publication process before using it for anything that matters.

For a one-off certificate, openssl req and openssl x509 may be a leaner fit. Use openssl ca when you specifically need its issued-certificate database, status queries, revocation tracking and CRL generation.

Done means

  • The CA certificate, private key, database, serial file and configuration are in one private workspace.
  • A CSR was inspected before signing and the resulting certificate verifies against the CA.
  • The issued serial appears in index.txt and -status reports it accurately.
  • Revocation changed the database from V to R, with the intended reason.
  • A CRL was generated and contains the revoked serial.
  • No production key or database was used, and no concurrent CA process can modify this test database.