Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium


Looking for Client SSL Certificate information

Posted on 2008-10-17
Medium Priority
Last Modified: 2013-12-06
I'm looking for some direction on setting up client certificate security. I know about setting up server SSL for HTTPS, which is related but not quite what I'm looking for.

A while back (10 years ago), I did some payroll processing using ADP and their web page interface to enter payroll information. When I signed up with them, they sent me a personal certificate I had to install on my computer/web browser which was part of the login process. If I didn't have that installed, it didn't matter if I had the right username and password at all, it would refuse to let me in. (This is discussed at https://support.adp.com/front/security.htm)

That's security is what I'm trying to set up now at a company I'm working for. I have an Apache web server on Fedora. I'm assuming the steps would be to create individual certificates for each user (only 5 people, not a big deal), then somehow with the PHP code for my login process, test for the presence of their personal certificate on the client end (not just any certificate, but one that was assigned to them I assume).

Can anyone point me to a resource that talks about this process, or any leads?

Question by:ktwdallas
1 Comment

Accepted Solution

zerodollars earned 2000 total points
ID: 22743555
Pulled this one from the ittoolbox website. http://it.toolbox.com/blogs/securitymonkey/howto-securing-a-website-with-client-ssl-certificates-11500

Someone has been bugging me to write this up since I figured it out and figuring it out has been a thorn in my side for a couple years.

About a week ago, things just clicked and I figured it out.

This isn't authentication in the classical sense; you are just saying which SSL certificates (that you have signed) you would like to be able to access a particular site.

This is in no way a new thing; there is just little to no usable documentation on how to accomplish this. The impetus for finally tackling this problem was setting up an internal wiki for use at $work that we can control who will be able to see it since our department is privy to info that shouldn't be available to all.

The interesting thing here is that a few of us are road warriors and need access to everything from anywhere meaning IP based ACL's are out and plain authentication is easy to grab on the wire (well not really but it is safer to assume it is by default). The best of both worlds would be to use some sort of decent encryption combined with a filtering mechanism which will allow us to say who we like versus who we don't.

The solution is signed certificates under SSL (in this case Apache's mod_ssl) as the distinguished name of an SSL certificate is perfect for filtering and the SSL handshake is about as secure as you are going to get on the wild wild web (assuming the server and client keys are trusted and not self signed but more on that later).

I first tried this using FreeBSD for this though getting and installing packages for PHP+Apache+MySQL+Mediawiki was far too much inconsistency for me to handle. There were too many package based (too lazy to compile source in the ports tree) inconsistencies so I just gave up and went to my old faithful OpenBSD. This should also work on any other OS with mod_ssl but considering I wanted to setup a Mediawiki I had significant overhead.

There are a few steps needed to get this going.
They are:
1) Setup openssl for use in being a certificate authority.
2) Create a root certificate authority key.
3) Create a web server key signed by your certificate authority's key.
4) Setup apache.
5) Create client certificates.
6) Install client certificates on hosts.
7) Profit.

This was pretty much hit and miss and I took info from all over the Internet. Unfortunately no one place gave me enough information to hang myself so I stole bits and pieces from each one until it worked. Get yourself a nice large caffeinated beverage... if you make mistakes you'll need the concentration and tenacity to work it out.

The very first thing to do here is to setup your openssl.cnf file (in OpenBSD /etc/ssl/openssl.cnf). Setup your root common name, organization and all that jazz. Don't skip this or use defaults since the distinguished name is our filtering mechanism.

Now we setup openssl to recognize us as the default certificate authority.

[ ca ]
default_ca = foo

Openssl will now look for a section called foo with the various knobs needed.
[ foo ]
dir = /etc/ssl/private
database = $dir/index.txt
serial = $dir/serial
private_key = $dir/ca.key
certificate = $dir/ca.crt
default_days = 3650
default_md = md5
new_certs_dir = $dir
policy = policy_match

After you've set the defaults and tuned the various other knobs to your liking you need to define the policy (specifically 'policy_match').
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = match
commonName = supplied
emailAddress = optional

This just says that the country, state, organization and unit names must match if the certificate is valid. The commonName is supplied in the certificate you receive and email address on the certificate is optional. For my purposes here, this is enough as I'm going to allow anyone issued a key for my department access to the department wiki.

Because I'm lazy I chose 10 years as the default key validity period. You can choose this to be anything you want with the caveat is the longer the period the less work for you (and the less secure your keys are).

