Many people wake up one morning and realize they desperately need their very own offline root certificate authority. I imagine that is the case. This guide was created in conjunction with someone I respect greatly and whose preference is to remain anonymous. I cannot take credit for the entirety of this guide – total transparency.
The cleanest way to do this is on a machine you can in a safe for eternity after offloading the certificate generated to portable media that can be physically secured and accessed only for disaster recovery purposes.
- A computer running Red Hat Enterprise Linux 8 and with OpenSSL compiled with FIPS_MODE enabled.
- A secure physical location to store this computer when finished. NOTE: This machine's sole purpose in life is to create your root certificate authority, do not expect or attempt to use this machine for any other purpose.
Create The CA
In a shell, begin creating the files and directories you will need to place your keys and certs.
mkdir .rootca cd .rootca/ mkdir certs crl csr private newcerts chmod 700 private touch index.txt echo 1000 > serial touch config vi config
config file can be modified but should at a minimum contain something like this:
# OpenSSL Root Certificate Authority Configuration File. [ openssl_conf_section ] # Configuration module list alg_section = evp_sect [ evp_sect ] # Set to “yes” to enter FIPS mode if supported fips_mode = yes [ ca ] default_ca = CA_default [ CA_default ] # Directory and file locations. dir = /home/<your user>/.rootca certs = $dir/certs crl_dir = $dir/crl new_certs_dir = $dir/newcerts database = $dir/index.txt serial = $dir/serial RANDFILE = $dir/private/.rand # The root key and root certificate. private_key = $dir/private/root.enc.key certificate = $dir/certs/root.crt # For certificate revocation lists. crlnumber = $dir/crlnumber crl = $dir/crl/root.crl.pem crl_extensions = crl_ext default_crl_days = 90 # SHA-1 is deprecated, so use SHA-2 instead. default_md = sha512 name_opt = ca_default cert_opt = ca_default default_days = 3650 preserve = no policy = policy_loose [ policy_strict ] # The root CA should only sign intermediate certificates that match. # See the POLICY FORMAT section of `man ca`. countryName = match stateOrProvinceName = optional organizationName = match organizationalUnitName = match commonName = supplied emailAddress = supplied [ policy_loose ] # Allow the intermediate CA to sign a more diverse range of certificates. # See the POLICY FORMAT section of the `ca` man page. countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional [ req ] # Options for the `req` tool (`man req`). default_bits = 4096 distinguished_name = req_distinguished_name string_mask = utf8only # SHA-1 is deprecated, so use SHA-2 instead. default_md = sha512 # Extension to add when the -x509 option is used. x509_extensions = v3_ca [ req_distinguished_name ] countryName = Country Name (2 letter code) stateOrProvinceName = State or Province Name localityName = Locality Name 0.organizationName = Organization Name organizationalUnitName = Organizational Unit Name commonName = Common Name emailAddress = Email Address # Optionally, specify some defaults. countryName_default = US stateOrProvinceName_default = California localityName_default = Los Angeles 0.organizationName_default = SHE BASH organizationalUnitName_default = Engineering emailAddress_default = email@example.com [ v3_ca ] # Extensions for a typical CA (`man x509v3_config`). subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = critical, CA:true keyUsage = critical, digitalSignature, cRLSign, keyCertSign #authorityInfoAccess = caIssuers;URI:http://My.Server.net:8080/pki/My-RootCA.cert.pem #crlDistributionPoints = URI:http://My.Server.net:8080:8080/pki/My-RootCA.crl.pem [ v3_intermediate_ca ] # Extensions for a typical intermediate CA (`man x509v3_config`). subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = critical, CA:true, pathlen:2 keyUsage = critical, digitalSignature, cRLSign, keyCertSign #authorityInfoAccess = caIssuers;URI:https://pki.shebash.io:443/v1/pki/config/crl crlDistributionPoints = URI:https://pki.shebash.io:443/v1/pki/config/crl [ usr_cert ] # Extensions for client certificates (`man x509v3_config`). basicConstraints = CA:TRUE #nsCertType = client, email #nsComment = “OpenSSL Generated Client Certificate” #subjectKeyIdentifier = hash #authorityKeyIdentifier = keyid,issuer #keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment #extendedKeyUsage = clientAuth, emailProtection [ server_cert ] # Extensions for server certificates (`man x509v3_config`). basicConstraints = CA:TRUE #nsCertType = server #nsComment = “OpenSSL Generated Server Certificate” #subjectKeyIdentifier = hash #authorityKeyIdentifier = keyid,issuer:always #keyUsage = critical, digitalSignature, keyEncipherment #extendedKeyUsage = serverAuth [ crl_ext ] # Extension for CRLs (`man x509v3_config`). #authorityKeyIdentifier=keyid:always [ ocsp ] # Extension for OCSP signing certificates (`man ocsp`). basicConstraints = CA:TRUE #subjectKeyIdentifier = hash #authorityKeyIdentifier = keyid,issuer #keyUsage = critical, digitalSignature #extendedKeyUsage = critical, OCSPSigning
I highly recommend reading the manual page for a comprehensive explanation of all these parameters.
Generate Root CA Private Key
openssl genrsa 4096 > private/root.key
Encrypt The Private Key
openssl pkcs8 -in private/root.key -topk8 -out private/root.enc.key -v1 PBE-SHA1-3DES # Always store the key encrypted and don't forget your password rm private/root.key
Self Sign Private Key / Generate Public Cert for RootCA
openssl req -config config -key private/root.enc.key -new -x509 -days 3650 -sha512 -extensions v3_ca -out certs/root.crt # accept all but the following default vaules (defaults are assigned in the config file) # The CN can be modified to your needs CN = shebash.io
Sign the CSR For Your Intermediate Certificate Authority
Given that the root will be offline, this will be a useless exercise or at least highly impractical if you do not stand up an intermediate certificate authority to handle the issuance of certificates to the various clients that need them. You will have to generate a private key and certificate signing request (CSR).
You will want to store this private key in a secret store or someplace safe; it is a private key.
Procedurally, however, run the following on the machine you will run the intermediate certificate CA:
# you'll need to find a means to bring the server.csr contents onto the root CA machine. openssl req -new -newkey rsa:4096 -nodes -keyout server.key -out server.csr cat server.csr
Take the entire contents of your CSR and on the Root CA machine (in the same directory context) run:
# paste the contents of your csr into a file within the csr directory vi csr/int_ca.csr openssl ca -config config -extensions v3_intermediate_ca -days 3650 -notext -md sha512 -in csr/int_ca.csr -out certs/int_ca.crt
Verify The Chain of Trust
openssl verify -CAfile certs/root.crt certs/int_ca.crt
Assuming the output is, then your
int_ca.crt along with the public certificate of your Root CA can be bundled and serve as the intermediate CA fully trusted internally. While the process is 100% known to you as trusted, this will not grant you public trust. CA Browser Forum gives public trust. However, for internal PKI, an offline Root CA is the most secure way to handle PKI for an organization to mitigate the compromise of the root CA.