Skip to content

Instantly share code, notes, and snippets.

@fntlnz
Last active November 14, 2024 10:34
Show Gist options
  • Save fntlnz/cf14feb5a46b2eda428e000157447309 to your computer and use it in GitHub Desktop.
Save fntlnz/cf14feb5a46b2eda428e000157447309 to your computer and use it in GitHub Desktop.
Self Signed Certificate with Custom Root CA

Create Root CA (Done once)

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 genrsa -des3 -out rootCA.key 4096

If you want a non password protected key just remove the -des3 option

Create and self sign the Root Certificate

openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -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 genrsa -out mydomain.com.key 2048

Create the signing (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 signign request is important to specify the Common Name providing the IP address or domain name for the service, otherwise the certificate cannot be verified.

I will describe here two ways to gener

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

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

Verify the certificate's content

openssl x509 -in mydomain.com.crt -text -noout
@garydmx29
Copy link

Does the root key and subsequent steps have to be done on the server hosting/using the SSL setup?

@yo1995
Copy link

yo1995 commented Sep 22, 2019

The commands I used for adding SAN to the request and certificate are

$ openssl req -new -sha256 \
    -key SERVER.key \
    -subj "/C=US/ST=North Carolina/O=ORG/OU=ORG_UNIT/CN=YOUR_DOMAIN_NAME" \
    -reqexts SAN \
    -config <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:YOUR_DOMAIN_NAME")) \
    -out SERVER.csr

and for signing

$ 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

based on the discussions above.

It finally results in a secure connection with Chrome.
Picture1

Hope it helps people who come here for classes or server SSL setup! 😄

@fhajji
Copy link

fhajji commented Oct 1, 2019

Thanks, that was very helpful.

Unfortunately, RSA keys are rather slow to process. It would be better to generate Elliptic Curve keys for the servers, and have them signed by the root CA. It looks like Let's Encrypt would do this already:

https://dev.to/benjaminblack/obtaining-an-elliptic-curve-dsa-certificate-with-lets-encrypt-51bc

It may be even better, it the root CA would use Elliptic Curve keys to sign any certificate that is thrown at it... but would certificates signed (by the root CA) with an EC key be accepted by the majority of TLS clients out there?

@itsnuwan
Copy link

The commands I used for adding SAN to the request and certificate are

$ openssl req -new -sha256 \
    -key SERVER.key \
    -subj "/C=US/ST=North Carolina/O=ORG/OU=ORG_UNIT/CN=YOUR_DOMAIN_NAME" \
    -reqexts SAN \
    -config <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:YOUR_DOMAIN_NAME")) \
    -out SERVER.csr

and for signing

$ 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

based on the discussions above.

It finally results in a secure connection with Chrome.
Picture1

Hope it helps people who come here for classes or server SSL setup!

this works.thanks.saved lot of time

@pistacchio
Copy link

I followed all the steps and configured nginx to use the generated certificate:

    ssl_certificate         /root/mydomain.com.crt;
    ssl_certificate_key     /root/mydomain.com.key;
    ssl_client_certificate  /root/rootCA.crt;

    ssl_verify_client on;
    ssl_verify_depth 2;

however, when trying it with curl it doens't work: curl --cacert rootCA.crt https://localhost:9090

Any idea?

@boypt
Copy link

boypt commented Dec 2, 2019

some may require the certificate contain extendedKeyUsage=serverAuth, the sign command needs some more attributes:

openssl x509 -req \
        -extfile <(printf "[v3_req]\nextendedKeyUsage=serverAuth\nsubjectAltName=DNS:*.mydomain.best,DNS:*.mydomian.xxx") \
        -extensions v3_req \
        -days 365 -in mydomain.com.csr -CA rootCA.crt -CAkey rootCA.key \
        -CAcreateserial -out mydomain.com.csr -sha256

@boypt
Copy link

boypt commented Dec 2, 2019

Apple require all TLS server certificates issued after July 1, 2019 "contain an ExtendedKeyUsage (EKU) extension containing the id-kp-serverAuth OID."

https://support.apple.com/en-us/HT210176

@ganeshkamath89
Copy link

How to pass subjectAltName parameter on windows?

@boumanb
Copy link

boumanb commented Dec 23, 2019

Thanks! 😄

@chainhead
Copy link

Thanks for the gist!

I couldn't get the instructions in this gist working on a Mac. When using the generated certificate elsewhere, I would get this error message - Error getting keypair for CA issuer: certificate is not a CA. After some research, I found that,

It turns out that on MacOS the default OpenSSL config does not include the configuration for v3_ca certificate generation.

For more details, see Error getting keypair for CA issuer: certificate is not a CA.

I have extracted the relevant portion and pasted here.

##  - Create new root CA key and certificate
openssl genrsa -out rootCA.key 4096
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -subj "/CN=rootCA.crt" \ 
-reqexts v3_req -extensions v3_ca \ 
-out rootCA.crt -config crt.conf

The crt.conf is defined as follows. Note the [ v3_ca ] section and basicConstraints = critical,CA:TRUE.

[ v3_ca ]
basicConstraints = critical,CA:TRUE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = org

@faizafaiza02
Copy link

Hi,
I am trying the above steps to generate a certificate for PWA(Ionic/Angular) intranet sites in IIS(Windows). There is no specific domain. So I tried using the Computer name instead of domain.com but for some reason the certificate doesn't get recognized in the IIS. Can someone please help in making the below steps run in Windows PowerShell:

Method 2 Create the signing (csr)
$ openssl req -new -sha256
-key SERVER.key
-subj "/C=US/ST=North Carolina/O=ORG/OU=ORG_UNIT/CN=YOUR_DOMAIN_NAME"
-reqexts SAN
-config <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:YOUR_DOMAIN_NAME"))
-out SERVER.csr

Method 2 Generate the certificate using the mydomain csr and key along with the CA Root key
$ 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

@q2dg
Copy link

q2dg commented Mar 31, 2020

Whats the difference between these two lines:

openssl x509 -req -in localhost.csr -CA root-CA.crt -CAkey root-CA.pem -CAcreateserial -out localhost.crt -days 365 -sha256

AND

openssl x509 -req -in localhost.csr -signkey root-CA.pem -out localhost.crt -days 365 -sha256

Are these commands are same? I have checked the output from these... Output were different but I guess both are used for signing CSR with Root CA... but whats the difference?

I've spent hours searching for a response to this exactly same question but finally I've given up :_/

@trevor403
Copy link

thank you this was super helpful!

@faktorqm
Copy link

Thanks a lot for all! I was able to make it "secure" on Chrome.

@chrismacp
Copy link

The commands I used for adding SAN to the request and certificate are

$ openssl req -new -sha256 \
    -key SERVER.key \
    -subj "/C=US/ST=North Carolina/O=ORG/OU=ORG_UNIT/CN=YOUR_DOMAIN_NAME" \
    -reqexts SAN \
    -config <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:YOUR_DOMAIN_NAME")) \
    -out SERVER.csr

and for signing

$ 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

based on the discussions above.

It finally results in a secure connection with Chrome.
Picture1

Hope it helps people who come here for classes or server SSL setup! 😄

Thanks, the initial instructions didn't work properly, the SAN was missing from the final signed certificate.

@notariuss
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

@perseon
Copy link

perseon commented Jun 2, 2020

I had to amend the last command because on ubuntu the certificate was missing the SAN section.
what i did was to create ans supply a config file
openssl x509 -req -in 192.168.10.100.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out 192.168.10.100.crt -days 3650 -sha256 -extfile certificate.conf -extensions req_ext

and the config file contains:

[req]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
[dn]
C = RO
ST = Bucharest
L = Bucharest
O = ACME, INC
OU = OU
emailAddress = [email protected]
CN = address.local
[req_ext]
subjectAltName = @alt_names
[alt_names]
DNS.1 = address.local
IP.1 = 192.168.10.100

@AlexeyNovikov
Copy link

A small note. If you want to use this in your local network you have to add IP addresses your server runs on as well as localhost to subjectAltName, e.g.:

[req_ext]
subjectAltName = @alt_names
[alt_names]
DNS.1 = development.personal #real domain
DNS.2 = localhost
IP.1 = 127.0.0.1 # ip for localhost
IP.2 = 192.168.10.10 # local network address
IP.3 = 94.181.174.85 # external address

@ykfq
Copy link

ykfq commented Jul 16, 2020

openssl genrsa -out apiserver.key 2048
openssl req -new -key apiserver.key -out apiserver.csr -subj "/CN=kube-apiserver"
openssl x509 -req -in apiserver.csr -CA /etc/kubernetes/ssl/ca.crt -CAkey /etc/kubernetes/ssl/ca.key \
             -CAcreateserial -out apiserver.crt -days 3650 -extensions SAN \
             -extfile <(printf "[SAN]\nsubjectAltName=DNS:localhost,DNS:kubernetes,DNS:kubernetes.default,DNS:kubernetes.default.svc,DNS:kubernetes.default.svc.cluster.local,DNS:lb-apiserver.kubernetes.local,IP:127.0.0.1,IP:192.168.10.21,IP:192.168.10.21")

@edbshubham
Copy link

The security team is just providing the rootCA.crt but not rootCA.key, is their any way to sign the server level certificate using the only rootCA.crt?

@ppandrangi
Copy link

@edbshubham Usually security teams won't share rootCA private key. Please send the CSR to your security team and ask them to sign it In response you should receive the signed cert and the rootCA.crt.

@jimmychu0807
Copy link

This is very convenience. Thank you!

@cgm-aw
Copy link

cgm-aw commented Nov 4, 2020

The problem mentioned by @chainhead also appears in Java when you use the generated root CA certificate in your truststore. You will see this exception:

javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

You must make sure that you use the v3_ca extension when creating the root CA:
openssl req -x509 -new -nodes -extensions v3_ca -key rootCA.key -sha256 -days 1024 -out rootCA.crt

Or you just disable this check with a VM parameter: -Djdk.security.allowNonCaAnchor=true
Of course this is not recommended :)