Now OpenSSL knows about our CA (even though the keys haven't been generated or signed yet) so now we move on to creating the root Certificate Authority key.

# Generate the key.
openssl genrsa -out private/ca.key
# Generate a certificate request.
openssl req -new -key private/ca.key -out private/ca.csr
# Self signing key is bad... this could work with a third party signed key... registeryfly has them on for $16 but I'm too cheap lazy to get one on a lark.
# I'm also not 100% sure if any old certificate will work or if you have to buy a special one that you can sign with. I could investigate further but since this
# service will never see the light of an unencrypted Internet see the cheap and lazy remark.
# So self sign our root key.
openssl x509 -req -days 3650 -in private/ca.csr -signkey private/ca.key -out private/ca.crt
# Setup the first serial number for our keys... can be any 4 digit hex string... not sure if there are broader bounds but everything I've seen uses 4 digits.
echo FACE > private/serial
# Create the CA's key database.
touch private/index.txt
# Create a Certificate Revocation list for removing 'user certificates.'
openssl ca -gencrl -out /etc/ssl/private/ca.crl -crldays 7

This is your root certificate authority key. I would suggest giving it a nice strong password and not storing it on the webserver or network the webserver it is on (of course 99% of us will not heed that warning but hell for completion's sake it is healthy to mention).

All this effort is useless if we don't have a web server so let's create a key and sign it with our CA's key.

# Create us a key. Don't bother putting a password on it since you will need it to start apache. If you have a better work around I'd love to hear it.
openssl genrsa -out private/apache.key
# Take our key and create a Certificate Signing Request for it.
openssl req -new -key apache.key -out apache.csr
# Sign this bastard key with our bastard CA key.
openssl ca -in private/apache.csr -cert private/ca.crt -keyfile private/ca.key -out private/apache.crt

4) Now we've got our CA and web server's key let's setup apache to deal with it, so let's get over the Apache SSL boiler plate.

< IfDefine SSL >
AddType application/x-x509-ca-cert .crt
AddType application/x-pkcs7-crl .crl
< /IfDefine>

< IfModule mod_ssl.c >
SSLPassPhraseDialog builtin
SSLSessionCache dbm:logs/ssl_scache
SSLSessionCacheTimeout 300
SSLMutex sem
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
SSLRandomSeed startup file:/dev/arandom 512
SSLLog logs/ssl_engine_log
SSLLogLevel info
< /IfModule >

Blah, ugly ugly stuff but once you understand it is not so bad.
Now let's setup the rest.

# Mon deus, I'm a lazy bastard.

< VirtualHost _default_:443 >
DocumentRoot /var/www/htdocs.ssl/
# ServerName must be the same as the CommonName given for the web server certificate.
ServerName foo.foo.net
ServerAdmin root@foo.net
ErrorLog logs/error_log
TransferLog logs/access_log
SSLEngine on
# Remember the apache certificate we created? Well let apache know we can use it.
SSLCertificateFile /etc/ssl/private/apache.crt
# Only necessary if the key is not supplied in ther certificate.
SSLCertificateKeyFile /etc/ssl/private/apache.key
# CA Validation!
# Remember only certificate is needed not the KEY.
SSLCACertificateFile /etc/ssl/private/ca.crt

# REQUIRE valid certificates from clients.
SSLVerifyClient require
# FakeBasicAuth... if cert is good, auth is granted.
SSLOptions +FakeBasicAuth +ExportCertData +CompatEnvVars
CustomLog logs/ssl_request_log \
"%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"

# Depth is 1 because our root is a self signed cert, increase for the number of certs in the chain (ie. &gt2 for purchased certificated that were not self signed).
SSLVerifyDepth 1

# Note we can change the rules for any Location, Directory, URL we wish.
# This is very flexible as we can allow some types of certificates in one place
# but not in others.
&ltLocation /wiki>
# Let's also provide a list of the certificates we want to purposely deny.
SSLCARevocationFile /etc/ssl/private/ca.crl

# Time for the Authentication filtering mojo...
SSLRequire ( %{SSL_CIPHER} !~ m/^(EXP|NULL)-/ and %{SSL_CLIENT_S_DN_O} eq "Foo Net" and %{SSL_CLIENT_S_DN_OU } in {"Department of Department Creation"} )


This is kind of weird here so I'll break each of these down.
%{SSL_CIPHER} !~ m/^(EXP|NULL)-/ &lt--- Certificate must not be empty or expired.
%{SSL_CLIENT_S_DN_O} eq "Foo Net" &lt--- The organization string in the signed certificate's distinguished name must be "Foo Net"
%{SSL_CLIENT_S_DN_OU} in {"Department of Department Creation"} &lt-- The organizational unit string in the signed certificate's distinguished name must bee "Department of Department Creation"

The SSLRequire line states that this statement must evaluate to true if access will be granted. Note this is a chained and meaning all or nothing and since I'm only validating two parts of the certificate (because I'm lazy) we let OpenSSL deal with policy matches. This is terrible for the Internet so you should be checking the common names (ie. names of people you assigned certs to and setup a revocation list for then they are no longer welcome).

