A while back I had written two blog posts about setting up an Apache, MySQL and multiple, simultaneous PHP versions environment for macOS -or Linux, same concept- and for Windows. In the meantime HTTPS has been promoted to a near necessity and so being able to build and test a site on HTTPS is very desirable. Well, as it turns out, it’s perfectly possible too!

The following tutorial is written on Windows but it is applicable on macOS and Linux as well. You will need to adapt the paths in the commands and file names. Also, please bear in mind this is a fairly long and detailed read. You may want to set aside an hour or two to follow it through.

What you’ll need

The Apache server I’ve described in my blog posts, obviously.

The OpenSSL executable (the instructions assume it’s somewhere in your path). The OpenSSL download page includes links for Windows and macOS downloads. Linux should have it already installed. If you’re on Ubuntu try sudo apt-get install openssl ssl-cert to make sure. Please note that Apache already includes a copy of OpenSSL. Therefore on Windows it might be sufficient to just add C:\Apache24\bin to your path.

Chrome or Firefox. You’ll need to install the generated certificate in your browser. You can probably do that in other browsers but, frankly, these two are the easiest.

Wildcard SSL certificates and custom Certification Authorities

As you remember, the server setup allows you to have a special domain name for each version of PHP. For example, if you’re testing a site on the ancient PHP 5.3 you’d be using the local53.web domain, whereas if you’re testing it on PHP 5.4 you’d be using the local54.web domain and so on and so forth. The name of the site would be in the subdomain part, e.g. mysite.local53.webmysite.local54.web and so on.

Luckily, we do not have to create an SSL certificate for each subdomain and domain combination. We do, however, need to create and install a wildcard SSL certificate for each one of the PHP version-specific domains. A wildcard SSL certificate is issued against the “star” subdomain, e.g. *.local53.web, which tells the browser that it’s valid for all subdomains.

The downside is that with six concurrent PHP versions (at the time of this writing I have PHP 5.3 to 7.1 inclusive) this gets complicated as I have to tell the operating system and Firefox to trust each one of these certificates. That’s 12 operations. Every time I add a PHP version I have to remember to do something in two different places. If I have to remember to do things I’ll end up making mistakes and waste my time.

The solution to that is having a custom Certification Authority. In this case we only need to tell our operating system and Firefox to trust our Certification Authority. Since it signs our custom certificates they are implicitly trusted. That’s how certificates work in practice, too! When you buy an SSL certificate it’s signed by the issuing company’s certificate which is signed by their Certificate Authority root certificate. Your browser only knows and trusts the Certificate Authority root certificate. Since it sees that the chain of signatures goes all the way to this Certificate Authority root certificate it trusts your site’s certificate implicitly.

Creating a custom certification authority

We’ll create our Root Certification Authority in C:\Apache24\ca. Inside it we’ll also have the intermediatefolder with our Intermediate Certification Authority which signs our wildcard SSL certificates. The signed wildcard SSL certificates themselves will be in the intermediate subdirectory as well.

Let’s start by making the necessary folders:

cd c:\apache24
mkdir ca ca\certs ca\crl ca\newcerts ca\private
mkdir ca\intermediate ca\intermediate\certs ca\intermediate\crl ca\intermediate\newcerts ca\intermediate\private ca\intermediate\csr
cd c:\apache24\ca
copy nul index.txt
echo 1000>serial
copy nul intermediate/index.txt
echo 1000>intermediate/serial

Our certificates will not work properly unless we use Subject Alternative Name (SAN) extensions. This is only possible by creating a custom openssl.cnf file with the following contents, i.e. copy and paste the following to C:\Apache24\ca\openssl.cnf :

# OpenSSL root CA configuration file.
# Copy to `/root/ca/openssl.cnf`.
#
# This file is based on the instructions found in https://jamielinux.com/docs/openssl-certificate-authority/index.html

[ ca ]
# `man ca`
default_ca = CA_default

[ CA_default ]
# Directory and file locations.
dir               = c:/apache24/ca
certs             = $dir/certs
crl_dir           = $dir/crl
new_certs_dir     = $dir/newcerts
database          = $dir/index.txt
serial            = $dir/serial
RANDFILE          = $dir/private/.rand

# The root key and root certificate.
private_key       = $dir/private/ca.key.pem
certificate       = $dir/certs/ca.cert.pem

# For certificate revocation lists.
crlnumber         = $dir/crlnumber
crl               = $dir/crl/ca.crl.pem
crl_extensions    = crl_ext
default_crl_days  = 30

# SHA-1 is deprecated, so use SHA-2 instead.
default_md        = sha256

