Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
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 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

openssl req -new -key -out

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 -subj "/C=US/ST=CA/O=MyOrg, Inc./" -out

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 \
    -subj "/C=US/ST=CA/O=MyOrg, Inc./" \
    -reqexts SAN \
    -config <(cat /etc/ssl/openssl.cnf \
        <(printf "\n[SAN]\,")) \

Verify the csr's content

openssl req -in -noout -text

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

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

Verify the certificate's content

openssl x509 -in -text -noout
Copy link

boumanb commented Dec 23, 2019

Thanks! 😄

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_extensions = v3_req
distinguished_name = req_distinguished_name
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
DNS.1 = org

Copy link

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

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


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 :_/

Copy link

thank you this was super helpful!

Copy link

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

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.

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.

Copy link

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

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

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 -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out -days 3650 -sha256 -extfile certificate.conf -extensions req_ext

and the config file contains:

default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
C = RO
ST = Bucharest
L = Bucharest
emailAddress =
CN = address.local
subjectAltName = @alt_names
DNS.1 = address.local
IP.1 =

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.:

subjectAltName = @alt_names
DNS.1 = development.personal #real domain
DNS.2 = localhost
IP.1 = # ip for localhost
IP.2 = # local network address
IP.3 = # external address

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:,IP:,IP:")

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?

Copy link

xB-2048 commented Sep 21, 2020

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

Copy link

This is very convenience. Thank you!

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: PKIX path building failed: 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:
Of course this is not recommended :)

Otherwise an extremely helpful overview, thanks!

Copy link

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

    ssl_certificate         /root/;
    ssl_certificate_key     /root/;
    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!

Copy link

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 "") -days 120 -in -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out -sha256

as pointed out by @chrismacp

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 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.

package main

import (

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 {

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

func main() {

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

    // load certificate
    crt, err := tls.LoadX509KeyPair(ServerCertFile, ServerKeyFile)
    if err != nil {
    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 {
    defer l.Close()

    for {
        conn, err := l.Accept()
        if err != nil {
        go handleConnection(conn)


package main

import (

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

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

    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...")

Copy link

pplmx commented Sep 17, 2021

Help: how to create a authentication tls in golang

Fixed it.

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.

Copy link

shreeve commented Dec 17, 2022


# =============================================================================
# - Self signing SSL certificates
# Author: Steve Shreeve <>
#   Date: Dec 17, 2022
# =============================================================================

# Use
# See

# variables
name="ACME, Inc."
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:,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:,IP:${myip}
    keyUsage = critical, digitalSignature, keyEncipherment
    extendedKeyUsage = serverAuth
    basicConstraints = CA:FALSE
    authorityKeyIdentifier = keyid:always
    subjectKeyIdentifier = none

# 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 "--";

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