In some cases it is needed to create you own chain of certificates - CA and server (for example TLS testing). There are many descriptions out there on how to do it, nevertheless I couldn’t find any copy-paste examples which would give me a RSA, ECDSA and EdDSA certificates. Hence here below, one can find some instructions on how to use openssl to quickly create your own certs which, then can then be used during TLS verification.

This post doesn’t explain meaning of configuration used. If such explenation is needed I would suggest reading “Network Security with OpenSSL: Cryptography for Secure Communications”, by J. Viega or looking for required information at this blog.

Configuration file

OpenSSL uses configuration file in order to store information required during certificate creation. Configuraiton file contains things like organization name, address, location, internet address, default hash algorithm used to produce signatures, etc.

Name of both - my example CA and an organization for which server certificate will be created - is called “Cert Testing Organization” with an address www.cert_testing.com.

Here below configuration file used in this example. Copy & paste it to file openssl.cnf:

[ ca ]
# `man ca`
default_ca = CA_default

[ CA_default ]
# Directory and file locations.
dir               = .
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/root.key
certificate       = $dir/root.pem

# For certificate revocation lists.
crlnumber         = $dir/crlnumber
crl               = $dir/crl/intermediate.crl.pem
crl_extensions    = crl_ext
default_crl_days  = 30

# SHA-1 is deprecated, so use SHA-2 instead.
default_md        = sha256

name_opt          = ca_default
cert_opt          = ca_default
default_days      = 9999
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     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ 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

