Skip to content

Self-signed Certificates with a custom CA

by Alexander Puls, Berlin, March 15th 2024 last updated March 18th 2024

Intro

Generating CAs and Signing Certificates can be done with OpenSSL.

For the purpose of only generating keys, CA certificates and Certificate Signing Requests (CSRs), a stripped down configuration file is sufficiant.

OpenSSL Configuration File

The original configuration can be used as a template:

#
# file:     ${OPENSSL_CONF:-${XDG_CONFIG_HOME}/openssl_ca+srv.cnf}
# brief:    Strips down the Configuration of OpenSSL for the Purpose of
#           just issuing CAs and to Generate Signing Requests (CSRs)
# note:     the domains to validate for are listed at the end (up to 99)
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-

[ req ] # ____________________________________________________
distinguished_name  = req_distinguished_name
x509_extensions     = v3_ca  # additions for self signed certs
req_extensions      = v3_req # additions for requests

[ req_distinguished_name ] # _______________________________________
countryName                 = country name (2 letter code)
countryName_default         = DE
countryName_min             = 2
countryName_max             = 2
stateOrProvinceName         = state or province Name (full name)
stateOrProvinceName_default = Berlin
localityName                = locality name (e.g. city)
localityName_default        = Kreuzberg
0.organizationName          = organization name (e.g. company)
organizationalUnitName      = organizational unit/section/department
commonName                  = common name (e.g. FQDN or a person)
commonName_max              = 64
emailAddress                = email address
emailAddress_max            = 64

[ v3_ca ] # extensions for a typical CA _______________________
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid:always, issuer
#basicConstraints      = critical, CA:true, pathlen:0
basicConstraints       = CA:true, pathlen:0
keyUsage               = cRLSign, digitalSignature, keyCertSign
subjectAltName         = email:move

[ v3_req ] # extensions for requests ____
basicConstraints = CA:FALSE
extendedKeyUsage = serverAuth, clientAuth
subjectAltName   = @domain_names

[ domain_names ] 
IP.1  = 127.0.0.1
IP.2  = ::1
DNS.1 = localhost

# EOL openssl_ca+srv.cnf -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-

Commands

The variable $OPENSSL_CONF needs to be set if one wants to use a custom config in a different path.

Generating Private Keys for the CA and the CSRs

  1. Generating an encrypted and 4096 bit long private key for the CA: user@host:~$: openssl genrsa -aes256 -out [path to the generated key for the CA] 4096

  2. The key(s) for the CSR(s) need to be stored server-side and therefore often stay unencrypted. The command is rather similiar, except for encryption options and possibly the key length:
    openssl genrsa -out [path to a private key] 2048
    It is a kind of best practice to keep that keys shorter for performance reasons. They are meant to expire faster and changing them in case of a compromising isn't complicated since every service can have its own key, not effecting the security of other servers/services.

Optional:
One can use eliptic curves to generate (at least CA) keys:
The command openssl ecparam -out [path to the new root key] -name secp384r1 -genkey generates a key on the base of a 384 bit long (NIST equivalent) SECP curve.
Encryption has to be done in a second step, but of course is strongly advised in case of CA keys: openssl ec -aes256 -in [unencrypted ec key] -out [same key encrypted]

Generating CA Certificates

A CA certificate is generated with the subcommand req (for request) but with the parameter x509 to get out a certificate instead of a request:
user@host:~$ openssl req -x509 -new -nodes -sha512 [-utf8] -days [runtime in days] -key [path to the private CA key] -out [path to the (new) CA certificate]

Generating CSRs

Next step is to generate a signing request for a target service:
user@host:~$: openssl req -new -sha256 [-utf8] -key [path to the private CSR key] -out [path to the (new) CSR]

Note: A list of domain (or wildcard) for which the certificate shall be valid has to be listed below the subjectAltNames section in the configuration file or handed over as a parameter:

[ v3_req ]
 ..
 ..
subjectAltName   = @domain_names

[ domain_names ] 
IP.1  = ..
IP.2  = ..
DNS.1 = ..

Generating a (PEM) certificate for a Target Server

The subcommand is x509, the extension v3_req has to be handed over together with an extension file (which in this case is identical to the overall configuration file): user@host:~$: openssl x509 -req -extensions v3_req -extfile [e.g. /etc/ssl/openssl.cnf] -in [path to a generated CSR] -CA [path to the root CA certificate] -CAkey [path to the private root CA key] -CAcreateserial -sha256 -days [runtime in days] -outform PEM -out [path to the (new) certificate].pem

Usage

To validate against clients, the services need to hold their private key and certificate, while the clients have to import and hold the CA certificate to verify the services.