Install Certificate Authority (CA)
Lars Jönsson 2022-10-05
NOTE Setting of xmppAddr and dnsSRV shall probably be removed from
intermed-ca.cnf
as it seems like newer versions of OpenSSL has native support for XMPP address
These instructions are based on a guide available at https://roll.urown.net/ca/index.html
Preparations
Throughout the document, the commands uses the environment variable
DOMAIN
to point the domain. Set up the variable. Replace
example.net
with your own domain.
DOMAIN=example.net
Create base of the CA at ~/CA
cd ~
mkdir CA
Create a safe storage for storage of root and intermediate CA
cd ~/CA
dd if=/dev/zero bs=1M count=100 of=secret-device.img
sudo cryptsetup luksFormat secret-device.img
sudo cryptsetup open --type luks secret-device.img secret-device
sudo mkfs.ext4 /dev/mapper/secret-device
mkdir safe-storage
sudo mount /dev/mapper/secret-device safe-storage
sudo chown $USER:`id -gn` safe-storage
Create the Root CA
Directories and files
Create the directory structure in the safe storage and populate it with basic files.
cd ~/CA/safe-storage/
mkdir -p ${DOMAIN}.ca/root-ca/{certreqs,certs,crl,newcerts,private}
cd ${DOMAIN}.ca/root-ca/
chmod 700 private
touch root-ca.index
echo 00 > root-ca.crlnum
Set a random serial number for next openssl action.
openssl rand -hex 16 > root-ca.serial
Configuration file
A good starting point is found later in this document (root-ca.cnf) and available for download at:
wget https://roll.urown.net/_downloads/1daede3e61516d4ccdba1eaf26bdb86c/root-ca.cnf
Download the file and modify it according to the following diff:
81c81
< nameConstraints = critical, @name_constraints
---
> #nameConstraints = critical, @name_constraints
Replace example.net
with your own domain, in the same file:
organizationName = example.net
commonName = example.net Root Certification Authority
URI = http://ca.example.net/
email = certmaster@example.net
caIssuers;URI = http://ca.example.net/certs/example.net_Root_Certification_Authority.cert.pem
fullname = URI:http://ca.example.net/crl/example.net_Root_Certification_Authority.crl
Activate the configuration
export OPENSSL_CONF=./root-ca.cnf
Generate key and CSR
Create the private key and generate a certificate signing request at the same time.
openssl req -new -out root-ca.req.pem
Protect the private key
chmod 400 private/root-ca.key.pem
Show the CSR
Optionally take peek at the CSR.
openssl req -verify -in root-ca.req.pem -noout -text -reqopt no_version,no_pubkey,no_sigdump -nameopt multiline
Self-Signing of the Root Certificate
Self-sign the request.
openssl rand -hex 16 > root-ca.serial
openssl ca -selfsign -in root-ca.req.pem -out root-ca.cert.pem \
-extensions root-ca_ext \
-startdate `date +%y%m%d000000Z -u -d -1day` \
-enddate `date +%y%m%d000000Z -u -d +10years+1day`
Optionally take peek at the certificate
openssl x509 -in ./root-ca.cert.pem -noout -text \
-certopt no_version,no_pubkey,no_sigdump -nameopt multiline
Verify that the certificate is valid
openssl verify -verbose -CAfile root-ca.cert.pem root-ca.cert.pem
Revocation List (CRL)
Create an empty certificate revication list.
openssl ca -gencrl -out crl/root-ca.crl
Install the Root Certificate
Save the root certificate for later installation and use.
mkdir -p ~/CA/${DOMAIN}.ca/certs/
cp root-ca.cert.pem ~/CA/${DOMAIN}.ca/certs/
Create the Intermediate CA
Directories and files
Create the directory structure in the safe storage and populate it with basic files.
cd ~/CA/safe-storage/
mkdir -p ${DOMAIN}.ca/intermed-ca/{certreqs,certs,crl,newcerts,private}
cd ${DOMAIN}.ca/intermed-ca/
chmod 700 private
touch intermed-ca.index
echo 00 > intermed-ca.crlnum
Set a random serial number for next openssl action.
openssl rand -hex 16 > intermed-ca.serial
Configuration file
A good starting point is found later in this document (intermed-ca.cnf) and available for download at:
wget https://roll.urown.net/_downloads/7514c4fc42236f15a393223f7d7c98d9/intermed-ca.cnf
Download the file and modify it according to the following diff:
9a10
> ALTNAME =
73c74
< default_keyfile = private/intermed-ca.key
---
> default_keyfile = private/intermed-ca.key.pem
107 a109,121
> # Server Certificate Extensions with Subject Alternative Name
> [ server_ext_san ]
> basicConstraints = CA:FALSE
> keyUsage = critical, digitalSignature, keyEncipherment
> extendedKeyUsage = critical, serverAuth, clientAuth
> subjectKeyIdentifier = hash
> authorityKeyIdentifier = keyid:always
> issuerAltName = issuer:copy
> authorityInfoAccess = @auth_info_access
> crlDistributionPoints = crl_dist
> subjectAltName = $ENV::ALTNAME
>
> #
Replace example.net
with your own domain, in the same file:
organizationName = example.net
commonName = example.net Intermediate Certification Authority
URI = http://ca.example.net/
email = certmaster@example.net
caIssuers;URI = http://ca.example.net/certs/example.net_Intermediate_Certification_Authority.cert.pem
fullname = URI:http://ca.example.net/crl/example.net_Intermediate_Certification_Authority.crl
Activate the configuration
export OPENSSL_CONF=./intermed-ca.cnf
Generate key and CSR
Create the private key and generate a certificate signing request at the same time.
openssl req -new -out intermed-ca.req.pem
Protect the private key
chmod 400 private/intermed-ca.key.pem
Show the CSR
Optionally take peek at the CSR.
openssl req -verify -in intermed-ca.req.pem -noout -text -reqopt no_version,no_pubkey,no_sigdump -nameopt multiline
Sign the Intermediate CA with the Root CA
Copy the CSR to the Root CA
cp intermed-ca.req.pem ../root-ca/certreqs/
Go to the Root CA and invoke its configuration.
cd ~/CA/safe-storage/${DOMAIN}.ca/root-ca/
export OPENSSL_CONF=./root-ca.cnf
Sign the intermediate CSR with the root key with the intermediate extensions
openssl rand -hex 16 > root-ca.serial
openssl ca -in certreqs/intermed-ca.req.pem -out certs/intermed-ca.cert.pem \
-extensions intermed-ca_ext \
-startdate `date +%y%m%d000000Z -u -d -1day` \
-enddate `date +%y%m%d000000Z -u -d +5years+1day`
Optionally take peek at the certificate.
openssl x509 -in certs/intermed-ca.cert.pem -noout -text \
-certopt no_version,no_pubkey,no_sigdump -nameopt multiline
Verify the certificate
openssl verify -verbose -CAfile root-ca.cert.pem certs/intermed-ca.cert.pem
Copy the certificate to the Intermediate CA.
cp certs/intermed-ca.cert.pem ../intermed-ca/
Revocation List
Got to Intermediate CA and invoke its configuration.
cd ~/CA/safe-storage/${DOMAIN}.ca/intermed-ca/
export OPENSSL_CONF=./intermed-ca.cnf
Create an empty certificate revication list.
openssl ca -gencrl -out crl/intermed-ca.crl
Install the Intermediate Certificate
Save the intermediate certificate for later installation and use.
mkdir -p ~/CA/${DOMAIN}.ca/certs/
cp intermed-ca.cert.pem ~/CA/${DOMAIN}.ca/certs/
Install and use the Intermediate CA
Copy the Intermediate CA files to the working environment.
cp -R ~/CA/safe-storage/${DOMAIN}.ca/intermed-ca/ ~/CA/${DOMAIN}.ca/
Lock up safe storage
A copy of Intemediate CA is available in the working environment and and the Root and Intermediate CAs in the safe storage can be locked up (and stored in a safe place).
cd ~/CA
sudo umount safe-storage
sudo cryptsetup close secret-device
Usage of the Intermediate CA
Ensure that the environment variable DOMAIN
is set (see
Preparations).
The Intermediate CA working area is available at:
~/CA/${DOMAIN}.ca/intermed-ca
CSRs to be signed should be stored in:
~/CA/${DOMAIN}.ca/intermed-ca/certreqs
Signed certificates are stored in:
~/CA/${DOMAIN}.ca/intermed-ca/certreqs
Steps to sign a CSR:
Copy the CSR to the CA
cp <csr-file> ~/CA/${DOMAIN}.ca/intermed-ca/certreqs
Go the CA
cd ~/CA/${DOMAIN}.ca/intermed-ca
Invoke the configuration
export OPENSSL_CONF=./intermed-ca.cnf
Sign the certificate
When SAN (Subject Alternative Name) is set in the CSR
openssl rand -hex 16 > intermed-ca.serial openssl ca -notext \ -in certreqs/<function>.csr \ -out certs/<function>.crt \ -extensions server_ext
When SAN is missing in the CSR
openssl rand -hex 16 > intermed-ca.serial ALTNAME=DNS:<hostname-1>,DNS:<hostname-2>,IP:<ip-adress> \ openssl ca -notext \ -in certreqs/<function>.csr \ -out certs/<function>.crt \ -extensions server_ext_san
The Common Name (CN) of the CSR is usually used as the SAN and can be retrieved with the following command
openssl req -noout -subject -in certreqs/<function>.csr | \ sed -n '/^subject/s/^.*CN\s*=\s*//p'
Copy the certificate to the user
cp ~/CA/${DOMAIN}.ca/intermed-ca/certs/<cert-file> <user-location>
Configuration files
root-ca.cnf
#
# OpenSSL configuration for the Root Certification Authority.
#
#
# This definition doesn't work if HOME isn't defined.
CA_HOME = .
RANDFILE = $ENV::CA_HOME/private/.rnd
#
# Default Certification Authority
[ ca ]
default_ca = root_ca
#
# Root Certification Authority
[ root_ca ]
dir = $ENV::CA_HOME
certs = $dir/certs
serial = $dir/root-ca.serial
database = $dir/root-ca.index
new_certs_dir = $dir/newcerts
certificate = $dir/root-ca.cert.pem
private_key = $dir/private/root-ca.key.pem
default_days = 1826 # Five years
crl = $dir/root-ca.crl
crl_dir = $dir/crl
crlnumber = $dir/root-ca.crlnum
name_opt = multiline, align
cert_opt = no_pubkey
copy_extensions = copy
crl_extensions = crl_ext
default_crl_days = 180
default_md = sha256
preserve = no
email_in_dn = no
policy = policy
unique_subject = no
#
# Distinguished Name Policy for CAs
[ policy ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = supplied
organizationalUnitName = optional
commonName = supplied
#
# Root CA Request Options
[ req ]
default_bits = 4096
default_keyfile = private/root-ca.key.pem
encrypt_key = yes
default_md = sha256
string_mask = utf8only
utf8 = yes
prompt = no
req_extensions = root-ca_req_ext
distinguished_name = distinguished_name
subjectAltName = @subject_alt_name
#
# Root CA Request Extensions
[ root-ca_req_ext ]
subjectKeyIdentifier = hash
subjectAltName = @subject_alt_name
#
# Distinguished Name (DN)
[ distinguished_name ]
organizationName = example.net
commonName = example.net Root Certification Authority
#
# Root CA Certificate Extensions
[ root-ca_ext ]
basicConstraints = critical, CA:true
keyUsage = critical, keyCertSign, cRLSign
nameConstraints = critical, @name_constraints
subjectKeyIdentifier = hash
subjectAltName = @subject_alt_name
authorityKeyIdentifier = keyid:always
issuerAltName = issuer:copy
authorityInfoAccess = @auth_info_access
crlDistributionPoints = crl_dist
#
# Intermediate CA Certificate Extensions
[ intermed-ca_ext ]
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, keyCertSign, cRLSign
subjectKeyIdentifier = hash
subjectAltName = @subject_alt_name
authorityKeyIdentifier = keyid:always
issuerAltName = issuer:copy
authorityInfoAccess = @auth_info_access
crlDistributionPoints = crl_dist
#
# CRL Certificate Extensions
[ crl_ext ]
authorityKeyIdentifier = keyid:always
issuerAltName = issuer:copy
#
# Certificate Authorities Alternative Names
[ subject_alt_name ]
URI = http://ca.example.net/
email = certmaster@example.net
#
# Name Constraints
[ name_constraints ]
permitted;DNS.1 = example.net
permitted;DNS.2 = example.org
permitted;DNS.3 = lan
permitted;DNS.4 = onion
permitted;email.1 = example.net
permitted;email.2 = example.org
#
# Certificate download addresses for the root CA
[ auth_info_access ]
caIssuers;URI = http://ca.example.net/certs/example.net_Root_Certification_Authority.cert.pem
#
# CRL Download address for the root CA
[ crl_dist ]
fullname = URI:http://ca.example.net/crl/example.net_Root_Certification_Authority.crl
# EOF
intermed-ca.cnf
#
# OpenSSL configuration for the Intermediate Certification Authority.
#
#
# This definition doesn't work if HOME isn't defined.
CA_HOME = .
RANDFILE = $ENV::CA_HOME/private/.rnd
oid_section = new_oids
#
# XMPP address Support
[ new_oids ]
xmppAddr = 1.3.6.1.5.5.7.8.5
dnsSRV = 1.3.6.1.5.5.7.8.7
#
# Default Certification Authority
[ ca ]
default_ca = intermed_ca
#
# Intermediate Certification Authority
[ intermed_ca ]
dir = $ENV::CA_HOME
certs = $dir/certs
serial = $dir/intermed-ca.serial
database = $dir/intermed-ca.index
new_certs_dir = $dir/newcerts
certificate = $dir/intermed-ca.cert.pem
private_key = $dir/private/intermed-ca.key.pem
default_days = 730 # Two years
crl = $dir/crl/intermed-ca.crl
crl_dir = $dir/crl
crlnumber = $dir/intermed-ca.crlnum
name_opt = multiline, align
cert_opt = no_pubkey
copy_extensions = copy
crl_extensions = crl_ext
default_crl_days = 30
default_md = sha256
preserve = no
email_in_dn = no
policy = policy
unique_subject = no
#
# Distinguished Name Policy
[ policy ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
#
# Distinguished Name Policy for Personal Certificates
[ user_policy ]
countryName = supplied
stateOrProvinceName = optional
localityName = supplied
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = supplied
#xmppAddr = optional # Added to SubjAltName by req
#
# Intermediate CA request options
[ req ]
default_bits = 3072
default_keyfile = private/intermed-ca.key
encrypt_key = yes
default_md = sha256
string_mask = utf8only
utf8 = yes
prompt = no
req_extensions = req_ext
distinguished_name = distinguished_name
subjectAltName = subject_alt_name
#
# Intermediate CA Request Extensions
[ req_ext ]
subjectKeyIdentifier = hash
subjectAltName = @subject_alt_name
#
# Distinguished Name (DN)
[ distinguished_name ]
organizationName = example.net
commonName = example.net Intermediate Certification Authority
#
# Server Certificate Extensions
[ server_ext ]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = critical, serverAuth, clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
issuerAltName = issuer:copy
authorityInfoAccess = @auth_info_access
crlDistributionPoints = crl_dist
#
# Client Certificate Extensions
[ client_ext ]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
issuerAltName = issuer:copy
authorityInfoAccess = @auth_info_access
crlDistributionPoints = crl_dist
#
# User Certificate Extensions
[ user_ext ]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, clientAuth, emailProtection
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
issuerAltName = issuer:copy
authorityInfoAccess = @auth_info_access
crlDistributionPoints = crl_dist
#
# CRL Certificate Extensions
[ crl_ext ]
authorityKeyIdentifier = keyid:always
issuerAltName = issuer:copy
#
# Certificate Authorities Alternative Names
[ subject_alt_name ]
URI = http://ca.example.net/
email = certmaster@example.net
#
# Certificate download addresses for the intermediate CA
[ auth_info_access ]
caIssuers;URI = http://ca.example.net/certs/example.net_Intermediate_Certification_Authority.cert.pem
#
# CRL Download address for the intermediate CA
[ crl_dist ]
fullname = URI:http://ca.example.net/crl/example.net_Intermediate_Certification_Authority.crl
# EOF