Creating your own Apache SSL Certificate Signed by your Root CA

Posted by Christopher Wojno Sat, 08 Nov 2008 23:17:00 GMT

This article is part 2 of Going SSL with Your Own Root CA

Putting your Root CA to work

Now that you have your own Root CA, it doesn’t do anyone any good unless you use it. You use it by creating certificates for services. The rule is one certificate per-IP address. If you need additional certificates, you should have an IP address for each certificate. Any domain (or sub-domain, etc.) listed in the Certificate’s CN (Common Name) should resolve to its own unique IP address. Now, nothing is set in stone. But that’s the way it should be done. I’ve used multiple certificates for different domains on the same server using Virtual Hosts (same IP) before, but Apache warned me every time I started the server that this is bad practice. If you’re using Apache, it will still work, but you’ve been warned (again).

Disclaimers

This guide is for amateur, non-production use only. I make no warranties or guarantees as to the correctness of this document. This guide involves escalating privileges and may cause irreparable damage if used improperly. You use this document at your own risk and I hereby disclaim any responsibility or liability for your actions or non-actions.

Assumptions

I’m assuming you’re using Apache 2.X with mod_ssl already installed. I’m also assuming you’ve already set-up a vhost file with the site you want to SSL already created and configured minus the specification of the server certificate, and trust chain. I realize this is a lot to assume. This can help you setup an Apache server with SSL.

Creating a Server Key

Just as with the Root CA key, you need a server key. Now, if you already have one, relax, skip this step (though I still urge you to create and migrate to the “secure” environment created in the next step). You can re-use your server key as many times as you like until you want to upgrade the private key quality (or the key is compromised). If you have not created a server key, you’ll need to do so now.

We’ll use OpenSSL again, but first, let’s create a safe place for our key. The key needs to be readable by the server upon start-up (meaning, the key cannot be encrypted) or Apache (or any other server) will be unable to use it without first asking you for a password. If you don’t mind typing it in each time, that’s your prerogative. However, for live or even development environments, it’s simply impractical.

Just as with the Root CA key, you must keep this server key confidential as well. If it is breached, anyone can masquerade as your web server; thus defeating the purpose of the certificate. If you need to move the key, you are advised to use OpenSSL to encrypt it first, however, that is a tutorial for another day.

Create the “secure” environment

Create a comfy place for your key. I like to put it in /etc/apache2/ssl with Gentoo, but the better place is likely /usr/local/etc/apache2/ssl and if you use Windows: there are no rules for file placement. With that: create your directory:

sudo mkdir /usr/local/etc/apache2
sudo mkdir /usr/local/etc/apache2/ssl
sudo chmod 0770 /usr/local/etc/apache2/ssl
sudo groupadd apache_ssl_admins
sudo chown :apache_ssl_admins /usr/local/etc/apache2/ssl
sudo usermod -a -G apache_ssl_admins YOURUSERNAME # varies by OS

Here’s what I’ve done:

  1. Created an apache2 directory (if it didn’t exist, if this call fails, just make sure the directory is there)
  2. Created the SSL directory where we’ll store the server key
  3. Changed the permissions for the directory such that only root can access it
  4. Created a group called “apache_ssl_admins”
  5. Changed the ownership of the new directory to allow members of the apache_ssl_admins access
  6. Added you to the new group called “apache_ssl_admins”, note that this step may vary among Operating Systems so modify this command as required.

You’ll need to log out and log back into your server for the group membership to take effect.

Now, I’ve put the word “secure” in quotes as if anyone were to compromise the server and gain access to the apache_ssl_admins group, your key is then accessible. Your key is only as secure as your server.

Once you’ve logged back in, cd to the newly created “secure” environment.

cd /usr/local/etc/apache2/ssl

Creating the server key

You will now generate a server key. I highly recommend that you study the documentation for OpenSSL. It’s very very rough, but it will help you understand the commands you’re entering. It may be necessary to update these commands as machines become faster and the encryption level becomes insufficient.

openssl genrsa -out www.pem 2048

This generates a new file called www.pem in your “secure” environment. This is an RSA private key with a bit length of 2048. This bit-length is considered to be good by today’s standards. You may use any power of two; though, be warned, larger numbers require more time to initiate SSL connections. Smaller numbers will result in a less secure SSL handshake (and vicariously a less secure SSL session).

Again, it is a good idea to encrypt this key with 3des. I’ve not done so to facilitate this demonstration. Please see the previous tutorial for instructions about encrypting your server’s private key. The instructions for encrypting your Root CA private key are applicable to this purpose.