name_opt          = ca_default
cert_opt          = ca_default
default_days      = 375
preserve          = no
policy            = policy_strict

[ policy_strict ]
# The root CA should only sign intermediate certificates that match.
# See the POLICY FORMAT section of `man ca`.
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ policy_loose ]
# Allow the intermediate CA to sign a more diverse range of certificates.
# See the POLICY FORMAT section of the `ca` man page.
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
# Options for the `req` tool (`man req`).
default_bits        = 2048
distinguished_name  = req_distinguished_name
string_mask         = utf8only

# SHA-1 is deprecated, so use SHA-2 instead.
default_md          = sha256

# Extension to add when the -x509 option is used.
x509_extensions     = v3_ca

[ req_distinguished_name ]
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
countryName                     = Country Name (2 letter code)
stateOrProvinceName             = State or Province Name
localityName                    = Locality Name
0.organizationName              = Organization Name
organizationalUnitName          = Organizational Unit Name
commonName                      = Common Name
emailAddress                    = Email Address

# Optionally, specify some defaults.
countryName_default             = CY
stateOrProvinceName_default     = Nicosia
localityName_default            = Egkomi
0.organizationName_default      = Akeeba Ltd
organizationalUnitName_default  = Production Department
emailAddress_default            = This email address is being protected from spambots. You need JavaScript enabled to view it.

[ v3_ca ]
# Extensions for a typical CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign

[ v3_intermediate_ca ]
# Extensions for a typical intermediate CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign

[ usr_cert ]
# Extensions for client certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = client, email
nsComment = "OpenSSL Generated Client Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, emailProtection

[ server_cert ]
# Extensions for server certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
crlDistributionPoints = URI:http://local.web/intermediate.crl.pem

[ crl_ext ]
# Extension for CRLs (`man x509v3_config`).
authorityKeyIdentifier=keyid:always

[ ocsp ]
# Extension for OCSP signing certificates (`man ocsp`).
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, OCSPSigning

[ san_env ]
subjectAltName=${ENV::SAN}

Now we are ready to start creating the certificate authority. All of our certificates will be encrypted with a password. It is set by the first command below. Change it to something other than changeme ;)

set CERTIFICATE_PASSWORD=changeme
set SAN=DNS:local.web,DNS:*.local.web
cd c:\apache24\ca
openssl genrsa -aes256 -passout pass:%CERTIFICATE_PASSWORD% -out private/ca.key.pem 4096
openssl req -config openssl.cnf -key private/ca.key.pem -passin pass:%CERTIFICATE_PASSWORD% ^
-new -x509 -days 7300 -sha256 -extensions v3_ca ^
-subj "/CN=Windows Dev Box Root CA/O=Akeeba Ltd./OU=Production Department/C=CY/ST=Nicosia/L=Egkomi" ^
-out certs/ca.cert.pem

Just like before we need to create a custom openssl.cnf file in the intermediate folder with the following contents, i.e. copy and paste the following to C:\Apache24\ca\intermediateopenssl.cnf :

# OpenSSL root CA configuration file.
#
# This file is based on the instructions found in https://jamielinux.com/docs/openssl-certificate-authority/index.html

[ ca ]
# `man ca`
default_ca = CA_default

[ CA_default ]
# Directory and file locations.
dir               = c:/Apache24/ca/intermediate
certs             = $dir/certs
crl_dir           = $dir/crl
new_certs_dir     = $dir/newcerts
database          = $dir/index.txt
serial            = $dir/serial
RANDFILE          = $dir/private/.rand

# The root key and root certificate.
private_key       = $dir/private/intermediate.key.pem
certificate       = $dir/certs/intermediate.cert.pem

# For certificate revocation lists.
crlnumber         = $dir/crlnumber
crl               = $dir/crl/intermediate.crl.pem
crl_extensions    = crl_ext
default_crl_days  = 30

# SHA-1 is deprecated, so use SHA-2 instead.
default_md        = sha256

name_opt          = ca_default
cert_opt          = ca_default
default_days      = 375
preserve          = no
policy            = policy_loose

[ policy_strict ]
# The root CA should only sign intermediate certificates that match.
# See the POLICY FORMAT section of `man ca`.
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ policy_loose ]
# Allow the intermediate CA to sign a more diverse range of certificates.
# See the POLICY FORMAT section of the `ca` man page.
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
# Options for the `req` tool (`man req`).
default_bits        = 2048
distinguished_name  = req_distinguished_name
string_mask         = utf8only

# SHA-1 is deprecated, so use SHA-2 instead.
default_md          = sha256

# Extension to add when the -x509 option is used.
x509_extensions     = v3_ca

