DEV Community

Cover image for Trusted self-signed TLS certificates for dummies (w/ thorough explanations included)
deathroll
deathroll

Posted on • Updated on

Trusted self-signed TLS certificates for dummies (w/ thorough explanations included)

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

This article is my slight extension to the How to create a valid self signed SSL Certificate? 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) 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 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 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
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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

OpenSSL 3.0.2 15 Mar 2022 supported digests
OpenSSL 3.0.2 15 Mar 2022 supported digests

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
Enter fullscreen mode Exit fullscreen mode

X.509 certificate human-readable contents


3. Resource key pair

👇 This key could also be encrypted.

$ openssl genrsa -out deathroll-internal-key.pem 4096
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
    

    subjectAlternativeName supported values - x509v3_config (5)
    subjectAlternativeName supported values - x509v3_config

    That's how X.509 extensions look like in a certificate
    X.509 certificate human-readable contents

  2. Create the certificate itself

    For openssl <3.0.0
    Add the -CAcreateserial flag to create a certificate serial number. If omitted, an error will occur.

    $ 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
Enter fullscreen mode Exit fullscreen mode
$ cat CA.pem >> fullchain.pem
Enter fullscreen mode Exit fullscreen mode

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

  2. Select Add/Remove Snap-in in the File menu.

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

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

  5. Click OK in the previously opened window.

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

  7. Right-click on Certificates -> All tasks -> Import.

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

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
    
  2. Install it if it's not present on your system.

    # apt install ca-certificates
    
  3. 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)). For example, /usr/local/etc/ssl/certs.

    # mkdir -p /usr/local/etc/ssl/certs
    
  2. 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.

  3. Rebuild SSL certificates list

    # certctl rehash
    
  4. Verify that the CA certificate was installed (use part of your CN instead of internal)

    $ certctl list | grep internal
    
  5. Verify the certificate of your service

    $ openssl s_client -connect nas.deathroll.internal:443 | head
    

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.

iOS

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

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

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

  4. Tap on the profile and then Install.

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

  6. Toggle the switch near your cert.

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.

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

Internal HTTPS-secured web services opened in various web browsers
Firefox - Zabbix

Edge - pfSense

Brave - Homer

Chrome on Android - pfSense

Brave on iOS - pfSense


📖 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

Text

Top comments (0)