« A PHP bug... and a conversion | Main | A book you should read »

Installing your own CA authority to force client authentication

I wrestled a while today with client authentication with CA Certs. Setting up a CA and signing certs with a CA is a little trickier than doing cert self-signing. Here's some details.

I ran into a lot of caveats doing this for the first time, so I thought I might record this for posterity. First, we have two servers. In this case, the whole point of this is an added layer o f security for our server, we'll call it goliath. It has a web service running via apache, and we only want server david to access that service. We can do it with IP filtering, of course, but we think down the road we may want to allow a different server access, and we want to add an additional layer of security even if someone somehow defeats an filtering in place.

A more likely reason to do this might be that you want to authenticate a browser. We're not going to go into that, because I did not need to install the client cert on the browser, but a lot o f this will still be applicable.


  • Apache 2 with mod_ssl
  • openssl
  • client host has openssl as well, and our goal is to get wget working

Step 1: Create a CA

You can edit your openssl.cnf if you like. Alternately, you can plan to specify the -config flag with a lot of your openssl commands. In my case, I'm using what I think was the default, which con tains this line:

[ CA_default ]

dir             = ./demoCA            # Where everything is kept
So I did:
# mkdir -p /etc/ssl/goliath
# cd /etc/ssl/goliatch
# /usr/share/ssl/misc/CA -newca
This should create a directory matching you "dir" setting, like demoCA. Contents:
cacert.pem  crl        index.txt.old  private  serial.old
certs       index.txt  newcerts       serial
Roughly. That's my contents after signing a few certs. Over to the david box, we need to generate a key and a csr:
# openssl genrsa -out david.key 1024
# chmod 400 david.key
# openssl req -new -key david.key -out david.csr
It's unimportant at the moment where you're issuing those so long as you remember. Now, you copy the csr over to goliatch. For example:
# scp david.scp root@goliath:/var/tmp
Back on david, we now need to sign the csr with our CA key. This is where things are totally different from the self-signing routine many have probably done with an ssl cert in the past. On goliath:
# cd /etc/ssl/goliath
# openssl ca -startdate YYMMDDHHMMSSZ -policy policy_anything \
 -out /var/tmp/david.crt -infiles /var/tmp/david.csr
Caveat: I put in -startdate here. Why? When I did this, I was testing immediately, but I had one server on GMT and the other on Mountain time. Before I did this, the MST server actually th ought the "valid from:" date was in the future. So when I did it over, I made sure to specify a startdate one day in the past. The actual formula for the UTC date code would be, for example, for Jun 14th 2005 at noon: 050614120000Z Back on david, we retrieve the cert:
# scp root@goliath:/var/tmp/david.crt .
Ok. Now, we need to configure our server. Wherever applicable (in my case, it was in conf.d/ssl.conf and in conf.d/goliath.domain.com.conf), we need to set up these lines:
SSLEngine On
SSLVerifyClient require
SSLVerifyDepth 5
SSLCACertificateFile /etc/ssl/goliath/demoCA/cacert.pem
Those all don't necessarily need to go together, but do put the SSLVerify lines directly under SSLEngine On. Now, issue an "apachectl graceful" to get that config going. At this point, back on david, you should be able to do the following:
# cd /etc/ssl/client
# openssl s_client -connect goliath:443 \
  -cert ./david.crt -key ./david.key
Please note this assumes that GOLIATH already has a valid ssl certificate that is verified by your default CA. You may need to do:
# openssl s_client -connect goliath:443 \
  -CAfile /path/to/ca/bundle.ca \
  -cert ./david.crt -key ./david.key
If goliath's certificate is self-signed; you just slap the cert onto the bundle.ca so you can recognize it as a client. At this point, you should be able to wget a file, too:
# wget --certificate=/etc/ssl/client/david.crt \
  --private-key=/etc/ssl/client/david.key \
  --ca-certificate=/path/to/ca/bundle.ca \
I've put the bundle thing in there; again, you won't need the --ca-certificate flag if goliath's server ssl cert is signed up a CA recognized in the wget default CA bundle. Now, more importantly, if you remove the --certificate and --private-key parts, that wget should FAIL, giving you an error something like this:
OpenSSL: error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert
handshake failure
Closed fd 3
Unable to establish SSL connection.
Because, of course, goliath rejects you if you don't have that client cert it has signed. Anyhow, that's it. Hope someone finds that useful, as I had to waste a couple hours figuring out all the ins and outs.


TrackBack URL for this entry:

Listed below are links to weblogs that reference Installing your own CA authority to force client authentication:

» my Feast For Crows a Feast for Crows from Last Blog Standing::MattWallace.net
Long have I awaited the coming of that which was foretold... George RR Martin's Feast For Crows. Yes, it's the 4th book in the series, and it is like crack. I want that book. But UPS lost it. It shows... [Read More]

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)