Howto create a Root CA certificate and sign your own web certificates - jbilander/HowTos GitHub Wiki

Howto create a Root CA certificate and sign your own (localhost) web certificates

Using OpenSSL:

  1. Create the private key for your Root CA:

jbilander@apollo:~$ sudo -s
root@apollo:~#
root@apollo:~# cd /etc/ssl/private/
root@apollo:/etc/ssl/private# openssl genrsa -des3 -out myCA.key 2048

Generating RSA private key, 2048 bit long modulus (2 primes)
......+++++
............+++++
e is 65537 (0x010001)
Enter pass phrase for myCA.key:
Verifying - Enter pass phrase for myCA.key:

root@apollo:/etc/ssl/private#
root@apollo:/etc/ssl/private# ls -al
total 10
drwx--x--- 2 root ssl-cert    4 Dec 27 10:10 .
drwxr-xr-x 4 root root        5 Nov  2 20:27 ..
-rw------- 1 root root     1743 Dec 27 10:11 myCA.key
-rw-r----- 1 root ssl-cert 1708 Nov 28  2018 ssl-cert-snakeoil.key
  1. Generate a root certificate:

root@apollo:/etc/ssl/private# openssl req -x509 -new -nodes -key myCA.key -sha256 -days 7000 -out ../certs/myCA.pem

Enter pass phrase for myCA.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:SE
State or Province Name (full name) [Some-State]:Stockholm
Locality Name (eg, city) []:Stockholm
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Local Ltd.
Organizational Unit Name (eg, section) []:Local Security Division
Common Name (e.g. server FQDN or YOUR name) []:Local Root CA
Email Address []:noreply@localhost
root@apollo:/etc/ssl/private#
  1. Verify the newly created root certificate looks ok:

root@apollo:/etc/ssl/private# openssl x509 -in ../certs/myCA.pem -text

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            3c:2c:c8:d4:c9:78:ac:91:e5:da:28:1f:1a:1b:58:1b:ff:8e:85:ab
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = SE, ST = Stockholm, L = Stockholm, O = Local Ltd., OU = Local Security Division, CN = Local Root CA, emailAddress = noreply@localhost
        Validity
            Not Before: Dec 27 09:30:26 2022 GMT
            Not After : Feb 25 09:30:26 2042 GMT
        Subject: C = SE, ST = Stockholm, L = Stockholm, O = Local Ltd., OU = Local Security Division, CN = Local Root CA, emailAddress = noreply@localhost
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:cc:4e:a7:d8:68:24:7e:66:c2:25:9e:8f:44:a7:
                    2c:03:47:8e:e5:9d:0d:bb:c9:7c:18:bc:09:fb:b6:
                    ...
                    ...

Congratulations, you’re now a CA.

Now we look at creating the certificate to be used with our website

  1. First create a configuration file to be used for our CSR (Certificate Signing Request) Use Vim or Nano editor to create the file certificate.cfg, change the domain-names and IPs to whatever you want there. It should look similar to this when you are done:

root@apollo:/etc/ssl/private# more certificate.cfg

[req]
req_extensions     = req_ext
distinguished_name = req_distinguished_name
prompt             = no

[req_distinguished_name]
commonName=mydomainexample.org

[req_ext]
subjectAltName   = @alt_names

[alt_names]
DNS.1  = localhost
DNS.2  = mydomainexample.org
DNS.3  = *.mydomainexample.org
IP.1   = 127.0.0.1
IP.2   = 192.168.1.19
IP.3   = 192.168.1.20

It should now look like this in your folder:

root@apollo:/etc/ssl/private# ls -al
total 23
drwx--x--- 2 root ssl-cert    5 Dec 28 10:04 .
drwxr-xr-x 4 root root        5 Nov  2 20:27 ..
-rw-r--r-- 1 root root      356 Dec 28 10:04 certificate.cfg
-rw------- 1 root root     1743 Dec 27 10:11 myCA.key
-rw-r----- 1 root ssl-cert 1708 Nov 28  2018 ssl-cert-snakeoil.key
root@apollo:/etc/ssl/private# 
  1. Now create the certificate key:

root@apollo:/etc/ssl/private# openssl genrsa -out certificate.key 2048

or