Generate a Certificate Signing Request

The Certificate Signing Request (CSR) is used by Root CA’s to create certificates for people who need them. A CSR is a uniform way to telling Root CA’s exactly what the certificate should say. The Root CA merely signs the CSR and viola: a certificate. Well, it’s not all that simple. The Root CA also adds an expiration date to the signature or other fields deemed necessary or desired. But before it can be signed, the CSR must be generated by using the server’s key:

openssl req -new -sha1 -out www.csr -key www.pem

This command will create a file called www.csr in your “secure” environment. This file is a Certificate Signing Request. You may expose this file publicly, though that is not required if you’re signing it yourself. This new request requires that it be identified using the SHA1 hashing algorithm. This algorithm is safer than MD5 as it has been proven that MD5 collisions can be generated in a reasonable time.

You will be asked a series of questions after running this command. Answer them honestly. When you see the CN (Common Name), enter the domain name and subdomain for your certificate. I.E. for my domain: christopher.wojno.com, use as the CN: “christopher.wojno.com”

Sign your CSR and create a real server Certificate

Now that you have a CSR, we’ll use our Root CA certificate and key to sign the CSR. This creates a certificate you can use in your Apache server (or other SSL-capable web server). If you created your Root CA using my previous article, then you may use the paths I have specified here. Otherwise, you’ll need to adjust the -CA, -CAkey and -CAserial parameters to match what you have used.

openssl x509 -req -days 365 -in www.csr -CA /usr/local/etc/certificate_authority/certs/cacert.crt -CAkey /usr/local/etc/certificate_authority/private/cakey.pem -CAserial /usr/local/etc/certificate_authority/serial.srl -out www.crt

This is a lot to swallow in one step. This used to be where I became completely lost with regard to SSL Certificate generation. The command is very complex so I’ll break it down part by part:

  • We’re asking OpenSSL to do something
  • Specifically, we’re asking it to invoke it’s x509 library (SSL certificate chains)
  • We’re then specifying that our input will be a CSR (-req)
  • We want the resulting certificate to be valid for 365 days (-days 365) from date of signing
  • The CSR is the input (-in www.csr)
  • We’re using the CA public certificate created in the previous tutorial: (-CA /usr/local/etc/certificate_authority/certs/cacert.crt)
  • We’re using the CA’s private key created in the previous tutorial to sign this CSR: (-CAkey /usr/local/etc/certificate_authority/private/cakey.pem)
  • We’re using the CA’s serial file to mark the resulting certificate: (-CAserial /usr/local/etc/certificate_authority/serial.srl)
  • Finally, we’re outputting a new certificate for the web server: (-out ../www.crt)

That little command does quite a bit. After running it, you will be prompted for the CA Root private key password (if you created one as I recommended that you do). It should automatically increment the serial file (serial.srl) so that future certificates do not have the same serial number used to identify certificates.

Now we have an SSL certificate for your server located in /usr/local/etc/apache2. Time to tell Apache where it is.

Telling Apache about our certificate

This next step takes place in the Virtual Host configuration file (or in your httpd.conf if your OS distro has not broken that file up yet). The true location of this next step can only be described in a universal fashion by describing the purpose: website configuration. Locate that file. In Gentoo, it’s located at: /etc/apache2/vhosts.d/00_default_ssl_vhost.conf I am unsure as to its location on other operating systems.

Once you’ve located this file, you’ll need to edit it (this may require privilege escalation). Edit this file in your favorite editor (or least favorite, there is no favorite requirement).

Add or amend the following lines in the VirtualHost section of the website you wish to secure using the new SSL Certificate. The hostname of this virtual host must match the CN name you specified when you created the CSR for this site.

SSLCertificateFile /usr/local/etc/apache2/ssl/www.crt
SSLCertificateKeyFile /usr/local/etc/apache2/ssl/www.pem
SSLCertificateChainFile /usr/local/certificate_authority/certs/cacert.crt

This tells Apache:

  1. Where our certificate can be accessed so that the server may present it to requesters
  2. Where the server’s private key file is so that the certificate may be used to decrypt encrypted requests from requesters (these requests are encrypted using the certificate in step 1)
  3. Where the certificate authority’s certificate is located so that Apache is able to append it to the certificate presented in step 1 (to avoid having the user’s browser look it up first)

Again, I’m assuming that you’ve already set up Apache to use SSL:

  1. You’ve installed mod_ssl
  2. You have enabled it in the /etc/conf.d/apache2 file
  3. You’ve allowed access to port 443 through all firewalls (or other port you wish to use for SSL)
  4. You’ve configured your Apache instance to listen on the SSL port

