Skip to content

Instantly share code, notes, and snippets.

Embed
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 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
@abdkaviani
Copy link

abdkaviani commented Aug 25, 2019

Hi

I'm looking for the way that use SSL in my local network (Apache & Windows server with IP adress like 192.168.10.10) and I don't like that browser display "your connection is not secure" or it works after "Add Exception" option.
I read many articles & I couldn't solve this problem.
I want a real secure ssl in my localhost
Can you help me?
Thanks a lot

@gaochundong
Copy link

gaochundong commented Aug 30, 2019

Thanks.

@bkrahmer
Copy link

bkrahmer commented Sep 4, 2019

I'm looking for the way that use SSL in my local network (Apache & Windows
server with IP adress like 192.168.10.10) and I don't like that browser display
"your connection is not secure" or it works after "Add Exception" option.
I read many articles & I couldn't solve this problem.
I want a real secure ssl in my localhost

@abdkaviani: This article is about creating a self-signed certificate. If you want to avoid the security warnings, the certificate has to have a chain all the way back to a trusted authority. You'll have to use one of the commercial services to get a certificate like that.

@garydmx29
Copy link

garydmx29 commented Sep 16, 2019

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

itsnuwan commented Oct 23, 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!

this works.thanks.saved lot of time

@pistacchio
Copy link

pistacchio commented Nov 28, 2019

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

ganeshkamath89 commented Dec 12, 2019

How to pass subjectAltName parameter on windows?

@boumanb
Copy link

boumanb commented Dec 23, 2019

Thanks! 😄

@chainhead
Copy link

chainhead commented Dec 28, 2019

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

faizafaiza02 commented Feb 22, 2020

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

trevor403 commented Mar 31, 2020

thank you this was super helpful!

@faktorqm
Copy link

faktorqm commented Apr 23, 2020

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

@chrismacp
Copy link

chrismacp commented May 6, 2020

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

notariuss commented May 24, 2020

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 = some@email.com
CN = address.local
[req_ext]
subjectAltName = @alt_names
[alt_names]
DNS.1 = address.local
IP.1 = 192.168.10.100

@AlexeyNovikov
Copy link

AlexeyNovikov commented Jul 4, 2020

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

edbshubham commented Sep 6, 2020

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?

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

@jimmychu0807
Copy link

jimmychu0807 commented Nov 3, 2020

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

minhphungbk03 commented Mar 24, 2021

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

EmmanuelKasper commented Sep 1, 2021

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

@pplmx
Copy link

pplmx commented Sep 17, 2021

Help: how to create a authentication tls in golang

Fixed it.
FYI.
https://awesomeopensource.com/project/jcbsmpsn/golang-https-example
image

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