Apache SSL/TLS mini-HOWTO with Ubuntu 14.04.01 LTS

Introduction

This tutorial originally was written here http://www.vanemery.com/Linux/Apache/apache-SSL.html by VanEmery. Thank you! I just updated it!

I recently had a need to setup a private directory on my web server that could only be accessed by a handful of selected people. The content also needed to be encrypted in transit. This mini-HOWTO details how I did this on a Ubuntu 14.04.1 LTS and Apache/2.4.7 server using mod_ssl and OpenSSL (OpenSSL 1.0.1f 6 Jan 2014). Here are the goals of this small project:

The key to this whole system is the SSL/TLS protocol. SSL stands for Secure Sockets Layer, and it was developed by Netscape to enable secure transactions over the Web. It operates between the TCP layer and the HTTP application layer. TLSv1 is the IETF standard implementation, based on SSLv3. TLS stands for Transport Layer Security.

Assumptions/Prerequisites

First and foremost, this document assumes that you are using some flavor of Linux, Apache, and that you have OpenSSL installed. You should also check out the excellent documentation at Apache.Org. Other assumptions:

Most client tests were performed with Firefox web browser. Firefox is the "reference platform".

Setup your own CA (Certificate Authority)

In order to run a secure (SSL/TLS encrypted) web server, you have to have a private key and a certificate for the server. For a commercial web site, you will probably want to purchase a certificate signed by a well-known root CA. For Intranet or special-purpose uses like this, you can be your own CA. This is done with the OpenSSL tools.

Here, we will make a private CA key and a private CA X.509 certificate. We will also make a directory for the certs and keys:

[root]# sudo -i
[root]# mkdir CA
[root]# chmod 0770 CA
[root]# cd CA

[root]# openssl genrsa -des3 -out my-ca.key 2048
Generating RSA private key, 2048 bit long modulus
.....................................................+++
...................................................+++
e is 65537 (0x10001)
Enter PEM pass phrase: passwordca
Verifying password - Enter PEM pass phrase:

[root]# openssl req -new -x509 -days 3650 -key my-ca.key -out my-ca.crt
Using configuration from /usr/share/ssl/openssl.cnf
Enter PEM pass phrase:
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]:IT
State or Province Name (full name) [Some-State]:Italy
Locality Name (eg, city) []:Padova
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Company
Organizational Unit Name (eg, section) []:CA
Common Name (e.g. server FQDN or YOUR name) []:companyca.com             
Email Address []:info@companyca.com

[root]# openssl x509 -in my-ca.crt -text -noout

Notes: The first OpenSSL command makes the key. The second command makes the X.509 certificate with a 10-year lifetime. The third command lets you view the completed certificate. Make sure that you keep the password in a safe place, you will need this every time you sign another certificate! You will probably also want to make backups of the cert and key and lock them in a safe place.

Make a key and a certificate for the web server:

Now, we have to make an X.509 certificate and corresponding private key for the web server. Rather than creating a certificate directly, we will create a key and a certificate request, then "sign" the certificate request with the CA key we made in Step 1. You can make keys for multiple web servers this way. One thing to note is that SSL/TLS private keys for web servers need to be either 512 or 1024 bits. Any other key size may be incompatible with certain browsers.

[root]# openssl genrsa -des3 -out my-server.key 1024
Generating RSA private key, 1024 bit long modulus
....++++++
.++++++
e is 65537 (0x10001)
Enter PEM pass phrase: passwordserver
Verifying password - Enter PEM pass phrase:

[root]# openssl req -new -key my-server.key -out my-server.csr
Using configuration from /usr/share/ssl/openssl.cnf
Enter PEM pass phrase: passwordserver
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]:IT
State or Province Name (full name) [Some-State]:Italy
Locality Name (eg, city) []:Padova
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Server company
Organizational Unit Name (eg, section) []:server
Common Name (e.g. server FQDN or YOUR name) []:secure.mysite.com
Email Address []:info@secure.mysite.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

[root]# openssl x509 -req -in my-server.csr -out my-server.crt -sha1 -CA my-ca.crt -CAkey my-ca.key -CAcreateserial -days 2190
Signature ok
subject=/C=TW/ST=Taipei County/L=Nankang/O=VanEmery.Com/OU=Web Services/CN=mars.vanemery.com/Email=hostmaster@vanemery.com
Getting CA Private Key
Enter PEM pass phrase: passwordca

[root]# openssl x509 -in my-server.crt -text -noout

Make sure that your server name is the same as the FQDN that your clients will use when connecting to your site. Also, let's get in the habit of protecting our keys with appropriate permissions:

