Skip to content

Instantly share code, notes, and snippets.

@fntlnz
Last active December 3, 2025 20:05
Show Gist options
  • Select an option

  • Save fntlnz/cf14feb5a46b2eda428e000157447309 to your computer and use it in GitHub Desktop.

Select an option

Save fntlnz/cf14feb5a46b2eda428e000157447309 to your computer and use it in GitHub Desktop.
Self Signed Certificate with Custom Root CA

Create Root CA (Done once)

Last update: Nov 2025.

Create Root Key

Attention: this is the key used to sign the certificate requests, anyone holding this can sign certificates on your behalf. So keep it in a safe place!

openssl ecparam -genkey -name secp384r1 | openssl ec -aes256 -out rootCA.key

Create and self sign the Root Certificate

openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 3650 -out rootCA.crt

Here we used our root key to create the root certificate that needs to be distributed in all the computers that have to trust us.

Create a certificate (Done for each server)

This procedure needs to be followed for each server/appliance that needs a trusted certificate from our CA

Create the certificate key

openssl ecparam -genkey -name prime256v1 -noout -out mydomain.com.key

Create the signing request (csr)

The certificate signing request is where you specify the details for the certificate you want to generate. This request will be processed by the owner of the Root key (you in this case since you create it earlier) to generate the certificate.

Important: Please mind that while creating the signing request it is important to specify the Common Name providing the IP address or domain name for the service, otherwise the certificate cannot be verified.

Important: Modern browsers and clients require Subject Alternative Names (SANs). The Common Name alone is no longer sufficient.

I will describe here two ways to generate the csr

Method A (Interactive)

If you generate the csr in this way, openssl will ask you questions about the certificate to generate like the organization details and the Common Name (CN) that is the web address you are creating the certificate for, e.g mydomain.com.

openssl req -new -key mydomain.com.key -out mydomain.com.csr

Method B (One Liner)

This method generates the same output as Method A but it's suitable for use in your automation :) .

openssl req -new -sha256 -key mydomain.com.key -subj "/C=US/ST=CA/O=MyOrg, Inc./CN=mydomain.com" -out mydomain.com.csr

If you need to pass additional config you can use the -config parameter, here for example I want to add alternative names to my certificate.

openssl req -new -sha256 \
    -key mydomain.com.key \
    -subj "/C=US/ST=CA/O=MyOrg, Inc./CN=mydomain.com" \
    -reqexts SAN \
    -config <(cat /etc/ssl/openssl.cnf \
        <(printf "\n[SAN]\nsubjectAltName=DNS:mydomain.com,DNS:www.mydomain.com")) \
    -out mydomain.com.csr

Verify the csr's content

openssl req -in mydomain.com.csr -noout -text

Generate the certificate using the mydomain csr and key along with the CA Root key

Important: Modern certificates require Subject Alternative Names (SANs) to be included during signing, not just in the CSR.

Create an extensions file (mydomain.com.ext):

authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = mydomain.com
DNS.2 = www.mydomain.com

Then sign the certificate:

openssl x509 -req -in mydomain.com.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out mydomain.com.crt -days 397 -sha256 -extfile mydomain.com.ext

Note: 397 days is the maximum validity now accepted by major browsers. For internal use, you may use longer periods (e.g., -days 825), but shorter validity is recommended.

Verify the certificate's content

openssl x509 -in mydomain.com.crt -text -noout

Check that the Subject Alternative Names are present:

openssl x509 -in mydomain.com.crt -noout -ext subjectAltName
@shreeve
Copy link

shreeve commented Dec 17, 2022

#!/bin/bash

# =============================================================================
# ssl-certs.sh - Self signing SSL certificates
#
# Author: Steve Shreeve <[email protected]>
#   Date: Dec 17, 2022
# =============================================================================

# Use https://gist.github.com/shreeve/3358901a26a21d4ddee0e1342be7749d
# See https://gist.github.com/fntlnz/cf14feb5a46b2eda428e000157447309

# variables
name="ACME, Inc."
base="acme.com"
root="root"
myip="$(ifconfig | awk '/inet / { print $2 }' | grep -v -E "^127\." | head -1)"

# create root key and certificate
openssl genrsa -out "${root}.key" 3072
openssl req -x509 -nodes -sha256 -new -key "${root}.key" -out "${root}.crt" -days 731 \
  -subj "/CN=Custom Root" \
  -addext "keyUsage = critical, keyCertSign" \
  -addext "basicConstraints = critical, CA:TRUE, pathlen:0" \
  -addext "subjectKeyIdentifier = hash"

