Objectives

The objective of this lab is to learn about configuring TLS (formerly SSL) on web servers.

In this exercise, you will:

  • Set yourself up as a Certification Authority

    • Create private key and certificate request for yourself as a CA
    • Create and install CA self-signed certificate (effectively a root certificate)

  • Configure web server security

    • Install server private key and server (public key) certificate
    • Configure nginx and node/hapi for TLS

Certificate Authorities

The term Certificate Authority (CA) refers to anyone who issues (i.e. signs) digital certificates. As we'll see below, anyone (even you!) can be a CA, though certain CAs are widely trusted on the Internet and their certificates are accepted by browsers that support them.

Most widely-recognised certificate authorities charge a fee for issuing certificates. Examples are Comodo GoDaddy, DigiCert and GlobalSign. Some have free trial certificates with short validity period (e.g. 30 days). There are also some free services, including Let's Encrypt. In practice, this is where you would get a certificate for a production server. For the purposes of this lab, however, we will generate our own.

Getting a signed certificate

Become a Certificate Authority yourself

To be a CA, you create a public/private key pair and issue yourself with a certificate containing this public key. This cert will be self-signed, meaning you'll be signing it with your private key.

  • Launch Linux or other environment that has OpenSSL installed and start a terminal session. In you do not have Linux on your own machine, you can create an AWS EC2 instance and connect to it using SSH.

  • We first create a 2048-bit RSA private key for the CA

    openssl genrsa -out myca.key 2048

    The key is created in PEM format, which is printable:

    cat myca.key
  • Now use this to create a certificate signing request (CSR) for the certificate authority (so the CA can sign its own key):

    openssl req -new -key myca.key -out myca.csr

    You will be prompted for identification data for the subject of this signing request. Make up an identity for your "pretend" CA (e.g. Highly Secure Certificates Ltd, or suchlike).

  • Now, as CA, use your private key to issue a (self-)signed certificate based on the CSR:

    openssl x509 -req -trustout -signkey myca.key -in myca.csr -out myca.crt

    The resulting certificate is valid for just 30 days (default). If you would like it to last longer (e.g. a year), run again with the additional option -days 365

Create keys and a certificate signing request for an application (e.g. web server)

  • We now create a 2048-bit RSA private key for the web server
    openssl genrsa -out webserver.key 2048
  • Now use this to create a certificate signing request for the web server:
    openssl req -new -key webserver.key -out webserver.csr
    You'll be prompted for identification data for the subject of the signing request. Make up an identity for your web server here.

Get your own CA to create and sign the web server certificate

  • Get the CA, using its private key, to sign the server’s CSR file:

    openssl x509 -req -CA myca.crt -CAkey myca.key -CAcreateserial -in webserver.csr -out webserver.crt

    You now have a web server public key, signed by a third party (the CA), in the file webserver.crt. You also have the web server's private key in the file webserver.key that you generated above.

  • (Optional) You could alternatively have got your web server to self-sign its certificate. The command for this is:

    openssl x509 -req -in webserver.csr -signkey webserver.key -out webserver_self.crt

    The next step is to configure your web server to be able to locate these keys.

Hapi/Node TLS Configuration

You can configure a Hapi.js server to listen for HTTPS connections.

  • Make a directory (called private, for example) in a Hapi application and copy your webserver.key and webserver.crt files there. You may use an application you are working on, or download the very basic application provided here.

  • Add the following lines before adding a server connection:

    const fs = require('fs');
    var options = {
      port: 4443,     //  or any port you wish
      tls: {
          key: fs.readFileSync('private/webserver.key'),
          cert: fs.readFileSync('private/webserver.crt')
      }
    };
    
  • Then pass options as a parameter to server.connection():

    server.connection(options);
    
  • Next start the server

    node hapi_tls_example.js
  • Test that it’s working by pointing a web browser to https://localhost:4443 (replace localhost if published elsewhere).

  • Replace the reference to webserver.crt with webserver_self.crt as created in step 2, restart node and browse again to https://localhost:4443. Have a look at the certificate trust chain and see the difference.

Nginx TLS Configuration (e.g. on AWS EC2 instance)

Here, we similarly configure a Nginx server to listen for HTTPS connections.

Create an nginx web server

  • Create an EC2 instance and connect to it with SSH/PuTTY.

  • Install nginx, enable at startup, and start service.

    sudo yum -y update
    sudo yum –y install nginx
    sudo chkconfig nginx on
    sudo service nginx start

    You should now be able to browse to the public IP address of your instance and see the nginx welcome page.

Configure TLS on nginx

  • Create a directory for the web server certificate and private key you created in part A above. You will need to copy them to your EC2 instance using SCP/WinSCP unless you wish to create new versions.

    sudo mkdir /etc/pki/nginx
  • Copy the certificate and key files to this location

    sudo cp webserver.key /etc/pki/nginx
    sudo cp webserver.crt /etc/pki/nginx
  • Now edit the nginx configuration:

    sudo nano /etc/nginx/nginx.conf
  • Scroll down to the bottom of the file and find the section that begins with:

    # HTTPS server
    
  • Nginx supports having more than one website enabled at the same time, which in effect means listening on a different port for each one. By default, one site is configured to listen on port 80. Your objective is to enable another site to listen on port 443.

  • To do this, uncomment the entire section under this heading

      # Settings for a TLS enabled server.
      #
      server {
          listen       443;
      etc…
    
  • and make the two changes shown here:

         ssl_certificate      /etc/pki/nginx/webserver.crt;
         ssl_certificate_key  /etc/pki/nginx/webserver.key;
    
  • Finally, restart nginx:

    sudo service nginx restart

Test it

  • Edit your instance's security group to allow incoming connections to port 443.

  • Enter the following in a web browser:

    https://ip_address
    (replacing ip_address with your instance’s Public IP address)

  • You'll get a warning as your certificate is signed by a CA that your browser doesn't trust. You should be able to view the certificate and verify that it's the one that you created.

(Optional) Configuring cipher suites

  • Using Wireshark or equivalent, capture the setup of a TLS session from a browser to a web server. It should start with a "Client Hello" message, something like that shown here: Tracing establishment of TLS connection

  • Trace the TLS handshake. What TLS version is used? What Cipher Suite does the browser prefer? Does the server match this? Try a variety of browsers and web servers and see if you can find examples where they don't match.

  • On your Nginx configuration, edit the nginx.conf file to force browsers to downgrade from TLSv1 and describe what happens. Try a variety of browsers and see if they all treat this in the same way.

  • Re-enable TLSv1 and now see what happens when you change the supported cipher specs to something different, and see how various browsers respond. You'll need to change the line in the nginx SSL configuration that begins with ssl_ciphers. Try for example to change it to: RC4-MD5

  • Note that you can also force browsers to only use an up to date version and/or cipher – e.g. TLSv1.2. Are there any disadvantages in doing this? There are various test suites available that allow you to try old browser versions – e.g. https://crossbrowsertesting.com or https://www.browserling.com