Part 6 – Setting Up HTTPS
November 29, 2024
!Series: Local Environment Apache OpenSSL
In this section, we will be setting up our site to accept connections over HTTPS.
General Concepts
In order to understand the rest of this section, we need to think about a few fundamentals. Specifically, how does the internet work and how can we transmit data (reasonably) securely over it?
All the data available on the internet, from the simplest personal homepage to the most popular apps in the world, are simply pieces of information stored on a computer somewhere.
That means that the internet is simply a series of wires and cables connected by routers which direct pieces of information to and from their destination. HTTP is a protocol that acts like a common language which all the machines connected to the internet are able to use to exchange data.
However, there’s a problem with HTTP. HTTP sends everything in plain text. That’s fine if it’s information that can be safely shown to anyone, but it’s a problem if you want to do something like entering a password or transmitting your credit card details.
Early in the history of the internet, it became apparent that we needed a way to add security to HTTP requests. This led to the development of HTTPS, which stands for Hypertext Transfer Protocol Secure. HTTPS still uses the same protocol to exchange information, but it uses extra technology to do that more securely. Specifically, it uses SSL or TLS, with TLS being the more modern version.
SSL/TLS has three goals:
- Make sure data can’t be read by unauthorized parties (confidentiality)
- Make sure data can’t be tampered with on the way to its destination (integrity)
- Make sure the party sending the information is who they say they are (authentication)
This is a deep and interesting rabbit hole and I would recommend looking at the resources in Further Reading for more information. In the interests of not just paraphrasing other people’s existing work, I’m not going to get further into the details here.
For our purposes, we’re going to need some more components to allow our site to use HTTPS. First, we will need a public / private key set that can secure our data and make sure it can’t be tampered with (confidentiality and integrity). And second, we will need to create a certificate that validates the identity of our local website (authentication).
Installing OpenSSL
On a basic hosting package, your hosting provider will probably set up HTTPS for you. (If you’re self-hosting or using a more complex setup, this may not be true—but you also probably aren’t reading this tutorial in that case.) However, we need to do more legwork in a local environment.
We’ll be using OpenSSL to generate our keys and a certificate. To get OpenSSL, download it at this link. You will want the Windows Installer for the most recent version.
Run the installer. Proceed through the installation screens. When you have the option to do so, check “Adjust PATH system environment”.
After installation is completed, run Command Prompt as an administrator. Test whether OpenSSL has installed correctly by running the command:
openssl
If OpenSSL installed correctly, you should see a list of commands.
openssl
without any additional commands)Creating a Certificate Authority
On a live website, a certificate would be signed by a trusted Certificate Authority, such as Let’s Encrypt, GlobalSign, or IdenTrust. You can see what certificate authority signed a website’s certificate by looking at the security information in your browser.
Since that isn’t possible for a local site, we are going to create our own Certificate Authority and tell our computer to trust it.
Create a new folder called CA in the Development folder.
In Command Prompt, CD into that folder.
cd C:\Development\CA
Enter the following command:
openssl req -x509 -noenc -newkey RSA:2048 -keyout root-ca.key -days 365 -out root-ca.crt -subj "/CN=Local Root"
Let’s walk through what each of the parts of this command are doing:
openssl
indicates the command is an OpenSSL commandreq
indicates that we are making a certificate request-x509
indicates a few things:- We want a certificate and not a certificate signing request
- We want the certificate in the X.509 format (which will include both information about who issued the certificate and a public key)
-noenc
indicates that the private key shouldn’t be encrypted-newkey
indicates we want a private key createdRSA:2048
indicates we want the private key to be an RSA key 2048 bits in size-keyout root-ca.key
indicates the private key file should be given the nameroot-ca.key
-days 365
indicates that the certificate will be good for 365 days-out root-ca.crt
indicates that the certificate will be saved as a file calledroot-ca.crt
-subj "/CN=Local Root"
adds information about the certificate and specifies a Common Name- You need to specify Common Name for other parts of this process to work correctly
If the process completed correctly, you shouldn’t see any errors in the command prompt panel and you should see two files created in the CA folder, one called root-ca.crt
and one called root-ca.key
.
Double-click the certificate file to open it. You will see that the certificate says that it’s not a trusted root certificate yet. We’ll need to fix that.
Trusting the Root CA
Since our certificate doesn’t come from a generally trusted Certificate Authority, we need to take some extra steps to make it trusted on our local machine. Chromium-based browsers (such as Chrome and Edge) use the Windows Certificate Store to determine if the certificate should be trusted,[1] whereas Firefox maintains its own records.
Adding the Root CA to the Windows Certificate Store
Type mmc
into the search bar and click “Run command” or launch Run and use the command mmc
.
This launches the Microsoft Management Console.
Under File, click “Add/Remove Snap-in”.
Select “Certificates”, click “Add”, and then “OK”.
Choose “Computer account” and click “Next”.
Choose “Local computer” and click “Finish”. Click “OK” on the Add or Remove Snap-ins screen.
The console should now show “Certificates” in the leftmost panel. Click on it to see the Certificates logical stores in the center panel. In the center panel, click on “Trusted Root Certification Authorities” to show the Trusted Root Certification Authorities actions in the right panel.
Under “Trusted Root Certification Authorities” on the right, click on “More Actions”, choose “All Tasks”, and then “Import”.
The Certificate Import Wizard will launch. Click “Next” and then locate the certificate. You want to import the certificate file (root-ca.crt
), not the private key.
Accept the default, which is to place the certificate in Trusted Root Certification Authorities. Click “Finish”. You should get a popup that says that import was successful.
If you click into Trusted Root Certification Authorities and then into Certificates, you should be able to see your new certificate listed.
If you double click on it now, you’ll be able to see that the certificate is now trusted for all purposes.
We only want to use the certificate for specific purposes, so let’s go into Details and Edit Properties to change that.
Choose “Enable only the following purposes” and uncheck everything except Server Authentication. (You will have to uncheck everything one at a time as there does not appear to be a uncheck all option.)
Click “Apply” and then “OK”. Close out of MMC.
Adding the Root CA to Firefox
In Firefox, go to about:preferences#privacy
. Scroll down to Certificates and click on “View Certificates”.
Click on “Import”.
Select the root-ca.crt
file from C:\Development\CA
and click “Open”. Choose “Trust this CA to identify websites” and click OK.
If you scroll down in the list of certificates, you should see your newly added certificate. Click “OK” to close.
Creating Site Private Keys and Certificates
We need to create a private key and certificate for each of our websites, using our Certificate Authority as the certificate issuer. To do that, we’ll create a private key and a Certificate Signing Request (CSR), and then use our root Certificate Authority to generate a certificate.
Creating a Private Key and CSR
Open Command Prompt in as an administrator. For now, I’m still doing this in C:\Development\CA
. Run the following command:
openssl req -noenc -newkey rsa:2048 -keyout test-1.key -out test-1.csr -subj "/CN=test-1.local"
Let’s walk through what each of the parts of this command are doing:
openssl
indicates the command is an OpenSSL commandreq
indicates that we are making a certificate request- Because we didn’t put
-x509
, this will generate a certificate signing request instead of a certificate -noenc
indicates that the private key shouldn’t be encrypted-newkey
indicates we want a private key createdRSA:2048
indicates we want the private key to be an RSA key 2048 bits in size-keyout test-1.key
indicates the private key file should be given the nametest-1.key
-out test-1.csr
indicates that the certificate signing request should be saved as a file calledtest-1.csr
-subj "/CN=test-1.local"
adds information about the certificate and specifies a Common Name
Repeat the process for test-2 with the following command:
openssl req -noenc -newkey rsa:2048 -keyout test-2.key -out test-2.csr -subj "/CN=test-2.local"
When both commands have been run, you should have four new files:
test-1.key
, the private key for test-1.localtest-1.csr
, a certificate signing request for test-1.localtest-2.key
, the private key for test-2.localtest-2.csr
, a certificate signing request for test-2.local
Generating Certificates
We need to use the two Certificate Signing Requests to create our site certificates. Before we complete that process, we also need to create a couple of option files to give additional details about generating our certificates. Create the following files:
test-1-opt.txt
:
subjectAltName = DNS:test-1.local
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage=serverAuth
test-2-opt.txt
:
subjectAltName = DNS:test-2.local
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage=serverAuth
Once again, let’s walk through what these commands are doing:
subjectAltName
specifies a Subject Name (which is required in may circumstances)- If we were supporting multiple ways to reach our sites (e.g. www.test-1.local), we could have multiple lines here
authorityKeyIdentifier
indicates what kind of info should be carried over from the Certificate AuthoritybasicConstraints = CA:FALSE
indicates that the certificate we’re creating is not a Certificate AuthoritykeyUsage
indicates how the public key can be used (in this case, to create digital keys and I think to aid in key exchange)extendedKeySusage
indicates more ways the public key can be used—in this case to perform SSL/TLS server authentication
With that done, we’re ready to create our certificates. Use the following command:
openssl x509 -req -CA root-ca.crt -CAkey root-ca.key -in test-1.csr -out test-1.crt -days 365 -extfile test-1-opt.txt
This looks very similar to what we did when we created the CA certificate earlier, but it’s actually a different command. Earlier, we were using req
as the command and -x509
as the option, but now we’re using x509
as the command and -req
as the option. Let’s walk through it:
x509
is a general command that lets you do a bunch of things with certificates-req
indicates that the command should expect a CSR instead of a finished certificate-CA root-ca.crt
indicates thatroot-ca.crt
should be used as the Certificate Authority to sign the certificate-CAkey root-ca.key
indicates that the private key for the root certificate is a file namedroot-ca.key
-in test-1.csr
indicates that the Certificate Signing Request is a file namedtest-1.csr
-out test-1.crt
indicates that the output file should be calledtest-1.crt
-days 365
indicates that the certificate will be good for 365 days-extfile test-1-opt.txt
indicates that the certificate should use the options specified intest-1.opt.txt
Run the command. You should get a message that confirms the certificate was created.
Repeat the process with this command:
openssl x509 -req -CA root-ca.crt -CAkey root-ca.key -in test-2.csr -out test-2.crt -days 365 -extfile test-2-opt.txt
You should see newly created certificates in the CA folder. Double-click on one of them to see more details. You should see that the certificate is issued to your site but issued by the Common Name of the certificate authority we created earlier.
With that done, all of our certificates and keys are ready! You can delete the two CSR files and the option files.
Installing Certificates
Now that we’ve created our site certificates and private keys, we just need to set up our sites to use them. Let’s create a home for those files at C:\Development\www\test-1\ssl
and C:\Development\www\test-2\ssl
.
Move the certificate and key files into the SSL directory. For example, we should end up with test-1.crt
and test-1.key
in C:\Development\www\test-1\ssl
.
Open the Apache configuration directory and open the main Apache configuration file (C:\Development\Apache24\conf\httpd.conf
). We will use this file to update some global settings. Uncomment the following lines:
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule ssl_module modules/mod_ssl.so
Include conf/extra/httpd-ssl.conf
Next, we need to make some changes in httpd-ssl.conf (located in C:\Development\Apache24\conf\extra
). Most of the settings are fine as is, but we’re going to be handling our virtual hosts settings in our httpd-vhosts.conf file, so we need to comment out everything within the <VirtualHost>
statement. (At the time of writing, that’s from Line 121 to the end of the file.)
Finally, open httpd-vhosts.conf (located in C:\Development\Apache24\conf\extra
). Change the ports for our sites to :443
. You’ll also need to add information in each Virtual Host directive to tell Apache where to find the certificate and private key files.
Inside the VirtualHost for test-1.local:
SSLEngine on
SSLCertificateFile "${DOCROOT}/test-1/ssl/test-1.crt"
SSLCertificateKeyFile "${DOCROOT}/test-1/ssl/test-1.key"
Inside the VirtualHost for test-2.local:
SSLEngine on
SSLCertificateFile "${DOCROOT}/test-2/ssl/test-2.crt"
SSLCertificateKeyFile "${DOCROOT}/test-2/ssl/test-2.key"
If you restart Apache and try visiting https://test-1.local, you should see that the site works. Moreover, you should see something that indicates you’ve connected via HTTPS.
There’s one last issue, though. If you try visiting http://test-1.local, you just get the “It works!” screen. That’s because we’ve set up that page as a fallback for any requests received on Port 80 (see Part 3 for a refresher). We can fix that by adding a couple more virtual hosts to handle that situation:
<VirtualHost *:80>
ServerName test-1.local
Redirect permanent "/" "https://test-1.local/"
</VirtualHost>
<VirtualHost *:80>
ServerName test-2.local
Redirect permanent "/" "https://test-2.local/"
</VirtualHost>
This will just listen on Port 80 (the default port for HTTP requests) for any requests for test-1.local or test-2 local. If it gets any, it will redirect to the HTTPS version of the site, and it will send a 301 response code (meaning the requested site has been permanently moved).
With that done, our final httpd-vhosts.conf code looks like this:
# Fallback if no other conditions met
<VirtualHost *:80>
ServerName localhost
</VirtualHost>
# Permanent redirect from HTTP to HTTPS
<VirtualHost *:80>
ServerName test-1.local
Redirect permanent "/" "https://test-1.local/"
</VirtualHost>
<VirtualHost *:80>
ServerName test-2.local
Redirect permanent "/" "https://test-2.local/"
</VirtualHost>
# Main virtual hosts instructions
<VirtualHost *:443>
ServerName test-1.local
DocumentRoot "${DOCROOT}/test-1"
<Directory "${DOCROOT}/test-1/">
Require all granted
</Directory>
<IfModule fcgid_module>
FcgidInitialEnv PATH "${PHPROOT}/php-8.3.9"
FcgidInitialEnv PHPRC "${PHPROOT}/php-8.3.9"
<Files ~ "\.php$>"
Options ExecCGI
AddHandler fcgid-script .php
FcgidWrapper "${PHPROOT}/php-8.3.9/php-cgi.exe" .php
</Files>
</IfModule>
SSLEngine on
SSLCertificateFile "${DOCROOT}/test-1/ssl/test-1.crt"
SSLCertificateKeyFile "${DOCROOT}/test-1/ssl/test-1.key"
</VirtualHost>
<VirtualHost *:443>
ServerName test-2.local
DocumentRoot "${DOCROOT}/test-2"
<Directory "${DOCROOT}/test-2/">
Require all granted
</Directory>
<IfModule fcgid_module>
FcgidInitialEnv PATH "${PHPROOT}/php-7.4.9"
FcgidInitialEnv PHPRC "${PHPROOT}/php-7.4.9"
<Files ~ "\.php$>"
Options ExecCGI
AddHandler fcgid-script .php
FcgidWrapper "${PHPROOT}/php-7.4.9/php-cgi.exe" .php
</Files>
</IfModule>
SSLEngine on
SSLCertificateFile "${DOCROOT}/test-2/ssl/test-2.crt"
SSLCertificateKeyFile "${DOCROOT}/test-2/ssl/test-2.key"
</VirtualHost>
Conclusion
That’s it for this section. In the next (and final) section, we will install WordPress on one of our sites.
Further Reading
Note: Practical Networking has a series of especially good videos that are part of a larger paid course (which I haven’t taken and can’t speak to). I have highlighted the videos I personally found especially useful here.
- General principles of encryption
- “Asymmetric Encryption” by Simply Explained on YouTube
- “How do SSL & TLS protect your Data?” by Simply Explained on YouTube
- “Encryption” by Practical Networking on YouTube
- “Public and Private Keys” by Practical Networking on YouTube
- Fundamentals of TLS
- “What is SSL & TLS?” by Practical Networking on YouTube
- “How do SSL & TLS protect your Data?” by Practical Networking on YouTube
- Understanding certificates
- “Key players of SSL & TLS” by Practical Networking on YouTube
- “Public Key Infrastructure” by Practical Networking on YouTube
- Information about OpenSSL
- “openssl-req” on OpenSSL Documentation (documentation on req command and options)
- “openssl-x509” on OpenSSL Documentation (documentation on x509 command and options)
- “What is the difference between a certificate and a key with respect to SSL?” on Stack Exchange / superuser
- “The .crt and .key Files” by Chin Ming Jun on Baeldung / Linux
- Setting up trusted certificates
- “Is the Common Name mandatory for digital certificates?” on Stack Exchange / Information Security
- “How to make Chrome trust Windows system root CA certificate” on Stack Exchange / superuser
- “Do browsers use client certificates to authenticate the user, the device, or both?” by Johannes Passing
- More information about asymmetric encryption
- “RSA Algorithm” by Practical Networking on YouTube
- “Diffie-Hellman Key Exchange” by Practical Networking on YouTube
- This is especially helpful if you want to understand how it’s possible to have asymmetric encryption
Notes
[1] Internet Explorer isn’t Chromium-based, but also uses the Windows Certificate store.
Posted In: Tutorials