root@apollo:/etc/ssl/private# openssl genrsa -des3 -out certificate.key 2048

note: use -des3 flag if you want to encrypt the key, it's the recommended way to keep your key safe but can make config more challenging to get working since the pass-phrase must be handled by the server using the cert and key.

root@apollo:/etc/ssl/private# openssl genrsa -out certificate.key 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
..............+++++
................................+++++
e is 65537 (0x010001)
root@apollo:/etc/ssl/private#

It should now look like this in your folder:

root@apollo:/etc/ssl/private# ls -al
total 27
drwx--x--- 2 root ssl-cert    6 Dec 28 10:27 .
drwxr-xr-x 4 root root        5 Nov  2 20:27 ..
-rw-r--r-- 1 root root      356 Dec 28 10:04 certificate.cfg
-rw------- 1 root root     1675 Dec 28 10:27 certificate.key
-rw------- 1 root root     1743 Dec 27 10:11 myCA.key
-rw-r----- 1 root ssl-cert 1708 Nov 28  2018 ssl-cert-snakeoil.key
root@apollo:/etc/ssl/private#
  1. Now create the CSR using the certificate.cfg

root@apollo:/etc/ssl/private# openssl req -config certificate.cfg -extensions req_ext -key certificate.key -new -out certificate.csr

It should now look something like this if you take a look at the csr-file:

root@apollo:/etc/ssl/private# more certificate.csr

-----BEGIN CERTIFICATE REQUEST-----
MIICyDCCAbACAQAwHjEcMBoGA1UEAwwTbXlkb21haW5leGFtcGxlLm9yZzCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOFGOHnl0j5CQyAcM/WIQzAPpT4H
5dZYaipx9+jdjly4CiuCdW1a1rr0PA4LHNHC3dZMWudObMMQLSiwAU+zl3qyafct
yb/ol04R/TKk+wJBAKQM0riwgWJiBneNBDWOMCwMOIBsFe94yy38QHlvxzh1mC86
JQAwsjEQN4GyViA6kuoebwFLBuvUnY3aEiqp6bCcG4ipxjzZxhTbuunn43fEB691
XB3FriFLHbKVBGZLdbUMciyCus3Ogxizk7U+60bapfYNRXj/wWuDlXSrqLZsT53L
cFjMI2J4vcsxZLSNIPlI6AyENAwS7Df32g0n6GIMwbmwdQVF0DDAhBgtdRcCAwEA
AaBlMGMGCSqGSIb3DQEJDjFWMFQwUgYDVR0RBEswSYIJbG9jYWxob3N0ghNteWRv
bWFpbmV4YW1wbGUub3JnghUqLm15ZG9tYWluZXhhbXBsZS5vcmeHBH8AAAGHBMCo
AROHBMCoARQwDQYJKoZIhvcNAQELBQADggEBAI2/M1XcgGx3JxWCLno+O3GqnJhw
VYG1p7i7f/TYRsDS6SHtLeBx0O4PWEDa045eDTua4kRWqa3pmbFgWRRb0XOiGVfL
tnKg/bY1vKOaBovPueQL0q2UmfQsomQXrZ7WX3hymKAo9urS8YLpvL7wxpi2rhmg
KOYZYE9C8/aAMmLomfqarS5cW4OP+nXyIVAwUpKtNuj5NgIe1ClkEDCjjyP6ICNJ
US70wZqlifEq+n+PH+VBJDeYs6JoI3+cMoCR4aGcxPhjp90vhcPmKMI8ujR+0das
PyEXIWidvj6bO7vxlowzun7f4RF9t11iSRLvj9ITW/Oom3srWMQf3SY+Jl4=
-----END CERTIFICATE REQUEST-----

It should now look like this in your folder:

root@apollo:/etc/ssl/private# ls -al
total 32
drwx--x--- 2 root ssl-cert    7 Dec 28 10:37 .
drwxr-xr-x 4 root root        5 Nov  2 20:27 ..
-rw-r--r-- 1 root root      356 Dec 28 10:04 certificate.cfg
-rw-r--r-- 1 root root     1041 Dec 28 10:36 certificate.csr
-rw------- 1 root root     1675 Dec 28 10:27 certificate.key
-rw------- 1 root root     1743 Dec 27 10:11 myCA.key
-rw-r----- 1 root ssl-cert 1708 Nov 28  2018 ssl-cert-snakeoil.key
root@apollo:/etc/ssl/private#
  1. Now create a configuration file called custom_openssl.cnf that we will use when signing the CSR. Use Vim or Nano editor and put the content below in it... (change domain-names and IPs to whatever you want here):