If you’ve not done so, Apache will not understand the SSL commands and will not start.

If all goes well, when you access the website at the CN name on your certificate and use the https protocol (i.e. https://CN where CN is the name used when you were asked for the Common Name when generating the CSR), you will see your website and will not be prompted to accept the authenticity of the certificate. Again, I assume you’ve completed the previous tutorial and have installed your own Root CA (used to sign this certificate) into your machine’s trusted Root certificates.

Should something go wrong

Debugging OpenSSL certificate problems is tricky and complicated. OpenSSL provides a tool for doing so over the network. I used it extensively during my first attempts at creating my certificates. Use:

openssl s_client -connect CN:PORT -debug

See the documentation# for more information. Be warned, this is a developer tool and comes with few instructions and brief explanations.

Posted in ,  | Tags , , , , , , , , , ,  | no comments

Going SSL with your own Root CA

Posted by Christopher Wojno Wed, 05 Nov 2008 04:06:00 GMT

The Prompt

Recently, my slew of SSL certificates expired for my site and e-mail. Like most people, I didn’t write anything down when I set it up. After all, I was just trying to explore what I could do with my server at that point. So, I decided to make my own Root CA for my site’s certificates. That way, my family and I can use my root certificate to verify the authenticity of my servers and services.

What are you talking about?

SSL Certificates. SSL/TLS is the secure socket layer. It uses the PKI (public key infrastructure) to provide both data encryption and confidentiality. You can review the Wikipedia article about PKI and SSL

Anywho, with a fully working SSL/TLS system in place, you can be relatively sure that your client-server (and server-server) communications are not only encrypted, but being held between the party with which you believe you are communicating. This means you can use your e-mail and web server on a public network and not worry about your password being sent in the clear or digested format. It also means you’re sure you’re talking with your servers and not someone pretending to be a gateway. It’s nice, but it’s much more complicated to set up.

One of the problems with self-signed certificates is that you are not sure if the certificate is authentic the first time you connect to the server. As I have SSL for my mail and https server, I’d have to independently verify each certificate via the SHA1 hash and identifying information to be absolutely sure my communications are secure.

Where does the Root CA Thingy come in?

A CA is short for “Certificate Authority.” A Certificate authority can sign certificates that you make. Because the CA is trusted, when the CA signs your certificate, it transfers that trust to your certificate. When you get your operating system (or your web browser or e-mail client), there are several Root CA’s pre-installed. They pay lots of cashey money to be there so that your browser, operating system, and e-mail client will recognize theirs and certificates they sign as trusted.

OK, so How do you do it?

Disclaimers

This guide is for amateur, non-production use only. I make no warranties or guarantees as to the correctness of this document. This guide involves escalating privileges and may cause irreparable damage if used improperly. You use this document at your own risk and I hereby disclaim any responsibility or liability for your actions or non-actions.

Overview

  1. Create a “proper” and “secure” environment for your CA private key and certificates
  2. Create a CA private key
  3. Create a CA Certificate Signing Request (CSR)
  4. Self-sign the CA CSR using the CA’s private key thereby creating a CA Certificate
  5. Install the CA Certificate locally

Create a “proper” and “secure” environment

Before we begin, we need a “secure” place to put our CA Root’s private key. We cannot let this fall into the wrong hands (or any hands other than ours).

When you see YOURUSERNAME, replace it with the username you use to access the system onto which you are creating the Root CA’s certificate.

sudo mkdir /usr/local/etc/certificate_authority
sudo groupadd ca_admins
sudo usermod -a -G ca_admins YOURUSERNAME
cd /usr/local/etc/certificate_authority
sudo mkdir certs
sudo mkdir private
sudo chmod 0770 private
sudo chmod 0775 certs
sudo chown :ca_admins private
sudo chown :ca_admins certs

You’ll need to log out and then log back in to properly join the ca_admins group. Be sure to cd to the /usr/local/etc/certificate_authority directory when you do so. The following steps assume that you are in that directory.

We’ll be generating the certificate’s private key. It’s important that it never be exposed. I highly recommend that you encrypt it now, when it’s being created, but in this example, I will not do so to facilitate this demonstration. I’ll indicate where you’ll need to put in the encryption.

I’ve created an environment that is relatively “secure.” That is, it is only as secure as your server. We will generate the private key in the private folder. I have designated this as readable only to root and members of the ca_admins group. I’ve also put commands in to create that group and have you join it.

When you create the CA private key, it will be placed under “private.” When the certificate is generated and signed, the resulting certificate will be placed in the certs directory. The only file that needs to be kept secret is the CA private key. We generate it in the next step.

The proper location for local configuration changes should be the /usr/local/etc folder. It’s a toss up as to whether or not a CA’s private key is a local configuration resource. It may be better to place all of this in the /var/local/certificate_authority. The serial file most assuredly belongs here (created later) as would a Root CA CSR configuration file (not used in this document). However, for the purposes of this document, we’ll keep it as is.

In this step, I’ve also created a certs directory and a private directory. Certificates that we create for the CA Root should be placed in the certs folder. The private key for the CA Root should be placed in the private directory, as it will not be world-readable.

The CA Private Key

I like RSA certificates, so I generate RSA server private keys.

openssl genrsa -out private/cakey.pem 2048

If you want to create an encrypted key, specify this:

openssl genrsa -out private/cakey.pem -des3 2048
and you’ll be asked to specify a password for your new key. Do NOT lose this password. You’ll never be able to get this key back. That means you can’t sign or create your certificate.

Create a CA CSR and Sign it (single step)

openssl req -new -x509 -key private/cakey.pem -out certs/cacert.crt -days 3600

This creates a 10-year (abouts) certificate signing request that is immediately signed by the CA’s Root private key to produce the certificate (certs/cacert.crt). We’ll be using it as our Root CA certificate. If you put a password on the key, you’ll be asked for that password again. You’ll also be asked a series of questions to identify your certificate. Answer them honestly. When you get to the CN field, do NOT enter your domain name or similar. Name it something that’s impossible to be a domain and does not conflict with another Root CA. I called my “Wojno CA Root” modeled after Apple’s certificate name: “Apple CA Root”.

You now have a real certificate. Now you need to install it locally onto every machine that will use a certificate signed by this Root CA Certificate. Yes, this is bothersome, but it sure beats having to specify an override for every certificate you generate. It also lowers the risk of accepting a bogus certificate by your users, in the event you miss one or more.

Installing the CA Certificate locally

This process varies between operating systems and/or browsers. Apple uses the Keychain. Windows has a root certificate listing in the Internet Options panel (accessible via the control panel). I have no clue where it would be in Vista. You’re on your own there. Once you have it installed, you’ll be able to create as many certificates and sign them and all will be automatically trusted because you now trust the Root CA Certificate that will sign them all. Special note: do NOT install stranger root certificates! This will pose a security risk if you go about installing root certificates willy-nilly.

Create the serial file

Back in your ca directory where you created the private and certs folders:

echo "01" > serial.srl
sudo chmod 0660 serial.srl
sudo chown :ca_admins serial.srl

Certificates issued by this CA will be imprinted with a serial number. Every time you sign a certificate, the serial file will be incremented and updated. Only ca_admins will be able to modify or read this file. While hiding the contents of this file is not absolutely paramount, the less that is known, the harder it is to break something (this is a generic statement and not necessarily true, but is usually true).

For use with Apache2’s mod_ssl, please see the next article in the series.

Posted in ,  | Tags , , , , ,  | no comments

Session of Fear

Posted by Christopher Wojno Fri, 10 Aug 2007 23:48:00 GMT

Now, before you think “FUD Time!”, I preface this post with the following warning: Yes, this is a problem with not just Rails but all sessions; no, it’s not unsolvable. So, in light of the bad news, there is much good news.

The Problem

Rails applications are very useful. However, most require sessions in order to be useful. Sure, you can provide an ActionWebService or REST-based service to not use sessions, but for the majority of user services, you’re stuck with sessions.

A Session About Sessions

(Go ahead and skip this part if you’re already familiar with how sessions work)

A session is a way a server remembers who you are (in case you didn’t know). The most common way of allowing a server to ID you, without knowing who you are exactly, is by sharing a secret! Well, it’s supposed to be secret and we’ll come to that in a bit. When a new session is created, your session ID is generated (it’s random, hopefully) and saved in the server. The id can be associated with other data, such as your name, or permissions. When you request a page, the server will give you this ID. You almost never see that ID because it’s sent in the background as a “cookie.” You’ve probably heard of these before. From now on, every time you request a page from the server, your cookie data is automatically included in the request (your web browser takes care of this) so the server can figure out who you are. Done correctly, no personally identifiable information is sent over the Web. Just your (hopefully) random ID.

Sounds good so far. But if you’re not using HTTPS (SSL/TSL encryption), your secret contained in that cookie can be seen when it is first sent, and when you access any page thereafter (because you’re sending it each time remember). So, if someone has access to your traffic (if you’re using a shared WiFi or a hub or have a shady ISP), they can pull out your secret. Here’s an old session I sniffed using a program called snort:

GET /javascripts/prototype.js?1186461045 HTTP/1.1..
Host: christopher.wojno.com..
User-Agent: Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.8.1.6) Gecko/20070810 Firefox/2.0.0.6..
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5..Accept-Language: en-us,en;q=0.5..
Accept-Encoding: gzip,deflate..
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7..
Keep-Alive: 300..
Connection: keep-alive..
Cookie: _session_id=db392fa5b39125fa7c1e581b9c1ec71d; is_admin=yes....