[ req_distinguished_name ]
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
countryName                     = Country Name (2 letter code)
stateOrProvinceName             = State or Province Name
localityName                    = Locality Name
0.organizationName              = Organization Name
organizationalUnitName          = Organizational Unit Name
commonName                      = Common Name
emailAddress                    = Email Address

# Optionally, specify some defaults.
countryName_default             = CY
stateOrProvinceName_default     = Nicosia
localityName_default            = Egkomi
0.organizationName_default      = Akeeba Ltd
organizationalUnitName_default  = Production Department
emailAddress_default            = This email address is being protected from spambots. You need JavaScript enabled to view it.

[ v3_ca ]
# Extensions for a typical CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign

[ v3_intermediate_ca ]
# Extensions for a typical intermediate CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign

[ usr_cert ]
# Extensions for client certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = client, email
nsComment = "OpenSSL Generated Client Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, emailProtection

[ server_cert ]
# Extensions for server certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth

[ crl_ext ]
# Extension for CRLs (`man x509v3_config`).
authorityKeyIdentifier=keyid:always

[ ocsp ]
# Extension for OCSP signing certificates (`man ocsp`).
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, OCSPSigning

[ san_env ]
subjectAltName=${ENV::SAN}

Next up, we’ll create the private key for our intermediate certificate, a CSR (certificate signing request) and sign it with the root CA certificate - thus having a valid certificate we can use to sign the actual wildcard certificates. Again, the certificates are encrypted with a password set in the command below. Change it to suit your needs.

Open a command prompt and run the following

set CERTIFICATE_PASSWORD=changeme
set SAN=DNS:local.web,DNS:*.local.web
cd c:\apache24\ca
openssl genrsa -aes256 -passout pass:%CERTIFICATE_PASSWORD% -out intermediate/private/intermediate.key.pem 4096

REM Create a certificate sign request
openssl req -config intermediate/openssl.cnf -new -sha256 ^
-key intermediate/private/intermediate.key.pem ^
-passin pass:%CERTIFICATE_PASSWORD% ^
-subj "/CN=Windows Dev Box Intermediate CA/O=Akeeba Ltd./OU=Production Department/C=CY/ST=Nicosia/L=Egkomi" ^
-out intermediate/csr/intermediate.csr.pem

REM Sign the request with the master CA certificate
openssl ca -config openssl.cnf -extensions v3_intermediate_ca -batch ^
-passin pass:%CERTIFICATE_PASSWORD% ^
-days 3650 -notext -md sha256 ^
-in intermediate/csr/intermediate.csr.pem ^
-out intermediate/certs/intermediate.cert.pem

REM Create the CA chain file
copy intermediate\certs\intermediate.cert.pem ^
+ certs\ca.cert.pem intermediate\certs\ca-chain.cert.pem

Creating a wildcard certificate per domain

Each PHP version domain name (local53.weblocal54.web, etc) gets its own wildcard certificate signed by the Intermediate CA we created above. This allows all sites, served as subdomains, to be usable through HTTPS. We assume that certificates are to be stored in C:\Apache24\ssl.

Like before, you need to supply the password your certificates will be encrypted with. This is the first line of the commands below. Please change it.

For the rest of this section we assume that the domain you are issuing for is local53.web. You MUST adjust this for each domain name. This is the second line of the commands below. Please remember to change it.

Open a command prompt and run the following

set CERTIFICATE_PASSWORD=changeme
set CERTIFICATE_DOMAIN=local53.web
set SAN=DNS:%CERTIFICATE_DOMAIN%,DNS:*.%CERTIFICATE_DOMAIN%
cd c:\apache24\ca

openssl genrsa -aes256 ^
-passout pass:%CERTIFICATE_PASSWORD% ^
-out intermediate\private\%CERTIFICATE_DOMAIN%.key.pem 2048

openssl req -config intermediate\openssl.cnf ^
-key intermediate\private\%CERTIFICATE_DOMAIN%.key.pem ^
-extensions san_env ^
-passin pass:%CERTIFICATE_PASSWORD% ^
-subj "/CN=*.%CERTIFICATE_DOMAIN%/O=Akeeba Ltd./OU=Production Department/C=CY/ST=Nicosia/L=Egkomi" ^
-new -sha256 -out intermediate\csr\%CERTIFICATE_DOMAIN%.csr.pem

openssl ca -config intermediate/openssl.cnf -batch ^
-extensions server_cert -extensions san_env ^
-days 1835 -notext -md sha256 ^
-in intermediate/csr/%CERTIFICATE_DOMAIN%.csr.pem ^
-passin pass:%CERTIFICATE_PASSWORD% ^
-out intermediate/certs/%CERTIFICATE_DOMAIN%.cert.pem