root@apollo:/etc/ssl/private# more custom_openssl.cnf

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

[alt_names]
DNS.1  = localhost
DNS.2  = mydomainexample.org
DNS.3  = *.mydomainexample.org
IP.1   = 127.0.0.1
IP.2   = 192.168.1.19
IP.3   = 192.168.1.20
  1. Now Sign the CSR with your Root CA cert:

root@apollo:/etc/ssl/private# openssl x509 -req -CA ../certs/myCA.pem -CAkey myCA.key -in certificate.csr -out certificate.crt -days 730 -CAcreateserial -extfile custom_openssl.cnf

Signature ok
subject=CN = mydomainexample.org
Getting CA Private Key
Enter pass phrase for myCA.key:
root@apollo:/etc/ssl/private#

Now take a look at the certificate we created and signed, and verify that it contains the X509v3 extensions with the Subject Alternative Names:

root@apollo:/etc/ssl/private# openssl x509 -in certificate.crt -text -noout

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            74:05:65:92:2b:1b:65:67:93:8b:c2:f7:9b:dd:67:5f:0f:e5:57:1e
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = SE, ST = Stockholm, L = Stockholm, O = Local Ltd., OU = Local Security Division, CN = Local Root CA, emailAddress = noreply@localhost
        Validity
            Not Before: Dec 28 10:42:32 2022 GMT
            Not After : Dec 27 10:42:32 2024 GMT
        Subject: CN = mydomainexample.org
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:e1:46:38:79:e5:d2:3e:42:43:20:1c:33:f5:88:
                    ...
                    ...
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Authority Key Identifier:
                keyid:95:E4:62:D9:45:D0:4C:82:75:D5:E5:B8:40:B6:48:86:97:1A:E9:AF

            X509v3 Basic Constraints:
                CA:FALSE
            X509v3 Key Usage:
                Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment
            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Subject Alternative Name:
                DNS:localhost, DNS:mydomainexample.org, DNS:*.mydomainexample.org, IP Address:127.0.0.1, IP Address:192.168.1.19, IP Address:192.168.1.20
    Signature Algorithm: sha256WithRSAEncryption
         4f:26:88:99:b8:f7:37:c9:78:40:3e:28:43:9f:01:b0:62:9d:
         ...
         ...

It should now look like this in your folder:

root@apollo:/etc/ssl/private# ls -al
total 41
drwx--x--- 2 root ssl-cert    9 Dec 28 11:41 .
drwxr-xr-x 4 root root        5 Nov  2 20:27 ..
-rw-r--r-- 1 root root      356 Dec 28 10:04 certificate.cfg
-rw-r--r-- 1 root root     1452 Dec 28 11:42 certificate.crt
-rw-r--r-- 1 root root     1041 Dec 28 10:36 certificate.csr
-rw------- 1 root root     1675 Dec 28 10:27 certificate.key
-rw-r--r-- 1 root root      368 Dec 28 11:41 custom_openssl.cnf
-rw------- 1 root root     1743 Dec 27 10:11 myCA.key
-rw-r----- 1 root ssl-cert 1708 Nov 28  2018 ssl-cert-snakeoil.key

Certificate and its corresponding key ready to be used, let's copy them over together with the Root CA cert and try it in a Windows machine. Make a folder certs in your home folder so we can use scp to copy them with the WinSCP client:

root@apollo:/etc/ssl/private# mkdir /home/jbilander/certs
root@apollo:/etc/ssl/private# cp certificate.crt certificate.key ../certs/myCA.pem /home/jbilander/certs/
root@apollo:/etc/ssl/private# cd /home/jbilander/certs/
root@apollo:~/certs# ls -al
total 23
drwxr-xr-x  2 root      root         5 Dec 28 11:48 .
drwxr-xr-x 16 jbilander jbilander   46 Dec 28 11:48 ..
-rw-r--r--  1 root      root      1452 Dec 28 11:48 certificate.crt
-rw-------  1 root      root      1675 Dec 28 11:48 certificate.key
-rw-r--r--  1 root      root      1513 Dec 28 11:48 myCA.pem

