Trusted self-signed TLS certificates for dummies (w/ thorough explanations included)
Tags: security, https, ssl, beginners
Published: 2022-11-25 12:57:30

> *upd 1/20/2023: Added FreeBSD steps, CA certificate SAN, and fixed shell commands formatting.*

> *upd 1/23/2023: Added Android 13 steps.*

> *upd 1/25/2023: Added openssl v1.1.1 fix for the step `5.2` in `🪄 How to generate a self-signed cert`. Removed FreeBSD instructions for installing openssl v3.*

> *This is a translation of my article, initially written in a different language. Please, let me know about language-related mistakes if you notice any.*

>*Also, I'm a student and not a professional with a rich knowledge base, though that's my goal. Let me know if I misunderstood something and consequently shared misinformation here.* 

---

> ## WARNING! NONE OF THE PERFORMED CONFIGURATIONS CONSIDER SPECIFIC PROJECTS' SECURITY REQUIREMENTS. USE THIS ARTICLE AS A SECONDARY SOURCE OF KNOWLEDGE ONLY AND HARDEN YOUR SYSTEMS AS APPROPRIATE.

## Contents
- [🧑‍🎓 A bit of theory](#a-bit-of-theory)
    - [🏛️ Certificate authority](#certificate-authority)
    - [🔏 Digital certificate](#digital-certificate)
    - [✒️ Digital signature](#digital-signature)
- [🧐 When you might need this](#when-you-might-need-this)
- [🔍 How HTTPS works (abstract)](#how-https-works-abstract)
- [📜 What you will need to generate a self-signed cert](#what-you-will-need-to-generate-a-selfsigned-cert)
- [🪄 How to generate a self-signed cert](#how-to-generate-a-selfsigned-cert)
- [🔫 How to force devices to trust our very own CA](#how-to-force-devices-to-trust-our-very-own-ca)
    - [Windows](#windows)
    - [Linux](#linux)
    - [FreeBSD](#freebsd)
    - [Android](#android)
    - [iOS](#ios)
    - [Some additional configuration required by Firefox](#some-additional-configuration-required-by-firefox)
- [🖥️ The result](#the-result)
- [📖 Additional sources of useful information](#additional-sources-of-useful-information)

> This article is my slight extension to the [How to create a valid self signed SSL Certificate?](https://youtu.be/VH4gXcvkmOY) video by Christian Lempa—I love this channel <3—that covers some topics more in-depth. The video turned out to be pretty helpful for me during the integration of HTTPS into my HomeLab services. Now I have trusted TLS certs yay!

> It's barely feasible to do anything without understanding what exactly is happening under the hood for me. I always try to dig deeper to get a grasp of truth—how things work. That's why I wrote this article. The goals are: firstly, to learn more about HTTPS and secondly, to help people that are just like me.

## 🧑‍🎓 A bit of theory
### 🏛️ Certificate Authority
[**Certificate Authority (CA)**](https://en.wikipedia.org/wiki/Certificate_authority) is an entity/party responsible for generating, storing, and issuing digital certificates. There are many CAs: governmental, commercial, non-profit, and private.

Almost every device stores trusted Certificate Authorities' public keys and digital certificates: a smartphone, a PC, or a single-board computer for IoT. If a device supports connecting to a network by design, its operating system has these artifacts. This approach allows us to verify network resources' identities and safely connect to them.

### 🔏 Digital certificate
It is an electronic document used to validate the identity of a public key. It contains info about the key itself, the subject (owner) of this key, and a digital signature of an entity/party that approved the certificate.

### ✒️ Digital signature
An electronic signature type. It's a mathematical algorithm used for validating data authenticity and integrity. A digital signature creates a unique fingerprint for an entity.

Abstractly,  digital signature creation and usage process may be performed like so:

Party *A* has a pair of keys—a public and a private one. This party wants to transfer a document *DOC* to party *B*. It also wants *B* to be able to validate the *DOC* and check its integrity.

To achieve this, *A* applies a hash function to *DOC* to get a digest—an alphanumeric value unique to this document. A digest may be of any fixed size depending on the hash algorithm. When the digest is calculated, the party encrypts it using its private key. That's how a digital signature *SIG* is created.

When *B* receives *DOC* from *A*, it also gets *SIG* and *A*'s public key *PUB*. Just like *A,* *B* applies a hash function to *DOC.* But after that, it uses *A*'s public key *PUB* to decrypt the digest. The next step is to compare the calculated digest to the decrypted one.

If digests are equal, the document wasn't altered during transmission.

## 🧐 When you might need this
Before we start doing anything, we always ask ourselves: but why do I need to do this?

There are many situations when a self-signed certificate might be needed/preferred. For example, it's common for an enterprise network to have a couple or a hundred **internal** services. I intentionally highlighted the word "internal" since using self-signed certs for public services is a security-violating habit. By doing so, you bring danger to your users, at least. As another example, such certificates may be used for your HomeLab services—just like in my case—or other cases when a non-public domain is utilized.

## 🔍 How HTTPS works (abstract)
A PKI or [Public Key Infrastructure](https://en.wikipedia.org/wiki/Public_key_infrastructure) is the foundation of HTTPS.

Sure, we could encrypt all network traffic with asymmetric algorithms. But it would be too slow and ineffective. Both time and computing resources spent on encryption and decryption increase as the amount of data grows, as you might already know. Symmetric encryption is used to avoid this, and both parties have session keys for it.

> But here's another problem: **how to transfer session keys in such a way that they could not be accessed by a threat actor? 🤔**

> **— Asymmetric encryption! 🎉**

To provide HTTPS traffic encryption, the client and the server must perform a "handshake." During the TLS handshake, parties agree to use a highest TLS version supported by both parties and [cipher suites](https://en.wikipedia.org/wiki/Cipher_suite) supported alike. A couple more things happen at this stage. The client verifies the identity of the server by a digital certificate belonging to the latter one, and in case of success, session keys are generated. These session keys allow to encrypt traffic symmetrically.

Both participants of the connection have key pairs consisting of public and private ones. To generate session keys, participants share some data that is encrypted using public keys.

Data sent after the end of the handshake is symmetrically encrypted with the session keys.

## 📜 What you will need to generate a self-signed cert
To create a certificate, we need several other things first:
- A pair of keys for the Certificate Authority
- A self-signed CA certificate
- A pair of keys for the target resource
- A Certificate Signing Request (CSR) for the resource

**CSR** is a message for the CA, signed by an entity's private key. It contains info such as an email, an organization's full name, a country code, a city, a key type and its length, etc. An entity sends this message to a CA with the intent to have it signed by the CA's private key.

## 🪄 How to generate a self-signed cert
> In the following example, we use a 4096-bit long RSA key and an SHA-256 hashing algorithm, but it's just a personal preference. Aside from that, the whole process is explicitly separated into several steps, though some things can be achieved quicker, involving lesser commands.

> And remember that you can choose filenames different from mine. Just make sure they are consistent. :)

> There's a high number of ways you can generate certs. In this scenario, an openssl utility is demonstrated (version 3 and higher).

### 1. Certificate Authority key pair
👇 For the sake of security, the key will be encrypted using an AES-256 algorithm. That's why you'll need to come up with a password.
```
$ openssl genrsa -aes256 -out CA-key.pem 4096
```

### 2. Self-signed CA certificate
It's possible to create a certificate valid for one day to *several years (👇)*. More commonly, they're valid for several months.

> #### A certificate that is expired in more than 13 months will be considered invalid by a huge number of modern devices. It was the case with my iOS-powered phone.

It's better to add a `subjectAltName` extension since the Common Name (CN) is `deprecated` and the Subject Alternative Name (SAN) is a more flexible replacement.

```
$ openssl req -new -x509 -sha256 -days 365 -key CA-key.pem -out CA.pem -addext 'subjectAltName = DNS:hl-ca.deathroll.internal'
```

👆 Initially, the `openssl req` command with a flag `-new` creates a new CSR, but we also specified a flag `-x509` and an option `-key`. This caused a change in the program's behavior:
- `-x509` tells the program to generate an X.509 certificate instead
- The certificate is signed with a private key you provide via `-key`, and the corresponding public key is attached to the certificate itself.

The `-sha256` flag tells `openssl` to use the SHA256 algorithm for digest computation. This algorithm is the default, but in case it changes somewhere in the near future, it's better to be explicit.

{% collapsible OpenSSL 3.0.2 15 Mar 2022 supported digests %}
![OpenSSL 3.0.2 15 Mar 2022 supported digests](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ewy3eack0r3xl1cn5s0x.png)
{% endcollapsible %}

{% collapsible You can check that the certificate is indeed for a CA %}

## And a command for this is pretty simple:
```
$ openssl x509 -in CA.pem -text
```

![X.509 certificate human-readable contents](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4csji2tmwl21pkjzzg9s.png)
{% endcollapsible %}

### 3. Resource key pair
👇 This key could also be encrypted.
```
$ openssl genrsa -out deathroll-internal-key.pem 4096
```

### 4. CSR for a resource/domain
To simplify my life, I generated a CSR for the whole HomeLab domain at once—`*.deathroll.internal`. Such a format is called `wildcard`. It allows one to use a single certificate no matter what subdomain it is. Sounds convenient, right?

```
$ openssl req -new -sha256 -key deathroll-internal-key.pem -out deathroll-internal.csr
```

### 5. Resource certificate
1. Create a small config file for X.509 extensions and add the `subjectAltName` extension to it. In our case it's a file called `certconf.cnf` with the following contents:

    ```
    subjectAltName="DNS:*.deathroll.internal"
    ```

    {% collapsible subjectAlternativeName supported values - x509v3_config (5) %}
    ![subjectAlternativeName supported values - x509v3_config](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kplxg66m7j32res1qy2g.png)
    {% endcollapsible %}

    {% collapsible That's how X.509 extensions look like in a certificate %}
    ![X.509 certificate human-readable contents](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bocnnolpolssdi5anub4.png)
    {% endcollapsible %}

1. Create the certificate itself
    {% collapsible For openssl <3.0.0 %}
    Add the `-CAcreateserial` flag to create a certificate serial number. If omitted, an error will occur.
    {% endcollapsible %}

    ```
    $ openssl x509 -req -sha256 -days 365 -in deathroll-internal.csr -CA CA.pem -CAkey CA-key.pem -out deathroll-internal.pem -extfile certconf.cnf
    ```

### 6. Certificate Chain (Chain of trust)
It's a list of certificates that begins with a resource certificate and ends with a root CA certificate. There can also be intermediate CAs between them. 

Such chains can be found on most web servers—among other places—and are easily generated. To create a chain, copy the contents of a resource cert and then a CA cert into a single file.

> The `cat` command can be applied here. Read the contents and redirect the `stdout` to a file.

```
$ cat deathroll-internal.pem > fullchain.pem
```

```
$ cat CA.pem >> fullchain.pem
```

## 🔫 How to force devices to trust our very own CA
> Don't forget to restart any running browsers so they can trust your certificates.

### Windows
1. Open the Microsoft Management Console
For example, `Win + R` -> `mmc.exe`.

1. Select `Add/Remove Snap-in` in the `File` menu.

1. Select `Certificates` among available snap-ins on the left-hand side of the window and click `Add` in the middle.

1. Next, choose for what account you will manage certificates. For me, it's a Computer account, and on the next page—Local computer.

1. Click `OK` in the previously opened window.

1. Expand `Certificates` in the console root -> `Trusted Root Certification Authorities` -> `Certificates`.

1. Right-click on `Certificates` -> `All tasks` -> `Import`.

1. On the second page of the opened window, choose a certificate file.

    > If the file is not listed, change the file type filter to show all files.

<!--[Demo](https://youtu.be/m9wIhZxzogM)-->

### Linux
> The methods vary vastly depending on a distribution. I use a Debian-based distro.

1. Check if the `ca-certificates` package is installed
    ```
    $ apt list --installed | grep ca-certificates
    ```

1. Install it if it's not present on your system.
    ```
    # apt install ca-certificates
    ```

1. Copy the certificate file to `/usr/share/ca-certificates` and reconfigure the `ca-certificates` package.
    ```
    # cp CA.pem /usr/share/ca-certificates/CA.crt
    ```
    👆 Note that the certificate in the target directory must have the `.crt` extension. Otherwise, it won't be recognized. 

    ```
    # dpkg-reconfigure ca-certificates
    ```
    👆 In a TUI window choose your certificate by pressing `space` on your keyboard.

### FreeBSD
1. Create a directory listed in *TRUSTPATH* (see *ENVIRONMENT* at [certctl(8)](https://www.freebsd.org/cgi/man.cgi?query=certctl&sektion=8)). For example, `/usr/local/etc/ssl/certs`.
    ```
    # mkdir -p /usr/local/etc/ssl/certs
    ```

1. Copy the certificate file to the created directory
    ```
    # cp CA.pem /usr/local/etc/ssl/certs/homelab-ca.crt
    ```

    👆 Note that the certificate in the target directory must have the `.crt` extension. Otherwise, it won't be recognized.

1. Rebuild SSL certificates list
    ```
    # certctl rehash
    ```

1. Verify that the CA certificate was installed (use part of your CN instead of `internal`)
    ```
    $ certctl list | grep internal
    ```

1. Verify the certificate of your service
    ```
    $ openssl s_client -connect nas.deathroll.internal:443 | head
    ```

<!--[Demo](https://youtu.be/tMQkCjw0g9Q)-->

### Android
#### 7.1
Surprisingly, it's stupidly simple to install a custom CA cert. Just download the file and open it with a certificate manager. After that, follow the installation wizard.

#### 13
Try to search in your settings app. It should be somewhere in the privacy section.

<!--[Demo (Pixel 6a)](https://youtube.com/shorts/HpPdLqv0VYk)-->

### iOS
1. Download the certificate and place it somewhere on your filesystem so it can be located in the `Files` app later.

1. Open `Files` and tap on the certificate file. You will be notified that the profile was downloaded.

1. Open `Settings` and tap on the `Profile Downloaded` item at the top.

1. Tap on the profile and then `Install`.

1. While still in the Settings app, go to `General` -> `About` -> `Certificate Trust Settings`.

1. Toggle the switch near your cert.

<!--[Demo](https://youtube.com/shorts/5Ue9rfiZuJE?feature=share)-->

### Some additional configuration required by Firefox
If you are a happy Firefox user, one more additional step is required. You need to permit the browser to use CAs installed on your system.

1. Open `about:config` in a new tab—just type it in the search bar—and accept the risk.

1. Type `security.enterprise_roots.enabled` in the search field on the page and then change the value of this key—it will appear under the input field—from `false` to `true` by clicking the `⇌` button on the right-hand side.

## 🖥️ The result
Congrats! You have created and installed your own Certificate Authority and a resource certificate. Now, if you load a web page or a web app that uses your certificate, a pleasing lock icon will greet you 🔒. Your devices trust your internal services, and annoying messages about insecurity are gone like a fart in a desert. It's worth it, believe me. :)

{% collapsible Internal HTTPS-secured web services opened in various web browsers  %}
![Firefox - Zabbix](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qvc6dp5mophlq3w1v8ok.png)

![Edge - pfSense](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lra3k4m0kze97lhmpe6l.png)

![Brave - Homer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/th42ycni3lhsgavr4r1o.png)

![Chrome on Android - pfSense](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/09960wz2o33xmk1kxq59.png)

![Brave on iOS - pfSense](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q5mle9bffop8p7fyql44.png)
{% endcollapsible %}

## 📖 Additional sources of useful information
### openssl man pages
A helpful one. There are manuals for both config files and openssl subcommands. For example, if you want to learn more about `openssl x509`, issue `man openssl-x509`.

### Videos
- [What is a certificate authority?](https://www.youtube.com/watch?v=8ItJ-VqYo_s)
- [What are Digital Signatures? - Computerphile](https://www.youtube.com/watch?v=s22eJ1eVLTU)
- [TLS Handshake Explained - Computerphile](https://www.youtube.com/watch?v=86cQJ0MMses)
- [How does HTTPS work? What's a CA? What's a self-signed Certificate?](https://www.youtube.com/watch?v=T4Df5_cojAs)

### Text
- [What is a CA? Certificate Authorities Explained](https://www.digicert.com/blog/what-is-a-certificate-authority)
- [What happens in a TLS handshake? | SSL handshake | Cloudflare](https://www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/)
- [How does public key cryptography work? | Public key encryption and SSL | Cloudflare](https://www.cloudflare.com/learning/ssl/how-does-public-key-encryption-work/)
- [What is an SSL certificate? | How to get a free SSL certificate | Cloudflare](https://www.cloudflare.com/learning/ssl/what-is-an-ssl-certificate/)
- [Understanding Digital Signatures | CISA](https://www.cisa.gov/uscert/ncas/tips/ST04-018)
- [What is a Certificate Signing Request (CSR)? Do I need one?](https://www.globalsign.com/en/blog/what-is-a-certificate-signing-request-csr)
- [Certificate Signing Request (CSR) - Overview](https://support.globalsign.com/ssl/ssl-certificates-installation/certificate-signing-request-csr-overview)
- [Maximum SSL/TLS Certificate Validity One Year](https://www.globalsign.com/en/blog/maximum-ssltls-certificate-validity-now-one-year)
- [OpenSSL Docs](https://www.openssl.org/docs/man3.0/)
- [X.509 Extensions - IBM](https://www.ibm.com/docs/en/external-auth-server/2.4.3?topic=securing-x509-extensions)
- [What is a Certificate Chain? | SSL Certificate Chain](https://www.appviewx.com/education-center/what-is-a-certificate-chain/)
- [What is MMC - Windows Server](https://learn.microsoft.com/en-us/troubleshoot/windows-server/system-management-components/what-is-microsoft-management-console)
- [How to: View certificates with the MMC snap-in](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-view-certificates-with-the-mmc-snap-in)