copy intermediate\certs\ca-chain.cert.pem c:\Apache24\ssl\ca-chain.crt

openssl rsa -in intermediate/private/%CERTIFICATE_DOMAIN%.key.pem ^
-out c:\Apache24\ssl\%CERTIFICATE_DOMAIN%.key ^
-passin pass:%CERTIFICATE_PASSWORD%

copy intermediate\certs\%CERTIFICATE_DOMAIN%.cert.pem c:\Apache24\ssl\%CERTIFICATE_DOMAIN%.crt

Enable SSL support in Apache

Edit your httpd.conf file. If you’d followed my Windows tutorial that would be in C:\Apache24\conf\httpd.conf.

Find this line:

# LoadModule ssl_module modules/mod_ssl.so

and remove the hash sign in front:

LoadModule ssl_module modules/mod_ssl.so

This tells Apache to load the SSL module which is required for HTTPS support.

Next, towards the top of the file, find the line

Listen 80

and change it to

Listen 80

<IfModule mod_ssl.c>
    Listen 443
</IfModule>

This tells Apache to listen not only to the HTTP port (80), but also the HTTPS (443) one as long as SSL support is enabled.

Restart Apache. It should load just fine.

Caveat: do NOT enable the default SSL configuration file for Apache e.g. C:\Apache24\conf\extra\httpd-ssl.conf on Windows. That’s the file that has the line <VirtualHost _default_:443>. This line tells Apache to use a default SSL certificate. This is exactly what we MUST NOT do. That bit got me until I found a mention of it in a DigitalOcean tutorial.

Add the correct SSL certificate to each PHP version

Now we need to edit our Apache virtual hosts file. According to my previous tutorial that file is in C:\Apache24\conf\extra\httpd-vhost.conf

At the top of the file we need to add two important lines:

NameVirtualHost *:80
NameVirtualHost *:443

They tell Apache that we want virtual hosts on both HTTP (port 80) and HTTPS (port 443).

Now find the block that starts with

# Dynamic virtual hosts using vhost_alias, PHP 5.3 

Right below its closing </VirtualHost> entry add the following:

# PHP 5.3 over HTTPS (local53.web) 
<IfModule mod_ssl.c>
        <VirtualHost *:443>
                ServerAdmin This email address is being protected from spambots. You need JavaScript enabled to view it.
                ServerName www.local53.web 
                DocumentRoot "c:/Apache24/htdocs" 

                SSLEngine on
                SSLCertificateFile "c:\Apache24\ssl\local53.web.crt"
                SSLCertificateKeyFile "c:\Apache24\ssl\local53.web.key"

                FcgidInitialEnv PATH "c:/php/5.3;C:/WINDOWS/system32;C:/WINDOWS;C:/WINDOWS/System32/Wbem;"
                FcgidInitialEnv PHPRC "c:/php/5.3"                
                
        <Directory "c:/Apache24/htdocs"> 
            <Files ~ "\.php$"> 
                AddHandler fcgid-script .php 
                FcgidWrapper "c:/php/5.3/php-cgi.exe" .php 
                Options +ExecCGI 
                order allow,deny 
                allow from all 
                deny from none 
            </Files> 
        </Directory> 

        </VirtualHost>

        <VirtualHost *:443>
                ServerAlias *.local53.web
                UseCanonicalName Off
                VirtualDocumentRoot "c:/Apache24/htdocs/%1"

                SSLEngine on
                SSLCertificateFile "c:\Apache24\ssl\local53.web.crt"
                SSLCertificateKeyFile "c:\Apache24\ssl\local53.web.key"
                
                FcgidInitialEnv PATH "c:/php/5.3;C:/WINDOWS/system32;C:/WINDOWS;C:/WINDOWS/System32/Wbem;"
                FcgidInitialEnv PHPRC "c:/php/5.3"                

                <Directory "c:/Apache24/htdocs"> 
            <Files ~ "\.php$"> 
                AddHandler fcgid-script .php 
                FcgidWrapper "c:/php/5.3/php-cgi.exe" .php 
                Options +ExecCGI 
                order allow,deny 
                allow from all 
                deny from none 
            </Files> 
        </Directory> 
        </VirtualHost>

</IfModule>