# create our key and certificate signing request
openssl genrsa -out "${base}.key" 2048
openssl req -sha256 -new -key "${base}.key" -out "${base}.csr" \
  -subj "/CN=*.${base}/O=${name}/OU=$(whoami)@$(hostname) ($(/usr/bin/id -F))" \
  -reqexts SAN -config <(echo "[SAN]\nsubjectAltName=DNS:${base},DNS:*.${base},IP:127.0.0.1,IP:${myip}\n")

# create our final certificate and sign it
openssl x509 -req -sha256 -in "${base}.csr" -out "${base}.crt" -days 731 \
  -CAkey "${root}.key" -CA "${root}.crt" -CAcreateserial -extfile <(cat <<END
    subjectAltName = DNS:${base},DNS:*.${base},IP:127.0.0.1,IP:${myip}
    keyUsage = critical, digitalSignature, keyEncipherment
    extendedKeyUsage = serverAuth
    basicConstraints = CA:FALSE
    authorityKeyIdentifier = keyid:always
    subjectKeyIdentifier = none
END
)

# update the macOS trust store (TODO: add other operating systems)
sudo /usr/bin/security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${root}.crt"

# review files
echo "--"; openssl x509 -in "${root}.crt" -noout -text
echo "--"; openssl req  -in "${base}.csr" -noout -text
echo "--"; openssl x509 -in "${base}.crt" -noout -text
echo "--";

@yura833
Copy link

yura833 commented Oct 13, 2023

Hi i am encouraging error like this:

So i am using reverse proxy for my web server and want ssl certificate to be included in my nginx config file so i followed this
guide here that creates ssl certificate and did these steps to create csr:
openssl genrsa -des3 -out rootCA.key 4096

openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.crt

openssl genrsa -out mydomain.com.key 2048

openssl req -new -sha256
-key SERVER.key
-subj "/C=US/ST=North Carolina/O=ORG/OU=ORG_UNIT/CN=mydomain.com"
-reqexts SAN
-config <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:mydomain.com"))
-out SERVER.csr
then i used this for signing certifiacte for my web server:
openssl x509 -req -extfile <(printf "subjectAltName=DNS:YOUR_DOMAIN_NAME") -days 120 -in SERVER.csr -CA rootCA.crt -CAkey root_rsa.key -CAcreateserial -out SERVER.crt -sha256

and everything looked fine btw i am using swarm, and when i try to connect to server i get 502 bad gateway and after using curl https://mydomain.com i get this error:

curl: (35) OpenSSL/3.1.3: error:0A00010B:SSL routines::wrong version number

inside my container logs it shows this:
failed (SSL: error:0A00010B:SSL routines::wrong version number) while SSL handshaking to upstream, client: 10.0.0.2, server: , request: "GET / HTTP/1.1", upstream

does anyone have idea what can i do here ?

@sourabhsarkar
Copy link

I followed your guide but cannot verify created certificate with root CA

openssl verify -CAfile rootCA.crt mydomain.com.crt C = AU, ST = Some-State, O = Internet Widgits Pty Ltd error 18 at 0 depth lookup: self signed certificate error mydomain.com.crt: verification failed

@notariuss you should use: openssl verify -CAfile C:\ca-cert.pem C:\mycert.pem

@bogushevich
Copy link

Hi
@fntlnz Why do you use parameter -CAcreateserial?
From docs https://docs.openssl.org/master/man1/openssl-x509/ :

"If the -CA option is specified and neither <-CAserial> or <-CAcreateserial> is given and the default serial number file does not exist, a random number is generated; this is the recommended practice."

@FLAK-ZOSO
Copy link

Please, do not use 3DES as it was deprecated back in 2019 by NIST that had made it a standard.

@fntlnz
Copy link
Author

fntlnz commented Nov 17, 2025

@bogushevich that is to create the srl file, it tracks the serials of issued certificates

@fntlnz
Copy link
Author

fntlnz commented Nov 17, 2025

I just updated the guide to bring it to 2025. Any suggestions are welcome!

@FLAK-ZOSO
Copy link

Thank you: we all copy paste blindly and old posts for how accurate when first written can become dangerous when it comes to cryptography.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment