A private Certificate Authority (CA) is a server that issues and manages digital certificates for an organization’s internal use, rather than relying on public CAs. This is incredibly useful for securing internal web servers, VPNs, or any service where you need to verify the identity of clients or servers.
Let’s get one up and running. We’ll use openssl, the Swiss Army knife for all things crypto.
First, we need a directory structure to keep things organized.
mkdir -p ~/private-ca/{certs,crl,newcerts,private}
chmod 700 ~/private-ca/private
touch ~/private-ca/index.txt
echo 1000 > ~/private-ca/serial.txt
This sets up subdirectories for certificates (certs), certificate revocation lists (crl), new certificates (newcerts), and the CA’s private key (private). index.txt will store a database of issued certificates, and serial.txt will hold the next serial number to be used for a certificate.
Next, we create the CA’s private key and its self-signed root certificate. This is the "master key" that will sign all other certificates issued by this CA.
openssl genrsa -aes256 -out ~/private-ca/private/ca.key.pem 4096
openssl req -new -x509 -days 3650 -key ~/private-ca/private/ca.key.pem -out ~/private-ca/certs/ca.cert.pem
The first command generates a 4096-bit RSA private key, encrypted with AES-256. You’ll be prompted for a passphrase – make it strong and remember it! The second command creates the self-signed root certificate. You’ll be asked a series of questions for the certificate’s distinguished name (DN). The most important is Common Name (CN). For a root CA, this should be something descriptive like My Company Internal Root CA. The -days 3650 sets the certificate’s validity to 10 years.
Now, let’s create a configuration file for openssl that will help automate certificate issuance.
# ~/private-ca/openssl.cnf
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = ~/private-ca/
certs = $dir/certs
crl_dir = $dir/crl
new_certs_dir = $dir/newcerts
database = $dir/index.txt
serial = $dir/serial
private_key = $dir/private/ca.key.pem
certificate = $dir/certs/ca.cert.pem
crlnumber = $dir/crlnumber
crl = $dir/crl.pem
default_days = 365
default_crl_days= 30
default_md = sha256
preserve = no
policy = policy_match
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
default_bits = 2048
default_md = sha256
default_keyfile = private/private.key
prompt = yes
distinguished_name = req_distinguished_name
[ req_distinguished_name ]
countryName_default = US
stateOrProvinceName_default = California
localityName_default = San Francisco
organizationName_default = My Company
organizationalUnitName_default = IT Department
commonName_max = 64
emailAddress_max = 64
[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints=critical,CA:true,pathlen:0
[ v3_intermediate_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints=critical,CA:true,pathlen:1
[ server_cert ]
basicConstraints=CA:FALSE
nsCertType=server
nsComment="OpenSSL Server Certificate"
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer:always
keyUsage=critical,digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth
[ client_cert ]
basicConstraints=CA:FALSE
nsCertType=client,email
nsComment="OpenSSL Client Certificate"
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer:always
keyUsage=critical,digitalSignature,keyEncipherment
extendedKeyUsage=clientAuth,emailProtection
Save this as ~/private-ca/openssl.cnf. This file tells openssl where to find your CA files, default values for certificate fields, and extensions to apply. The [ v3_ca ] section is crucial; it marks this certificate as a CA and sets a path length of 0, meaning no intermediate CAs can be chained below it.
Now, let’s issue a certificate for a hypothetical internal web server. First, we need to generate a Certificate Signing Request (CSR) for the server.
openssl genrsa -out ~/private-ca/private/webserver.key.pem 2048
openssl req -new -key ~/private-ca/private/webserver.key.pem -out ~/private-ca/webserver.csr.pem -config ~/private-ca/openssl.cnf
The first command creates the server’s private key. The second creates the CSR. You’ll be prompted for DN information again. The Common Name (CN) here must match the hostname of your web server (e.g., internal.mycompany.com). You’ll also be prompted for a passphrase for the server’s private key.
Finally, we use our CA to sign the server’s CSR.
openssl ca -config ~/private-ca/openssl.cnf -days 365 -notext -md sha256 -in ~/private-ca/webserver.csr.pem -out ~/private-ca/certs/webserver.cert.pem -extensions v3_server
This command is the core of the CA operation.
-config ~/private-ca/openssl.cnf: Specifies our configuration file.-days 365: Sets the server certificate to be valid for 1 year.-in ~/private-ca/webserver.csr.pem: The server’s CSR.-out ~/private-ca/certs/webserver.cert.pem: Where the signed server certificate will be saved.-extensions v3_server: Applies the extensions defined in the[ server_cert ]section of ouropenssl.cnf, which is essential for a server certificate.
You’ll be prompted for the CA’s private key passphrase. Once entered, openssl will sign the CSR, create the server certificate, and add it to the index.txt database. The webserver.cert.pem file is what you’ll install on your web server.
To use this certificate, clients (browsers, other servers) connecting to internal.mycompany.com must trust your ca.cert.pem. You’ll need to distribute ca.cert.pem to all clients and import it into their trust stores. For example, on Linux systems, you might copy it to /etc/pki/ca-trust/source/anchors/ and run update-ca-trust. On Windows or macOS, you’d import it via their respective certificate management tools.
The one thing most people don’t realize is that the openssl ca command, when used with the policy setting in the openssl.cnf, enforces strict matching for certain DN fields (like countryName, stateOrProvinceName, organizationName) between the CSR and the CA’s own DN. If these don’t match, the issuance will fail. This is a security feature to prevent rogue certificates from being issued within your organization.
The next step is usually to learn how to manage certificate revocation, which involves creating and distributing Certificate Revocation Lists (CRLs).