[root]# chmod 0400 *.key

Now, we need to move the new keys and certs into the proper directories in the /etc/apache2 hierarchy:

[root]# mkdir /etc/apache2/ssl/
[root]# cp my-server.crt /etc/apache2/ssl/
[root]# cp my-server.key /etc/apache2/ssl/
[root]# cp my-ca.crt /etc/apache2/ssl/

Create directories and files for the secure web service

I do not want the secure branch of my webserver directory tree to be part of my "insecure" branch that serves unencrypted files. My normal web root directory is /var/www/html . The document root for the secure web server will be located at /var/www/SSL .

[root]# mkdir /var/www/secure.mysite.com
[root]# chmod 0775 /var/www/secure.mysite.com
[root]# cd /var/www/secure.mysite.com
[root]# mkdir passneeded
[root]# mkdir certneeded
[root]# mkdir passandcert

For testing purposes, I added those very simple test index files into each dir.

This one is /var/www/secure.mysite.com/passneeded/index.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
	<title>SSL pass needed</title>
</head>
<body>
	<h2>SSL pass needed</h2>
</body>
</html>
This one is /var/www/secure.mysite.com/certneeded/index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
	<title>SSL cert needed</title>
</head>
<body>
	<h2>SSL cert needed</h2>
</body>
</html>
This one is /var/www/secure.mysite.com/passandcert/index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
	<title>SSL pass and cert</title>
</head>
<body>
	<h2>SSL pass and cert</h2>
</body>
</html>
Copy some JPEG files and text files into each directory, so that there will be something to look at/retrieve in each directory.

Configure the Apache web server

On a default Ubuntu 14.04.01 install, we have to edit two different files:

[root]# nano /etc/apache2/ports.conf
adding the line
Listen 443 
and then create
[root]# nano /etc/apache2/sites-available/secure.mysite.com.conf
with this text in it:
<VirtualHost *:443>
    ServerAdmin webmaster@localhost
    ServerName secure.mysite.com
    DocumentRoot /var/www/secure.mysite.com/
    ErrorLog ${APACHE_LOG_DIR}/secure.mysite.com/error.log
    CustomLog ${APACHE_LOG_DIR}/secure.mysite.com/access.log combined

    SSLEngine on
    SSLProtocol all -SSLv2

    SSLCertificateFile /etc/apache2/ssl/my-server.crt
    SSLCertificateKeyFile /etc/apache2/ssl/my-server.key
    SSLCertificateChainFile /etc/apache2/ssl/my-ca.crt
    SSLCACertificateFile /etc/apache2/ssl/my-ca.crt

    BrowserMatch "MSIE [2-6]" \
                 nokeepalive ssl-unclean-shutdown \
                 downgrade-1.0 force-response-1.0
    BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown

</VirtualHost>

Start the web server and test

Run the following commands to start the the Apache web server:

[root]# service apache2 restart
 * Restarting web server apache2
Apache needs to decrypt your SSL Keys for secure.mysite.com:443 (RSA)
Please enter passphrase: passwordserver

Note that you will have to enter the password for your server key in order to start the server. You will also have to do this during boot if you have httpd configured to start automatically.

Make sure that the web server is now listening on the SSL/TLS port, TCP port 443:

[root]# netstat -tna
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN

In order to test that your SSL/TLS web server is running, you will now need to connect to it with a browser. The URL you use should be https://secure.mysite.com. You will probably get a warning prompt about the Certificate Authority (CA) being unknown. You can view the certificate properties, which will look familiar because you created the cert yourself. You can save the cert in your browser, or import the my-ca.crt file into your browser as a new CA. How you do this will depend on which browser you are using. I had no problems doing this with Firefox.

Require simple username/password auth for one of the directories:

We want to require a valid username and password for the /var/www/secure.mysite.com/passneeded directory. The username and password will be encrypted in transit as part of the TCP stream. We will need to setup the access control directives, as well as use the htpasswd command to add the username/password pairs.

[root]# htpasswd -c -m /etc/apache2/.htpasswd foo
New password: foo
Re-type new password: foo
Adding password for user foo
[root]# htpasswd -m /etc/apache2/.htpasswd goo
New password: goo
Re-type new password: goo
Adding password for user goo

[root]# chown www-data:www-data /etc/apache2/.htpasswd
[root]# chmod 0460 /etc/apache2/.htpasswd

Now, we need to tell Apache to require a username/password to access the Passneeded directory. Here is what we will add to /etc/apache2/sites-available/secure.mysite.com.conf file:

<Directory /var/www/secure.mysite.com/passneeded">
	AuthType Basic
	AuthName "Username and Password Required"
	AuthUserFile /etc/apache2/.htpasswd
	Require valid-user
</Directory>

Now, restart the webserver. When you try to access the passneeded directory from a web browser, you should be prompted for a username and password. If you enter incorrect information, you should be denied access.

Creating Client Certificates for Authentication

Now, let's say that we want a stronger method of authenticating clients, one that is not as susceptible to password guessing and shoulder-surfing. What can we do? We can create an SSL/TLS client certificate. The certificate has to be digitally signed by a CA that the server trusts, the user has to have the client loaded in his browser, and the user has to know a pass phrase to use it. The certificate itself uses strong, public-key cryptography. We can make such a certificate with our OpenSSL toolkit.

A note on certificate formats:   The server and CA certs that we have been using up to now are encoded in PEM format, which uses ASCII characters. For some reason, the industry-standard client certs used in web browsers are encoded in the PKCS#12 format, which cannot be viewed as simple text. It is a binary file. We will now create a client cert by following these steps:

[root]# cd /root/CA
[root]# openssl genrsa -des3 -out client-c.key 1024
Generating RSA private key, 1024 bit long modulus
..++++++
........................................................................++++++
e is 65537 (0x10001)
Enter PEM pass phrase: passwordclient
Verifying password - Enter PEM pass phrase: passwordclient

[root]# openssl req -new -key client-c.key -out client-c.csr
Using configuration from /usr/share/ssl/openssl.cnf
Enter PEM pass phrase: passwordclient
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]:IT
State or Province Name (full name) [Some-State]:Italy
Locality Name (eg, city) []:Padova
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Client company
Organizational Unit Name (eg, section) []:client
Common Name (e.g. server FQDN or YOUR name) []:The client
Email Address []:info@client.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

# openssl x509 -req -in client-c.csr -out client-c.crt -sha1 -CA my-ca.crt -CAkey my-ca.key -CAcreateserial -days 3650

[root]# openssl pkcs12 -export -in client-c.crt -inkey client-c.key -name "Client Cert" -out client-c.p12

export password: passwordexpimp

[root]# openssl pkcs12 -in client-c.p12 -clcerts -nokeys -info

import password: passwordexpimp

Note: The "export password" is all the end-user needs to know. This is what you will be asked for when installing the certificate in a browser.

Now move the client-c.p12 file to your client machine and import it into your web browser. This is usually done your browser's "Preferences" section under "Privacy and Security", "Certificates", "Manage Certificates". You may be asked to input a Software Security Device Master Password. DO NOT FORGET this password! You will also be asked for the client certificate's export password.

Configure Apache to require client certificates for a specific directory:

In order to require client certificates for the /var/www/secure.mysite.com/certneeded directory, you will need to add the following lines to the /etc/apache2/sites-available/secure.mysite.com.conf configuration file:

<Directory /var/www/secure.mysite.com/certneeded>
    SSLVerifyClient require
    SSLVerifyDepth 1
</Directory>

Now, restart Apache. You can now connect from your client machine browser to the secure webserver at https://secure.mysite.com/certneeded. You should now be able to click on the "Client Certificate Required" link and view the files in that directory. I successfully tested my client certificate with all major browsers.

If you connect from a different browser that does not have the certificate installed, you will not be able to enter the directory at all. When you look in your error log file, you will see this error:

[17:41:41] [error] Re-negotiation handshake failed: Not accepted by client!?

Web Server Key Password:

You have probably noticed by now that every time you restart Apache or boot your server, you are forced to enter the password for the server key. This is a security measure, but it can be inconvenient. If you would like to make an insecure server key that will allow Apache to start automatically at boot time, then there is a way to do this. In my case, I don't run an e-commerce site and I'm not worried about someone else creating a fake VanEmery.Com secure web site. It is more likely that a power outage will occur that will cause my server to reboot while I am not around, so I want it to boot without requiring a password. The choice is yours...

Here is how you do it:

[root]# cd /etc/apache2/ssl
[root]# cp my-server.key my-server.key.org

[root]# openssl rsa -in my-server.key.org -out my-server.key

[root]# chmod 0400 my-server*

Now, you should be able to restart Apache or boot your server without having to input the password. This may also be a very good time to copy all the keys and certificates that you made to an external HDD. You can imagine what a pain it would be if you lost all of your keys and certs due to a disk failure. You may even want to make paper copies of the PEM encoded certificates and keys, which use ASCII text. Lock them in a secure place, along with any passwords.