Make sure you set the correct owner of the files so that we are allowed to copy them.

root@apollo:~/certs# chown jbilander:jbilander *
root@apollo:~/certs# ls -al
total 23
drwxr-xr-x  2 root      root         5 Dec 28 11:48 .
drwxr-xr-x 16 jbilander jbilander   46 Dec 28 11:48 ..
-rw-r--r--  1 jbilander jbilander 1452 Dec 28 11:48 certificate.crt
-rw-------  1 jbilander jbilander 1675 Dec 28 11:48 certificate.key
-rw-r--r--  1 jbilander jbilander 1513 Dec 28 11:48 myCA.pem
root@apollo:~/certs#

Using WinSCP to copy the files to a Windows machine (requires SSH-server to be running on the linux box)




Install the Root Certificate in your browser, e.g. for firefox:

Procedure:

   Open the browser.
   Click the Open menu button in the upper right corner.
   Choose Options.
   Open Privacy & Security tab.
   Scroll down till Certificates section.
   Click View Certificates… button.
   In Certificate Manager window click on Import… ...
   Navigate to the folder where the downloaded myCA.pem certificate is stored.

import_root_ca_to_firefox

Add Root CA cert to Windows using certmgr.msc:

install_trusted_root_ca_step1

install_trusted_root_ca_step2

install_trusted_root_ca_step3

install_trusted_root_ca_step4

install_trusted_root_ca_step5

install_trusted_root_ca_step6

You can also add the Root CA cert to your Python installation (this requires that you have copied the certs and key to this folder first):

(DjangoEnv) C:\Users\Jorgen\Projects\Django2022.3\first_project>python
Python 3.6.13 |Anaconda, Inc.| (default, Mar 16 2021, 11:37:27) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import certifi
>>> certifi.where()
'C:\\Users\\Jorgen\\AppData\\Local\\conda\\conda\\envs\\DjangoEnv\\lib\\site-packages\\certifi\\cacert.pem'
>>> exit()

(DjangoEnv) C:\Users\Jorgen\Projects\Django2022.3\first_project>type myCA.pem >> c:\Users\Jorgen\AppData\Local\conda\conda\envs\DjangoEnv\Lib\site-packages\certifi\cacert.pem

You can now start for example Django dev server by using the certificate and key this way:

(DjangoEnv) C:\Users\Jorgen\Projects\Django2022.3\first_project>python manage.py runserver_plus localhost:8443 --cert-file certificate.crt --key-file certificate.key

Django version 3.2.16, using settings 'first_project.settings'
Development server is running at https://localhost:8443/
Using the Werkzeug debugger (http://werkzeug.pocoo.org/)
Quit the server with CTRL-BREAK.
 * Debugger is active!

 * Debugger PIN: 441-272-188

 * Running on https://localhost:8443/ (Press CTRL+C to quit)

127.0.0.1 - - [28/Dec/2022 11:53:34] "GET / HTTP/1.1" 200 -

127.0.0.1 - - [28/Dec/2022 11:53:34] "GET /static/admin/css/fonts.css HTTP/1.1" 200 -

127.0.0.1 - - [28/Dec/2022 11:53:34] "GET /static/admin/fonts/Roboto-Bold-webfont.woff HTTP/1.1" 200 -

127.0.0.1 - - [28/Dec/2022 11:53:34] "GET /static/admin/fonts/Roboto-Light-webfont.woff HTTP/1.1" 200 -

127.0.0.1 - - [28/Dec/2022 11:53:34] "GET /static/admin/fonts/Roboto-Regular-webfont.woff HTTP/1.1" 200 -

This above requires some configuration first (see here: https://stackoverflow.com/a/28933593 )

Now we can see that https is working in Firefox without any warnings:

django_https

And if we look at the certificate info we can see the Subject Alt Names is in there:

firefox_certificate_info

That's all folks, Happy Hackin'

⚠️ **GitHub.com Fallback** ⚠️