Otherwise an extremely helpful overview, thanks!

@minhphungbk03
Copy link

I followed all the steps and configured nginx to use the generated certificate:

    ssl_certificate         /root/mydomain.com.crt;
    ssl_certificate_key     /root/mydomain.com.key;
    ssl_client_certificate  /root/rootCA.crt;

    ssl_verify_client on;
    ssl_verify_depth 2;

however, when trying it with curl it doens't work: curl --cacert rootCA.crt https://localhost:9090

Any idea?

Hi, I face with the same issue. Does anyone has solution yet? Thanks!
https://gist.github.com/fntlnz/cf14feb5a46b2eda428e000157447309#gistcomment-3095363

@EmmanuelKasper
Copy link

@fntlnz
not sure how long you want to maintain this gist, but it lacks the command where the CA is signing a CSR containing at subjectAltName entry. You're documenting how generate a CSR containing a subjectAltName, but not how the CA should sign it :)
It should be
openssl x509 -req -extfile <(printf "subjectAltName=DNS:mydomain.com") -days 120 -in mydomain.com.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out mydomain.com.crt -sha256

as pointed out by @chrismacp

@pplmx
Copy link

pplmx commented Sep 17, 2021

Help: how to create a authentication tls in golang

Could anyone give me some help? Thanks a lot.