Start up apache (make sure to set httpd_flags="-DSSL" in /etc/rc.conf for OpenBSD) via apachectl startssl.
Check for errors and make sure the damned thing is running. You won't have access to https://server/wiki because the SSL conversation won't work (since you haven't a corresponding valid certificate in your browser yet).

Now let's create some certificates for our clients/users/losers/friends/random folks...

# The base of where our SSL stuff lives.
# Were we would like to store keys... in this case we take the username given to us and store everything there.
mkdir -p $base/users/$1/

# Let's create us a key for this user... yeah not sure why people want to use DES3 but at least let's make us a nice big key.
openssl genrsa -des3 -out $base/users/$1/$1.key 1024
# Create a Certificate Signing Request for said key.
openssl req -new -key $base/users/$1/$1.key -out $base/users/$1/$1.csr
# Sign the key with our CA's key and cert and create the user's certificate out of it.
openssl ca -in $base/users/$1/$1.csr -cert $base/ca.crt -keyfile $base/ca.key -out $base/users/$1/$1.crt

# This is the tricky bit... convert the certificate into a form that most browsers will understand PKCS12 to be specific.
# The export password is the password used for the browser to extract the bits it needs and insert the key into the user's keychain.
# Take the same precaution with the export password that would take with any other password based authentication scheme.
openssl pkcs12 -export -clcerts -in $base/users/$1/$1.crt -inkey $base/users/$1/$1.key -out $base/users/$1/$1.p12

Wow now you have signed certificates whose distinguished names will match your CA and Web's keys.
Now you have to get these things to your clients to use them. As well keep in mind that the .p12 file for each user contains both the public and private keys so if your user doesn't keep that key safe and use a decent export password then this certificate is about as good as writing an all access password with a sharpie on a post-it for display in a public place.

On the inverse when you need to remove a certificate's access you can prematurely expire the certificate by adding it to the Certificate Revocation List

# Revoke a certificate and update the CRL.
# Revoke a particular user's certificate.
openssl -revoke $base/$1/$1.pem
# Update the CRL with the new info from the database (ie. index.txt)
openssl ca -gencrl -out $base/ca.crl -crldays 7

Send the username.p12 to clients. Don't bother sending the other bits. They are more useful to you should the user lose the thing.

FireFox Setup For Clients:
Go into preferences.
View Certificates.
Enter master password for FireFox (if you don't have one set one here otherwise stolen laptop = easy access).
Enter in the export password given to you by the dude who created your cert.
Hit OK like a mad man.

Go to site and it will ask you if you want to send the certificate in question (look at the distinguished name to make sure it matches what you expect it to).

Now you have access!

Safari Setup For Clients.
Open a shell.
open username.p12
Keychain access will open up and ask you what chain to import into... choose 'login' and enter export password.
Now safari should work!

Now go talk and wiki with your encrypted cohorts in an encrypted fashion to all your hearts content.

I didn't go through each of the SSL user input steps since I figured this audience can figure it out. If not let me know and I'll be even more verbose on the topic.

It would be really nice to get this working with valid third party signed keys thereby making this safe for the Internet at large and thereby open to a setting up a private wiki/forum/whatever for a small group of friends (ie. SSLVerifyDepth &gt1). Self signed keys are bad; really bad; since they can be spoofed easily (ie generate a new self signed certificate and MITM the hell out of the site since one invalid certificate is as good as any other). Until someone has figured out of a nice paid certificate will work then please don't use this on the Internet at large... yeah cheap certs are still largely 'unsafe' but are leagues safer than free self signed ones.

On second thought I don't think this will be possible without an inordinate amount of money as I just realized most CA's would dissalow keys to sign others as then you wouldn't need to pay them after the first key.

If you need to know how to check to see if your key can sign others:
openssl x509 -in third_party_issued_key.pem -noout -text
Look for this:
X509v3 Basic Constraints: critical

CA:FALSE means no deal. We will have to live with our insecure self signed keys. So be wary and not use this stuff on the Internet at large (or at least for anything you truely care about).

With that given, my future work will have be to figure out a way to make this easier and less of a hackish mess. As well some time should be spent to make the SSLRequire statement check more of the fields in the distinguished name. More fields checked makes for slightly increased certainty.

You can make it even better by using htaccess info and the keys for a cheap/crappy two factor authentication. The biggest benefit on the Internet at large would be for corporate Intranet sites that restrict what users can see on the web server in question without having to give them new tokens, passwords, etc. Pretty flexible if you ask me.

Questions or comments?
Something I missed perhaps?

Featured Post

Evaluating UTMs? Here's what you need to know!

Evaluating a UTM appliance and vendor can prove to be an overwhelming exercise.  How can you make sure that you're getting the security that your organization needs without breaking the bank? Check out our UTM Buyer's Guide for more information on what you should be looking for!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

It is possible to boost certain documents at query time in Solr. Query time boosting can be a powerful resource for finding the most relevant and "best" content. Of course the more information you index, the more fields you will be able to use for y…
If you are a web developer, you would be aware of the <iframe> tag in HTML. The <iframe> stands for inline frame and is used to embed another document within the current HTML document. The embedded document could be even another website.
How to Install VMware Tools in Red Hat Enterprise Linux 6.4 (RHEL 6.4) Step-by-Step Tutorial
Exchange organizations may use the Journaling Agent of the Transport Service to archive messages going through Exchange. However, if the Transport Service is integrated with some email content management application (such as an anti-spam), the admin…
Suggested Courses

581 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question