In some cases, it is needed to create your 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 an RSA, ECDSA and EdDSA certificates. Hence, here below, one can find some instructions on how to use openssl to quickly create your 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:

  1[ ca ]
  2# `man ca`
  3default_ca = CA_default
  4
  5[ CA_default ]
  6# Directory and file locations.
  7dir               = .
  8certs             = $dir/certs
  9crl_dir           = $dir/crl
 10new_certs_dir     = $dir/newcerts
 11database          = $dir/index.txt
 12serial            = $dir/serial
 13RANDFILE          = $dir/private/.rand
 14
 15# The root key and root certificate.
 16private_key       = $dir/root.key
 17certificate       = $dir/root.pem
 18
 19# For certificate revocation lists.
 20crlnumber         = $dir/crlnumber
 21crl               = $dir/crl/intermediate.crl.pem
 22crl_extensions    = crl_ext
 23default_crl_days  = 30
 24
 25# SHA-1 is deprecated, so use SHA-2 instead.
 26default_md        = sha256
 27
 28name_opt          = ca_default
 29cert_opt          = ca_default
 30default_days      = 9999
 31preserve          = no
 32policy            = policy_loose
 33
 34[ policy_strict ]
 35# The root CA should only sign intermediate certificates that match.
 36# See the POLICY FORMAT section of `man ca`.
 37countryName             = match
 38stateOrProvinceName     = match
 39organizationName        = match
 40organizationalUnitName  = optional
 41commonName              = supplied
 42emailAddress            = optional
 43
 44[ policy_loose ]
 45# Allow the intermediate CA to sign a more diverse range of certificates.
 46# See the POLICY FORMAT section of the `ca` man page.
 47countryName             = optional
 48stateOrProvinceName     = optional
 49localityName            = optional
 50organizationName        = optional
 51organizationalUnitName  = optional
 52commonName              = supplied
 53emailAddress            = optional
 54
 55[ req ]
 56# Options for the `req` tool (`man req`).
 57default_bits        = 4096
 58distinguished_name  = req_distinguished_name
 59string_mask         = utf8only
 60
 61[ req_distinguished_name ]
 62countryName                     = Country Name (2 letter code)
 63stateOrProvinceName             = State or Province Name (full name)
 64localityName                    = Locality Name (eg, city)
 65organizationalUnitName          = Organizational Unit Name (eg, section)
 66commonName                      = Common Name
 67
 68stateOrProvinceName_default     = PACA
 69countryName_default             = FR
 70localityName_default            = Cagnes sur Mer
 71organizationalUnitName_default  = Cert Testing Organization
 72commonName_default              = Cert Testing Organization
 73commonName_max                  = 64
 74
 75[ v3_ca ]
 76# Extensions for a typical CA (`man x509v3_config`).
 77subjectKeyIdentifier        = hash
 78authorityKeyIdentifier      = keyid:always,issuer
 79basicConstraints            = critical, CA:true
 80keyUsage                    = critical, digitalSignature, cRLSign, keyCertSign
 81
 82[ v3_intermediate_ca ]
 83# Extensions for a typical intermediate CA (`man x509v3_config`).
 84subjectKeyIdentifier        = hash
 85authorityKeyIdentifier      = keyid:always,issuer
 86basicConstraints            = critical, CA:true, pathlen:0
 87keyUsage                    = critical, digitalSignature, cRLSign, keyCertSign
 88
 89[ usr_cert ]
 90# Extensions for client certificates (`man x509v3_config`).
 91basicConstraints        = CA:FALSE
 92nsCertType              = client, email
 93nsComment               = 'Cert Testing Intermediate - Client'
 94subjectKeyIdentifier    = hash
 95authorityKeyIdentifier  = keyid,issuer
 96keyUsage                = critical, nonRepudiation, digitalSignature, keyEncipherment
 97extendedKeyUsage        = clientAuth, emailProtection
 98
 99[ server_cert ]
100# Extensions for server certificates (`man x509v3_config`).
101basicConstraints        = CA:FALSE
102nsCertType              = server
103nsComment               = 'Cert Testing Intermediate - Server'
104subjectKeyIdentifier    = hash
105authorityKeyIdentifier  = keyid,issuer:always
106keyUsage                = critical, digitalSignature, keyEncipherment
107extendedKeyUsage        = serverAuth
108subjectAltName          = @alt_names
109
110[ client_cert ]
111# Extensions for server certificates (`man x509v3_config`).
112basicConstraints        = CA:FALSE
113nsCertType              = client, email
114nsComment               = 'Cert Testing EE - Client'
115subjectKeyIdentifier    = hash
116authorityKeyIdentifier  = keyid,issuer
117keyUsage                = critical, nonRepudiation, digitalSignature, keyEncipherment
118extendedKeyUsage        = clientAuth, emailProtection
119
120[ crl_ext ]
121# Extension for CRLs (`man x509v3_config`).
122authorityKeyIdentifier  = keyid:always
123
124[ ocsp ]
125# Extension for OCSP signing certificates (`man ocsp`).
126basicConstraints        = CA:FALSE
127subjectKeyIdentifier    = hash
128authorityKeyIdentifier  = keyid,issuer
129keyUsage                = critical, digitalSignature
130extendedKeyUsage        = critical, OCSPSigning
131
132[alt_names]
133DNS.1   = *.cert_testing.com
134IP.1    = 127.0.0.1

Preparation

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

1mkdir -p private
2mkdir -p certs
3mkdir -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 byte size of those keys are much different.

Client cert creation

Commands below will create client private key and certificate that can be used for mutual TLS (client authentication). Procedure is similar to creating server certificate, so I’ll do it only for ECDSA.

  1. Client’s private key
openssl genpkey -algorithm EC \
          -pkeyopt ec_paramgen_curve:P-256 \
          -pkeyopt ec_param_enc:named_curve \
          -out private/cli_ecdsa_p256.key
  1. Create certificate signing request - intermediary step
openssl req -config openssl.cnf -new \
          -sha256 \
          -passin pass:test123 \
          -key private/cli_ecdsa_p256.key  \
          -out csr/cli_ecdsa_p256.csr \
          -subj "/O=Cert Testing ORG/CN=Client Cert"
  1. Create client cert
openssl x509 \
          -extfile openssl.cnf \
          -extensions client_cert \
          -req  \
          -CA certs/ca.cert \
          -CAkey private/ca.key \
          -CAcreateserial \
          -in csr/cli_ecdsa_p256.csr \
          -passin pass:test123 \
          -out certs/cli_ecdsa_p256.cert \
          -days 9999

Verification

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

1> openssl verify -CAfile certs/ca.cert certs/ecdsa_256.cert
2certs/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.