Error info

I can create root CA and the server CA successfully.
However, I cannot use localhost.key.pem and localhost.crt.pem to create a TLS connection in golang 1.17.
Client always failed to connect to the server, which threw the following

2021/09/17 14:27:21 x509: certificate signed by unknown authority
exit status 1

Server start

# server can be started normally
go run server.go 12345
# client cannot connect to the server.
go run client.go localhost:12345

CA creation

Here are my reproduced steps:

openssl req -x509 -nodes -sha256 \
	-newkey rsa:4096 \
	-days 10240 \
	-subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg, Inc./OU=Software Dept/CN=localhost" \
	-keyout root.key.pem \
	-out root.crt.pem

# OPENSSL_CONF="/etc/ssl/openssl.cnf"
OPENSSL_CONF="/System/Library/OpenSSL/openssl.cnf"
openssl req -new -nodes \
    -newkey rsa:4096 \
    -subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg, Inc./OU=Software Dept/CN=localhost" \
	-reqexts SAN \
    -config <(cat "${OPENSSL_CONF}" \
        <(printf "\n[SAN]\nsubjectAltName=DNS:localhost")) \
    -keyout localhost.key.pem \
    -out localhost.csr

openssl x509 -req -sha256 -CAcreateserial -days 365 \
	-CA root.crt.pem \
	-CAkey root.key.pem \
	-extfile <(printf "subjectAltName=DNS:localhost") \
	-in localhost.csr \
	-out localhost.crt.pem