A keen observer will notice that this is neraly identical to what we had done to enable PHP 5.3 support with a few minor, yet important, changes:

  • Everything is wrapped inside the <IfModule mod_ssl.c> statement. This prevents Apache from dying an undignified death on start-up should the SSL support be accidentally disabled.
  • The virtual host lines read <VirtualHost *:443> instead of <VirtualHost *:80>. It makes sense, since we’re dealing with HTTPS that’s served over port 443, not port 80.
  • We added the lines starting with SSL in each virtual host definition. They tell Apache to turn on HTTPS for that virtual host and which certificate to use.

So now you know how simple it is to repeat that process on all other virtual hosts, covering all installed versions of PHP. My dev servers have six PHP versions right now: 5.3, 5.4, 5.5, 5.6, 7.0 and 7.1. So I have to repeat that process another five times. Considering that I have 15 sites on each server I guess it’s much better only having to create and install six certificates than 90!

Now restart Apache

Caveat: If you try to access the SSL site right now you’ll get an error about the certificate being insecure. That’s because we’re using a self-signed certificate. We’ll deal with that in the next step.

Install the Certificate Authority

All browsers share an operating system level certificate authority store. This means that adding our CA Root certificate on one browser will make it available on all browsers on your system, as well as other application. There are a few exceptions to that rule. Firefox does its own thing, so you MUST add the Root CA certificate to it separately. Moreover, some applications will not use the OS-wide CA store, e.g. Bash on Ubuntu on Windows, CLI tools like cURL and scripting languages such as PHP (which expect you to configure your own CA cache). The following instructions deal with web browsers only and only for the three major desktop operating systems. Adding CA roots to mobile OS ranges from almost impossible at worst to very complicated at best. If you need it (you develop mobile apps), you know how to do it.

Windows (Chrome, Opera, Internet Explorer, Microsoft Edge)

  • Press the Windows key and R at the same time
  • Type certmgr.msc and press ENTER
  • You’ll get the UAC dialog. Click on Yes.
  • Double click on the Trusted Root Certification Authorities folder entry. You now see a Certificates folder entry.
  • Right click on the Certificates folder entry, choose All Tasks, Import.
  • Select the Root CA certificate file you created, stored in c:\Apache24\ca\certs\ca.cert.pem
  • You are asked about the certificate store, confirm it’s Trusted Root Certification Authorities.
  • Accept everything and close all dialogs.
  • Close and restart all browsers.

Firefox (all Operating Systems)

Unlike other browsers, Firefox uses its own certificate authority store. We need to add our CA root there:

  • Click on the hamburger menu, Options
  • Click on Advanced, then on the Certificates tab
  • Click on the View Certificates button
  • Click on the Authorities tab, then on the Import button
  • Select the Root CA certificate file you created, stored in c:\Apache24\ca\certs\ca.cert.pem
  • You see check boxes about the intended trust, select all of them.
  • Accept everything and close all dialogs.
  • Close and restart the browser.

Linux (Google Chrome and Opera)

  • Go to Chrome settings, Show advanced settings, HTTPS/SSL, Manage Certificates.
  • Now click on the Authorities or Trusted Root Certification Authorities tab depending on your operating system.
  • Click the Import button.
  • Select the Root CA certificate file you created, stored in c:\Apache24\ca\certs\ca.cert.pem
  • You see check boxes about the intended trust, select all of them.
  • Accept everything and close all dialogs.
  • Close and restart the browser.

macOS (All browsers except Firefox)

  • Copy the Root CA certificate file you created, stored in c:\Apache24\ca\certs\ca.cert.pem on your Windows computer, to your desktop (you may need to change the extension to .crt)
  • Double click the certificate on your desktop. You will be asked to enter your macOS password.
  • Add the certificate to the System keychain, not the Login keychain.
  • Double click on the certificate you just added in Keychain Access.
  • Expand the Trust section.
  • Set When using this certificate to Always Trust.
  • Restart all your browsers

Especially for Chrome, remember that it never really quits. It lingers in the background. You need to press CMD-Q twice in an open Google Chrome window to really quit it. If the instructions seem to not be working try logging off and logging back on.

Further ideas

You can use this in some production environments. Obviously it's not practical for live sites, as you'd have to ask every visitor to trust your arbitrary CA root. You could, however, use this for an Intranet where you exert control over the clients, meaning you can install custom CA roots on them as you see fit.

The principle of creating a custom Certificate Authority is absolutely not specific to Windows. You can apply this idea to any AMP stack on any operating system. I’m already using it on Linux and macOS.

Certificate Authorities are not just limited to HTTPS. They can be used, for example, to sign distributed code as I presented in the J and Beyond 2017 conference.

Please note that if you are using a CA root outside of a development environment you MUST use an air-gaped computer, protected by physical security, to prevent compromise of your CA root and the intermediate certificate. Only the signed site certificate should ever leave the air-gaped computer.