linuxopensslcertificatex509ca

How to use OpenSSL for self-signed certificates with custom CA and proper SAN settings?


I run across this topic once in a while, trying to remember how this was done then find all the incomplete answers again (with config files, oneliners,ignoring that we really want to use a custom CA to create a full certificate chain etc.). And many answers applying SAN for the certificate request, but messing SAN up in the signing command...

What I want to do?

  1. Create a custom CA
  2. Create a certificate request.
  3. Sign the certificate request.
  4. Create a chain file
  5. Additional steps in Firefox/Chrome (yes, Chrome needs SAN)

(Yes, please don't do everything in one command, I want my CA reside on another machine) (Yes, this might again be incomplete for somebody else. But this is exactly what I need 1-2 times a year, when a simple self-signed cert in my DEV environment, I simply trust by configuration, is not enough) (Yes, you might want to implement additional validation steps, if you are not the one requesting and signing certificates as one person)


Solution

  • Let us assume, we want to setup a self-signed certificate for our new server/host inside our LAN, where Let's Encrypt is not an option for us, e.g. FRITZ!Box, domain: fritz.box, https://fritz.box, IPv4: 192.168.1.1

    1.1 generate the private key for your CA

    openssl genrsa -aes256 -out patrickca.key 4096
    

    about -aes128 or -aes256: https://security.stackexchange.com/q/14068

    1.2 generate the CA certificate (root certificate)

    openssl req -new -key patrickca.key -x509 -out patrickca.crt -days 3650
    

    2. create your domain/server certificate request

    openssl req -new -nodes -newkey rsa:4096 -keyout fritzbox.key -out fritzbox.req -batch -subj "/C=DE/ST=Hamburg/L=Hamburg/O=Patrick CA/OU=router/CN=fritz.box" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:fritz.box,IP:192.168.1.1"))
    

    3. sign the certificate request

    openssl x509 -req -in fritzbox.req -CA patrickca.crt -CAkey patrickca.key -CAcreateserial -out fritzbox.crt -days 3650 -sha256 -extfile <(printf "subjectAltName=DNS:fritz.box,IP:192.168.1.1")
    

    4. build a chain file (you might want to have an additional intermediate certificate in your organizations process)

    cat fritzbox.key > fritzboxchain.pem 
    cat fritzbox.crt >> fritzboxchain.pem
    cat patrickca.crt >> fritzboxchain.pem 
    

    You would import this file into your FRITZ!Box, if this was not just an example.

    5. Additional steps in Firefox and Chrome (I don't use other browsers at the moment, and nobody else in my oganizations does)

    Of course you want to confirm that you know what your are doing, if your browser shows a warning message about your https certificate.

    And you might want to set this as a permanent exception.

    But: Please import the CA certificate of step 1.2 as a trusted CA. (in 05/2020 it should be enough for Firefox to have the CN= set, for Chrome you might have a SAN issue, if it still does not except your self-signed certificate)