Build a Throwaway CA and Certificate Bundle with CA.pl

CA.pl turns the handful of OpenSSL subcommands you would otherwise chain by hand into one script for standing up a disposable certificate authority. You will build the CA, generate a request, sign it, verify the result and package it as a PKCS#12 file, all useful for a lab, a browser test or a development service, but none of it a recipe for a production public CA. Allow about 20 minutes, most of it answering prompts for names and passwords.

The examples use the CA.pl installed with the Ubuntu openssl package, version 3.0.13-0ubuntu3.15. On this machine the script lives at /usr/lib/ssl/misc/CA.pl, off the normal shell PATH. The wrapper follows whichever OpenSSL version the openssl command on your path reports, unless you set OPENSSL.

Security boundary: Keep this work in a disposable directory. If you import the CA certificate anywhere, its private key can sign certificates that clients will trust. Never reuse it for a real service, and never let it leave the test environment.

1. Choose an empty working directory

CA.pl writes fixed names, demoCA, newkey.pem, newreq.pem, into whatever directory you run it from. Use a fresh one so an old request or CA database cannot get mixed into the run:

$ workdir=$(mktemp -d /tmp/ca-pl-demo.XXXXXX)
$ cd "$workdir"
$ command -v openssl
/home/linuxbrew/.linuxbrew/bin/openssl
$ test ! -e demoCA && echo 'empty CA workspace'
empty CA workspace

The workdir variable only lasts for this shell. Prefer a named directory instead? Give it permissions that shut out other users and confirm it holds no leftover CA files first.

2. Confirm the wrapper and its options

Run the script by its installed path. This is read-only and needs no sudo:

$ /usr/lib/ssl/misc/CA.pl -help
Usage:
    CA.pl -newcert | -newreq | -newreq-nodes | -xsign | -sign | -signCA | -signcert | -crl | -newca [-extra-cmd parameter]
    CA.pl -pkcs12 [certname]
    CA.pl -verify certfile ...
    CA.pl -revoke certfile [reason]

It is deliberately a thin front end over OpenSSL's req, ca, pkcs12, x509 and verify subcommands. Need a different policy, extension or file name? Use the relevant OpenSSL command directly rather than assuming CA.pl hides a switch for it.

3. Create the CA hierarchy

Start the interactive setup:

$ /usr/lib/ssl/misc/CA.pl -newca

Press ENTER at the first prompt to create a new CA, answer the distinguished-name questions, then set a strong passphrase when OpenSSL asks for the CA private key. This creates demoCA/ in the current directory: demoCA/private/cakey.pem, demoCA/cacert.pem, a certificate database and the directories for issued certificates.

Checkpoint: Stop if the output says demoCA/index.txt or demoCA/serial already exists. CA.pl will not overwrite an existing hierarchy, and you should not delete one casually either: that database is the record of every certificate issued and revoked. If this really was a failed disposable attempt, leave it alone and start fresh in a new temporary directory.

$ test -s demoCA/cacert.pem && test -s demoCA/private/cakey.pem && echo 'CA created'
CA created

4. Generate an encrypted certificate request

Create the user key and request:

$ /usr/lib/ssl/misc/CA.pl -newreq

OpenSSL asks for a passphrase to protect newkey.pem and for the request's subject fields. The key comes out encrypted; the request lands in newreq.pem. You can hand extra arguments to the underlying req command, for example to set a subject without giving up key encryption:

$ /usr/lib/ssl/misc/CA.pl -newreq -extra-req '-subj /C=GB/ST=Test/L=Test/O=Example/OU=Lab/CN=example.invalid'

Pick one, not both: the first form is fully interactive, the second still prompts for the encrypted key's passphrase but takes the subject from -subj. Check the files before moving on:

$ test -s newkey.pem && test -s newreq.pem && echo 'request and private key created'
request and private key created
$ openssl req -in newreq.pem -noout -subject
subject=...

5. Sign the request with the test CA

Issue the certificate against the CA database:

$ /usr/lib/ssl/misc/CA.pl -sign

OpenSSL asks for the CA's private-key passphrase and shows you the request. Only confirm if the subject is the one you meant to sign. The signed certificate lands in newcert.pem, and the CA database records the issuance:

$ test -s newcert.pem && echo 'certificate created'
certificate created
$ openssl x509 -in newcert.pem -noout -subject -issuer
subject=...
issuer=...

The issuer field should name the CA you just created, not the request's own subject. A certificate file existing on disk proves nothing by itself, check that relationship before you import anything into a browser or service.

6. Verify the certificate against the CA

-verify checks a certificate against demoCA/cacert.pem. Name the issued certificate explicitly:

$ /usr/lib/ssl/misc/CA.pl -verify newcert.pem
newcert.pem: OK

Some OpenSSL builds print extra diagnostic lines, but a genuine pass ends with OK and a zero exit status. If verification fails, check you are still sitting in the directory that holds the matching demoCA hierarchy. Do not copy a CA certificate over from another test directory just to make the check pass.

7. Export a PKCS#12 bundle

Package the user certificate, its private key and the CA certificate together:

$ /usr/lib/ssl/misc/CA.pl -pkcs12 'Example Lab Certificate'
PKCS #12 file is in newcert.p12
$ test -s newcert.p12 && echo 'PKCS#12 bundle created'
PKCS#12 bundle created

CA.pl expects newcert.pem, newkey.pem and demoCA/cacert.pem at exactly these relative paths. It prompts for the encrypted key's passphrase and a fresh export passphrase. Treat newcert.p12 as sensitive, it holds the private key. The name you pass is just the friendly label shown in most browser import dialogs.

Common traps

Done means