There it is, the last line. “Cookie.” So when my browser was trying to download some javascript libraries (Prototype is quite good), it sent my session ID. Now, the javascript doesn’t care about my session ID, but it was sent any way. If someone were to see that sent over the Internet (at any point on it’s way to the server), he or she could create a fake cookie with that ID and login as me without my password! (don’t try it, I’ve already logged out and reset the session) They could then, go into the settings and lock me out of my own system (until I change the password in the ruby console… that’ll show ‘em?). In the meantime, the damage has been done and it can happen again.

SSL!

I won’t go into SSL, but that little piece of technology has enabled credit card purchases over the Internet for years and will hopefully continue to do so for years to come. Any way, in short, it’s encryption and it will encrypt cookies too!

So, we need to force rails to encrypt cookies. Not as simple as it sounds. First, you need to setup an SSL certificate on your server. Easier said than done, though, some Apache packages come with the tools to automatically generate one. For a confusing, yet interesting read, see X509 on Wikipedia.

I assume you installed your SSL certificate and have configured Apache1 to run with SSL (or lighty, if you’re using that instead (mongrel is not mentioned as it does not have SSL, though there is a way to use SSL and mongrel together)). If not, there are loads of tutorials. I warn you though, it’s fairly technical.

Hold onto your Cookies!