Golang code

Here is my golang code.
Server

package main

import (
    "bufio"
    "crypto/rand"
    "crypto/tls"
    "fmt"
    "log"
    "net"
    "os"
    "strconv"
    "strings"
    "time"
)

var count = 0
var ServerCertFile = "../certificate/localhost.crt.pem"
var ServerKeyFile = "../certificate/localhost.key.pem"

func handleConnection(c net.Conn) {
    fmt.Println("Connection from ", c.RemoteAddr().String())
    for {
        netData, err := bufio.NewReader(c).ReadString('\n')
        if err != nil {
            fmt.Println(err)
            return
        }

        temp := strings.TrimSpace(netData)
        if temp == "STOP" {
            break
        }
        fmt.Println(temp)
        counter := strconv.Itoa(count) + "\n"
        c.Write([]byte(counter))
    }
    defer func(c net.Conn) {
        err := c.Close()
        if err != nil {
            fmt.Println("Failed to Close Connection from ", c.RemoteAddr().String())
        }
    }(c)
}

func main() {

    arguments := os.Args
    if len(arguments) == 1 {
        fmt.Println("Please provide a port number!")
        return
    }
    PORT := ":" + arguments[1]

    // load certificate
    crt, err := tls.LoadX509KeyPair(ServerCertFile, ServerKeyFile)
    if err != nil {
        log.Fatalln(err.Error())
        return
    }
    tlsConfig := &tls.Config{}
    tlsConfig.Certificates = []tls.Certificate{crt}
    tlsConfig.Time = time.Now
    tlsConfig.Rand = rand.Reader

    l, err := tls.Listen("tcp", PORT, tlsConfig)
    if err != nil {
        log.Fatalln(err.Error())
        return
    }
    defer l.Close()

    for {
        conn, err := l.Accept()
        if err != nil {
            fmt.Println(err.Error())
            continue
        }
        go handleConnection(conn)
        count++
    }
}

Client

package main

import (
    "bufio"
    "crypto/tls"
    "fmt"
    "log"
    "os"
    "strings"
)

func main() {
    arguments := os.Args
    if len(arguments) == 1 {
        fmt.Println("Please provide host:port.")
        return
    }

    CONNECT := arguments[1]
    c, err := tls.Dial("tcp", CONNECT, nil)
    if err != nil {
        log.Fatalln(err.Error())
        return
    }

    for {
        reader := bufio.NewReader(os.Stdin)
        fmt.Print(">> ")
        text, _ := reader.ReadString('\n')
        fmt.Fprintf(c, text+"\n")

        message, _ := bufio.NewReader(c).ReadString('\n')
        fmt.Print("->: " + message)
        if strings.TrimSpace(string(text)) == "STOP" {
            fmt.Println("TCP client exiting...")
            return
        }
    }
}

@cspiel1
Copy link

cspiel1 commented Nov 4, 2022

Option -copy_extensions copy is missing for openssl x509 -req command because of the example with the Subject alternative name.

@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

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