[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
stateOrProvinceName             = State or Province Name (full name)
localityName                    = Locality Name (eg, city)
organizationalUnitName          = Organizational Unit Name (eg, section)
commonName                      = Common Name

stateOrProvinceName_default     = PACA
countryName_default             = FR
localityName_default            = Cagnes sur Mer
organizationalUnitName_default  = Cert Testing Organization
commonName_default              = Cert Testing Organization
commonName_max                  = 64

[ 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

[ v3_intermediate_ca ]
# Extensions for a typical intermediate CA (`man x509v3_config`).
subjectKeyIdentifier        = hash
authorityKeyIdentifier      = keyid:always,issuer
basicConstraints            = critical, CA:true, pathlen:0
keyUsage                    = critical, digitalSignature, cRLSign, keyCertSign

[ usr_cert ]
# Extensions for client certificates (`man x509v3_config`).
basicConstraints        = CA:FALSE
nsCertType              = client, email
nsComment               = 'Cert Testing Intermediate - Client'
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid,issuer
keyUsage                = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage        = clientAuth, emailProtection

[ server_cert ]
# Extensions for server certificates (`man x509v3_config`).
basicConstraints        = CA:FALSE
nsCertType              = server
nsComment               = 'Cert Testing Intermediate - Server'
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid,issuer:always
keyUsage                = critical, digitalSignature, keyEncipherment
extendedKeyUsage        = serverAuth
subjectAltName          = @alt_names

[ crl_ext ]
# Extension for CRLs (`man x509v3_config`).
authorityKeyIdentifier  = keyid:always

[ ocsp ]
# Extension for OCSP signing certificates (`man ocsp`).
basicConstraints        = CA:FALSE
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid,issuer
keyUsage                = critical, digitalSignature
extendedKeyUsage        = critical, OCSPSigning

[alt_names]
DNS.1   = *.cert_testing.com
IP.1    = 127.0.0.1

Preparation

We will need some directories where output of cert generation will be stored:

mkdir -p private
mkdir -p certs
mkdir -p csr

CA cert creation

  1. CA private key

    First step is to create private key of CA cert. Root cert will use RSA keypair with key length of 4096 bits.

    OpenSSL will ask for pasword - provide test123.

    openssl genrsa -aes256 -out private/ca.key 4096 
    

    or in case of ECDSA certificates:

    openssl ecparam -name prime256v1 -genkey -noout -out private/ca.key
    openssl ec -in private/ca.key -out private/ca.key -aes256
    

    Here second line (encrypting ca.key) is needed only for rest of the article to be copy-paste’able.

  2. Create CA cert

    This command will use a key created above and create self-signed CA certificate. Certificate will be valid for 9999 days.

    Provide password test123 and hit enter on everything else. openssl will use values defined in openssl.cnf.

     openssl req -config openssl.cnf \
        -extensions v3_ca -new -x509 -days 9999 \
        -key private/ca.key \
        -out certs/ca.cert
    

    One interesting option to notice is -extensions v3_ca - it is reference to the section with the same name in openssl.cnf. This section tells the openssl that created certificate must be a CA cert (CA:true).

Server cert creation

In this example, certificate signing is done in 3 steps.

  • Create server certificate private key
  • Create certificate singing request
  • Sign the request with CA private key

So let’s do it.

  1. Server’s private key (I skip intermediate certs creation for the brevity).

    • RSA/2048 with e=3, for fast verification

      openssl genpkey -algorithm RSA \
          -pkeyopt rsa_keygen_bits:2048 \
          -pkeyopt rsa_keygen_pubexp:3 \
          -out private/rsa_2048.key
      
    • ECDSA/P-256

      openssl genpkey -algorithm EC \
          -pkeyopt ec_paramgen_curve:P-256 \
          -pkeyopt ec_param_enc:named_curve \
          -out private/ecdsa_p256.key
      
    • EdDSA/25519 (supported by newer version of openssl and in TLS 1.3 only)

      openssl genpkey -algorithm Ed25519 \
          -out private/ed25519.key
      
  2. Create certificate signing request - intermediary step

    • RSA

      openssl req -config openssl.cnf -new \
        -sha256 \
        -passin pass:test123 \
        -key private/rsa_2048.key \
        -out csr/rsa_2048.csr \
        -days 9999
      
    • ECDSA

      openssl req -config openssl.cnf -new \
        -sha256 \
        -passin pass:test123 \
        -key private/ecdsa_p256.key  \
        -out csr/ecdsa_p256.csr \
        -days 9999
      
    • EdDSA

      openssl req -config openssl.cnf -new \
        -passin pass:test123 \
        -key private/ed25519.key  \
        -out csr/ed25519.csr \
        -days 9999
      
  3. Create server cert

    Finally we can create set of server certificates.

    • RSA

      openssl x509 \
        -extfile openssl.cnf \
        -extensions server_cert -sha256 -req  \
        -CA certs/ca.cert -CAkey private/ca.key -CAcreateserial \
        -passin pass:test123 \
        -in csr/rsa_2048.csr \
        -out certs/rsa_2048.cert \
        -days 9999          
      
    • ECDSA

      openssl x509 \
        -extfile openssl.cnf \
        -extensions server_cert -sha256 -req  \
        -CA certs/ca.cert -CAkey private/ca.key -CAcreateserial \
        -passin pass:test123 \
        -in csr/ecdsa_p256.csr \
        -out certs/ecdsa_256.cert \
        -days 9999
      
    • EdDSA

      openssl x509 \
        -extfile openssl.cnf \
        -extensions server_cert -req  \
        -passin pass:test123 \
        -CA certs/ca.cert -CAkey private/ca.key -CAcreateserial \
        -passin pass:test123 \
        -in csr/ed25519.csr \
        -out certs/ed25519.cert \
        -days 9999          
      

It is currently believed that all private keys created above provide similar attack resistance, which is comparable to 128-bit symmetric cipher. Nevertheless, it’s worth to notice that size of those keys are much different.

Verification

In order to verify server certificate against CA following command can be used.

> openssl verify -CAfile certs/ca.cert certs/ecdsa_256.cert 
certs/ecdsa_256.cert: OK

That’s it, I hope it helps, but most of all I hope I won’t have to look for this stuff ever again.

Also

Thank you to @mattcaswell from OpenSSL team, for helping to figure out how to create EdDSA certs.