So, you have SSL and a Rails application running (I’m assuming). How do you tell Rails to make sure your cookies are only sent via SSL? Rails lets you specify it.

Tell Rails to use SSL Only

By default, Rails does NOT enforce SSL on cookies or sessions (that would be frustrating for development, wouldn’t it?). So you have to enable that enforcement yourself. If you want your session cookie to be sent over SSL site-wide (and generally, you do!), head into your rails application directory and open (in your favorite editor) app/controllers/application.rb Add the following anywhere in the ApplicationController class:

class ApplicationController < ActionController::Base
  session :session_key => '_session_id', :session_secure => true
end

The ApplicationController class definition should already exist, don’t duplicate it. Also, make sure that session isn’t already specified. If it is, the important part here is ”:session_secure => true”. Rails will now tell the browser to only send the session cookie if the browser is using the https (SSL) protocol. This feature is poorly documented but hopefully this will help keep your applications that you write, safe. NOTE: You MUST use SSL if you enable this or your application will become extremely forgetful (Who are you? What are you doing in my kitchen?!).

If you’re interested in storing OTHER data (not overly recommended though due to this and another exploit) in other cookies, Rails offers cookie—manipulation (not really management, the API leaves something to be desired). You can tell individual cookies to only be sent over encrypted connections, just like the session cookie. The other exploit: if the computer is shared and the browser doesn’t clear out the cookies, the next person to sit at the computer can harvest the cookie information, so don’t store passwords, e-mail addresses… really anything in cookies, it’s just a bad idea. DO store worthless information you don’t want to save in your server, such as squirrel preferences.

Admittedly, generally the odds of someone having access to your network traffic (and your cookies, no! MY cookies!) directly is moderate. For the attacker to successfully pick out your cookie data from the slew of traffic wizzing by, he/she would have to be looking and looking for a specific cookie name. So make sure no one has it out for you.

I don’t mean to sound down on the Rails team. Dang-fine-job I say. Cookies aren’t really important and the session is cookie-based, so session security falls by the way-side. It’s up to all developers to keep his or her eyes open for potential pitfalls.

1_000.thank( Rails::DevTeam.members.collect{|m|m.email} )

1 Note: Apache doesn’t know how to run Ruby code as of this writing. You need to use an Apache’s mod_proxy. This will (after it’s been configured) then pass the requests from Apache, to mongrel, lighty, or… WEBrick (if you’re nuts)

Posted in  | Tags , , , , , , ,  | 2 comments