If you use rvm on OS X and compile rubies more often than “almost never” you should really have a ~/.rvmrc file that contains these two lines.
cpus=`sysctl -n hw.ncpu` rvm_make_flags="-j$cpus" |
As programmers there are two things we appreciate most when attempting to accomplish something: documentation and examples. Unfortunately, for some classes of problems trivial examples take shortcuts that, if implemented in production code, cause performance problems and security flaws. Let’s take a tour.1
Here’s how to do a SQL query in PHP (3 ways!)
$username = $_REQUEST['username']; mysql_connect($server,$user,$password,$db); mysql_query("SELECT * FROM my_table where username=$username"); //but that's deprecated as of PHP5.5...let's do it with mysqli $dbconn = mysqli_connect($server,$user,$password,$db); mysqli_query($dbconn,"SELECT * FROM my_table where username=$username"); //or maybe we want PDO $pdo = new PDO('mysql:host=example.com;dbname=database', 'user', 'password'); $statement = $pdo->query("SELECT * FROM my_table where username=$username"); |
Done! You know how to build SQL queries from query parameters now! Or…maybe not. Obviously each of these examples is potentially vulnerable to SQL injection. There are quite a few ways to solve this, but sample code rarely mentions that this code is utterly unsafe for production use because that would be “needlessly complicated”.
$username = $_REQUEST['username']; //deprecated as of 5.5 mysql_connect($server,$user,$password,$db); $safe_query = sprintf("SELECT * FROM my_table where username='%s'",mysql_real_escape_string($username)); mysql_query($safe_query); $dbconn = mysqli_connect($server,$user,$password,$db); $safe_query = sprintf("SELECT * FROM my_table where username='%s'",mysqli_real_escape_string($username)); mysqli_query($dbconn,"SELECT * FROM my_table where username=$username"); $pdo = new PDO('mysql:host=example.com;dbname=database', 'user', 'password'); $statement = $pdo->prepare("SELECT * FROM my_table where username=:username"); $statement->execute(array(':username' => $username)); |
There is definitely a higher cognitive load associated with figuring out what these snippets do, but it is not high enough to justify the problems caused by ignoring it. The people who most need training about the danger inherent in these methods are the ones who are Googling for examples.
So you need to open a TCP socket? Here’s one way in Ruby.
require 'socket' server = TCPServer.open(10000) loop { client = server.accept client.puts "At the sound of the tone the current timestamp will be #{Time.now.to_i}" client.puts "Tone!" client.close } |
Go ahead, telnet to it and you’ll see the message. Pretty cool right? Except you’re bound to every interface on your machine.
require 'socket' server = TCPServer.open("localhost",10000) loop { client = server.accept client.puts "At the sound of the tone the current timestamp will be #{Time.now.to_i}" client.puts "Tone!" client.close } |
There, now we’re binding to just a single interface. Many examples of this sort of trivial TCP server online neglect to mention that binding to all interfaces will implicitly make your server available to anyone with network access to your machine.2
Many popular MVC frameworks in Ruby share instance variables from their controllers into the views. Let’s build a trivial example.
class SomeController def method @my_data = "Hello World!" end end |
<!--our view--> <p><%= @my_data %></p> |
Pretty slick! Unless your MVC doesn’t auto-escape your instance variables (Rails 3+ does this by default, calm down). If it does not, then if your variable comes from user input you have a potential XSS vector! A better example depends on the available HTML sanitization methods that the framework you choose to use supplies, so this is perhaps an example of where the security of apparently generic code is in fact highly dependent on the framework it runs within.
Totally trivial: HMAC-SHA256 signatures in Ruby.
require 'openssl' key = "key" data = "data to sign" digest = OpenSSL::Digest::SHA256.new puts OpenSSL::HMAC.hexdigest(digest,key,data) |
Simple. Well, as long as you ignore RFC 2104 at least. If you look deeper (and why would you? The code above works great!) you’ll find the following:
The definition of HMAC requires a cryptographic hash function, which
we denote by H, and a secret key K. We assume H to be a cryptographic
hash function where data is hashed by iterating a basic compression
function on blocks of data. We denote by B the byte-length of such
blocks (B=64 for all the above mentioned examples of hash functions),
and by L the byte-length of hash outputs (L=16 for MD5, L=20 for
SHA-1). The authentication key K can be of any length up to B, the
block length of the hash function. Applications that use keys longer
than B bytes will first hash the key using H and then use the
resultant L byte string as the actual key to HMAC. In any case the
minimal recommended length for K is L bytes (as the hash output
length).The key for HMAC can be of any length (keys longer than B bytes are
first hashed using H). However, less than L bytes is strongly
discouraged as it would decrease the security strength of the
function. Keys longer than L bytes are acceptable but the extra
length would not significantly increase the function strength. (A
longer key may be advisable if the randomness of the key is
considered weak.)Keys need to be chosen at random (or using a cryptographically strong
pseudo-random generator seeded with a random seed), and periodically
refreshed. (Current attacks do not indicate a specific recommended
frequency for key changes as these attacks are practically
infeasible. However, periodic key refreshment is a fundamental
security practice that helps against potential weaknesses of the
function and keys, and limits the damage of an exposed key.)
Encryption and digital signatures are tricky business and the cryptographic primitives people use to accomplish their goals are just that, primitive. As a programmer, you will likely be required to implement some type of encryption and signing at some point. It is an unfortunate reality that unless you have some training, experience, or are fortunate enough to use great tools you will likely get it wrong. Even something simple like having insufficient key length for your chosen HMAC digest could have serious consequences and no example is going to tell you that.
require 'openssl' digest = OpenSSL::Digest::SHA256.new key = OpenSSL::Random.random_bytes(digest.digest_length) data = "data to sign" puts OpenSSL::HMAC.hexdigest(digest,key,data) |
Pretty simple change, but now the key is decent. In this case I’ve chosen to generate enough random bytes to cover the length of the chosen digest. Of course, you don’t actually want to be generating a new key for every signature so the complexity will be higher since a “real world” implementation would generate the key out of band, save it to disk, then load it as required.
First generate a key with sufficient length and entropy
require 'openssl' digest = OpenSSL::Digest::SHA256.new key = OpenSSL::Random.random_bytes(digest.digest_length) File.open("/some/path",'w') { |f| f.write(key) } |
Now HMAC your data
require 'openssl' digest = OpenSSL::Digest::SHA256.new key = File.read("/some/path") data = "data to sign" puts OpenSSL::HMAC.hexdigest(digest,key,data) |
Of course now this code can’t be executed without making at least some modifications for your own environment. Damn. Moving on.
Here’s a problem that’s more subtle than HMAC. AES 256 (via Ruby OpenSSL::Cipher3) using cipher block chaining (a very common4 block cipher mode). Again we’ll be using Ruby.
require 'openssl' enc = OpenSSL::Cipher::AES256.new(:CBC) enc.encrypt key = enc.random_key # let's avoid the pitfall from HMAC and generate a good key ct = enc.update("my encrypted text is extremely important") ct << enc.final # and for completeness, here's a decryption dec = OpenSSL::Cipher::AES256.new(:CBC) dec.decrypt dec.key= key pt = dec.update(ct) pt << dec.final puts pt |
There, a basic example of encryption and we’re using a good key. Unfortunately CBC requires two inputs for safe encryption, not just one. We’ve neglected the initialization vector (IV) so OpenSSL sets it to a 16 byte string of null characters (\0). This is very bad (and extremely common).
require 'openssl' enc = OpenSSL::Cipher::AES256.new(:CBC) enc.encrypt key = enc.random_key # let's avoid the pitfall from HMAC and generate a good key iv = enc.random_iv # we need this too! ct = enc.update("this encrypted text is actually somewhat secure") ct << enc.final # and for completeness, here's a decryption dec = OpenSSL::Cipher::AES256.new(:CBC) dec.decrypt dec.key= key dec.iv= iv pt = dec.update(ct) pt << dec.final puts pt |
Once again the fixed version does not appear significantly more complex than the (admittedly confusing) original example, but upon closer inspection problems appear. The key and IV are both generated on a per execution basis. You do want to do this with an IV, but not with a key…
First generate a key
require 'openssl' enc = OpenSSL::Cipher::AES256.new(:CBC) enc.encrypt key = enc.random_key # let's avoid the pitfall from HMAC and generate a good key File.open("/some/path",'w') { |f| f.write(key) } |
Now encrypt/decrypt your data
require 'openssl' enc = OpenSSL::Cipher::AES256.new(:CBC) enc.encrypt key = File.read("/some/path") iv = enc.random_iv ct = enc.update("no one can ever read this") ct << enc.final # and for completeness, here's a decryption dec = OpenSSL::Cipher::AES256.new(:CBC) dec.decrypt dec.key= key dec.iv= iv pt = dec.update(ct) pt << dec.final puts pt |
Once again we’ve had to split our example into two separate bits of code. One is executed a single time and the other is the common case. We do have a per-encryption IV that needs to be saved so you can fully decrypt later, so don’t forget about that! But what if you want to use a password rather than a pile of random bytes?
We’ve learned our lesson from before and we will not be forgetting the IV.
require 'openssl' enc = OpenSSL::Cipher::AES256.new(:CBC) enc.encrypt key = "Testing1" # a password provided by the user perhaps iv = enc.random_iv # we need this too! ct = enc.update("customer secrets go here!") ct << enc.final # and for completeness, here's a decryption dec = OpenSSL::Cipher::AES256.new(:CBC) dec.decrypt dec.key= key dec.iv= iv pt = dec.update(ct) pt << dec.final puts pt |
No problem. The user is at fault for their own terrible password right? We just gave the block cipher the key, generated a proper IV, and now we’re done. Well, not so fast. Encryption has tricked you yet again. What you really need is PBKDF2, part of RFC 2898. (What, you didn’t know that? Guess that code sample you copied might have been kind of dangerous…)
require 'openssl' enc = OpenSSL::Cipher::AES256.new(:CBC) enc.encrypt user_password = "Testing1" key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(user_password,"somesalt",1000,32) enc.key= key iv = enc.random_iv ct = enc.update("my first ciphertext") ct << enc.final # and for completeness, here's a decryption dec = OpenSSL::Cipher::AES256.new(:CBC) dec.decrypt dec.key= key dec.iv= iv pt = dec.update(ct) pt << dec.final puts pt |
Well that’s…substantially less understandable. What the hell are all those parameters in pbkdf2_hmac_sha1 anyway? And is it a good idea to use “somesalt” as a fixed string? (It might be okay, or it might be a serious problem depending on what you’re trying to accomplish. Damned nuance!)
Now things are getting complicated… Let’s assume we care about having a random salt (pretty likely since if you’re encrypting with user passwords you’ll find that users are terrible about having distinct passwords!). RFC 2898 recommends we use a salt of minimum length 64-bits (8 bytes). It also recommends a minimum of 1000 iterations of the password derivation function. Of course, the document is from 2000 and computers are much faster now than they used to be…Let’s choose 10000 iterations (somewhat arbitrarily).
require 'openssl' enc = OpenSSL::Cipher::AES256.new(:CBC) enc.encrypt user_password = "Testing1" length = 32 # byte length of an AES-256 key (256/8) iterations = 10000 salt = OpenSSL::Random.random_bytes(8) key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(user_password,salt,iterations,length) enc.key= key iv = enc.random_iv ct = enc.update("secure password-based encryption ahoy") ct << enc.final # and for completeness, here's a decryption dec = OpenSSL::Cipher::AES256.new(:CBC) dec.decrypt dec.key= key dec.iv= iv pt = dec.update(ct) pt << dec.final puts pt |
There, a nice secure way to do this. Of course, now that we’ve gone this far down the rabbit hole we have the issue of how to store per-encryption salts such that you can decrypt the ciphertext at a later time, but that’s an exercise left to the reader.5
I’m getting a bit long-winded so I’ll leave you with some more ideas for dangerous trivial examples. If you’ve got more don’t hesitate to speak up!
This post has been updated to be compatible with the r509 0.9 API and configuration file format.
I’ve covered how to create a self-signed certificate authority using OpenSSL in the past, but today we’re going to go step by step through the process of building a fully functional CA using r509 and its ancillary projects. At the end you’ll have a CA capable of issuing and revoking X.509v3 certificates via an HTTP interface1, publishing CRLs, and responding to OCSP requests.
Certificate authorities are useful in any number of environments where you want to establish the identity of a server (or client). Most commonly this takes the form of TLS handshakes on port 443 that are then used to transmit HTTP traffic (HTTPS). This howto aims to help you set up a basic certificate authority that you can use to issue certificates to consumers in your own environment for whatever specific use case you have.
Of course! For brevity2 and simplicity this document does several things that are not best practice for running your own CA. We will create a self-signed root, but won’t create a subordinate root to issue certs off of. We will use the root itself to sign OCSP responses rather than creating an OCSP signing delegate. We won’t talk at all about security around your root certificate private key3.
r509 is a ruby project, so you’ll want to have ruby. At this time I’d recommend the latest ruby (1.9.3-p286), but r509 is tested against 1.8.7, 1.9.3, ree, ruby-head, and rubinius 2.0 (in 1.9 mode). JRuby is not supported because r509 uses the OpenSSL bindings, which are unavailable there.
Once you have a functional ruby install, you’ll need to install some gems. We’ll start with r509 (which is available via rubygems.org).
gem install r509
Next, we’ll need r509-ca-http, a RESTful interface for issuing certificates.
gem install r509-ca-http
We’ll also want r509-middleware-validity to write validity data for our issued/revoked certs to a data store (in this case, redis). This gem is optional, but without it you will not be able to serve OCSP responses.
gem install r509-middleware-validity
Finally, r509-middleware-certwriter will write issued certificates to the filesystem for archival purposes. This gem is optional. You may choose to store your issued certificates in whatever way you wish.
gem install r509-middleware-certwriter
If we’re going to write to redis, we’ll need it installed. You can download the source, grab it from your Linux distribution’s package manager, or use a tool like homebrew if you’re on OS X (just type brew install redis). Once installed go ahead and start it up (either directly via redis-server or init.d) and leave it running. You can obviously skip this step if you’ve chosen to not use r509-middleware-validity.
Now that we have the gems (and their dependencies) installed we can set up a system to issue certificates. First let’s create a new directory called r509-howto-ca and cd into it (we’ll be working within this directory from now on). r509 comes with a utility (cleverly called “r509″). Simply type r509 --out root.cer --keyout root.key in the shell and it will enter an interactive mode that will let us generate the root certificate quickly and easily. You’ll need to choose the following:
After these steps the script will write a certificate and private key to the files specified.
r509 is largely configured through yaml files. Here’s an example configuration we’re going to customize for your needs.
certificate_authorities: { r509_howto_ca: { ca_cert: { cert: 'root.cer', key: 'root.key' }, ocsp_location: 'URI:http://ocsp.myca.com:9291', ocsp_start_skew_seconds: 3600, ocsp_validity_hours: 168, cdp_location: 'URI:http://crl.myca.com/r509_howto_ca.crl', crl_list: 'r509_howto_ca_list.txt', crl_number: 'r509_howto_ca_crlnumber.txt', crl_validity_hours: 168, #7 days default_md: 'SHA1', #SHA1, SHA256, SHA512 supported. MD5 too, but you really shouldn't use that unless you have a good reason profiles: { server: { basic_constraints: "CA:FALSE", key_usage: [digitalSignature,keyEncipherment], extended_key_usage: [serverAuth], certificate_policies: [ ], subject_item_policy: { CN: "required", O: "required", OU: "optional", ST: "required", C: "required", L: "required" } } } } } #remove if not using certwriter certwriter: { path: "/absolute/path/to/wherever/you/want/the/certs" } |
Phew, there’s a lot in there (and this is a simple example!). You can check out the r509 config specification to learn more, but for now copy this config exactly and write it to a file named config.yaml in the same directory as your root+key.
r509-middleware-certwriter: Skip this if you are not using this gem. If you are, update the path parameter at the bottom to point to the absolute path where you want it to write certificates. This directory must exist.
r509-middleware-validity: Skip this if you are not using this gem. If you are, start up redis using the command redis-server or via init.d. You’ll want to leave this running for the remainder of the tutorial.
You’ll also need to create empty files named r509_howto_ca_crlnumber.txt and r509_howto_ca_list.txt in the same directory. You can do this via touch r509_howto_ca_crlnumber.txt r509_howto_ca_list.txt.
r509-ca-http is a gem that contains a sinatra server for performing simple certificate issuance. To start up the server you’ll need a rackup file. Write the following into a file named config.ru in the same directory as everything else.
require 'r509' require 'dependo' require 'logger' Dependo::Registry[:log] = Logger.new(STDOUT) begin gem 'r509-middleware-validity' require 'r509/middleware/validity' use R509::Middleware::Validity Dependo::Registry[:log].info "Using r509 middleware validity" rescue Gem::LoadError end begin gem 'r509-middleware-certwriter' require 'r509/middleware/certwriter' use R509::Middleware::Certwriter Dependo::Registry[:log].info "Using r509 middleware certwriter" rescue Gem::LoadError end config_data = File.read("config.yaml") Dependo::Registry[:config_pool] = R509::Config::CAConfigPool.from_yaml("certificate_authorities", config_data) require 'r509/certificateauthority/http/server' Dependo::Registry[:config_pool].all.each do |config| Dependo::Registry[:log].info "Config: " Dependo::Registry[:log].info "CA Cert:"+config.ca_cert.subject.to_s Dependo::Registry[:log].info "OCSP Cert (may be the same as above):"+config.ocsp_cert.subject.to_s Dependo::Registry[:log].info "OCSP Validity Hours: "+config.ocsp_validity_hours.to_s Dependo::Registry[:log].info "CRL Validity Hours: "+config.crl_validity_hours.to_s Dependo::Registry[:log].info "\n" end server = R509::CertificateAuthority::HTTP::Server run server |
We’ve got everything in place, so just type rackup -p 9292, hit enter, and you should see output something like this (although your details will vary):
I, [2012-11-02T11:22:43.202323 #33028] INFO -- : Config: I, [2012-11-02T11:22:43.202470 #33028] INFO -- : CA Cert:/C=US/ST=Illinois/L=Chicago/O=r509 LLC/CN=r509 CA Howto Root I, [2012-11-02T11:22:43.202559 #33028] INFO -- : OCSP Cert (may be the same as above):/C=US/ST=Illinois/L=Chicago/O=r509 LLC/CN=r509 CA Howto Root I, [2012-11-02T11:22:43.202611 #33028] INFO -- : OCSP Validity Hours: 168 I, [2012-11-02T11:22:43.202647 #33028] INFO -- : CRL Validity Hours: 168 I, [2012-11-02T11:22:43.202691 #33028] INFO -- : [2012-11-02 11:22:43] INFO WEBrick 1.3.1 [2012-11-02 11:22:43] INFO ruby 1.9.3 (2012-10-12) [x86_64-darwin12.2.0] [2012-11-02 11:22:43] INFO WEBrick::HTTPServer#start: pid=33028 port=9292 |
The server is now up and running on port 9292! The following test interfaces are available:
These interfaces are meant solely for testing. In a production environment please look at the r509-ca-http API.
Since we’re testing let’s go ahead and open a web browser and go to http://localhost:9292/test/certificate/issue
To issue a certificate off our root we’re going to need to fill in quite a bit of data. For security reasons r509-ca-http overwrites the data4 from a certificate signing request (CSR) with the data you specify. Let’s fill out the fields:
r509_howto_ca5server631536000 (this is 1 year in seconds)r509 script again, but be sure you leave “Self-signed cert duration in days” blank. You must include the —–BEGIN CERTIFICATE REQUEST—– and —–END CERTIFICATE REQUEST—– lines.Now click “gimme” and you’ll get your certificate! You should copy the cert and write it to a file named cert.pem.
Note: If you’re using the r509-middleware-validity gem or r509-middleware-certwriter you will also have a record of this issuance in redis or the cert will be written to the filesystem for archival purposes.
We’ve issued a certificate now, but what if we want to revoke it? Head to http://localhost:9292/test/certificate/revoke
There are 3 fields on this page. CA, serial, and reason.
r509_howto_ca for CAr509-parse cert.pem to get some details about your cert, then copy the value marked “Serial (Decimal)” and paste it into the serial fieldNow hit revoke and you’ll be given a PEM encoded X.509 CRL! You can also look in the r509_howto_ca_list.txt we created earlier to see that the serial is listed as revoked.
So we have a certificate and we revoked it, but how would a client who reaches a server using the certificate know that it was revoked? SSL certificates offer two methods for providing revocation information to relying parties. Let’s talk about Certificate Revocation Lists (CRLs) first.
Whenever a client receives a certificate it performs numerous checks8 to ascertain the acceptability of the certificate. One of those is looking within the certificate for a CRL Distribution Point (CDP). If found, the client fetches the CRL from the URL provided, validates it, and checks to see if the certificate in question is listed in the CRL. In our config.yaml above we had this line: cdp_location: 'URI:http://crl.domain.com/r509_howto_ca.crl',. If you want your certificates to be able to check CRL you’ll need to change this to a domain name that you can control.
Once you’ve made that change you’ll need to set up something to copy CRLs obtained from http://localhost:9292/1/crl/r509_howto_ca/generate to a web accessible area9.
CRLs are nice to have, but the more modern solution for certificate revocation information is the Online Certificate Status Protocol (OCSP) (RFC 2560 and 5019). To use r509′s OCSP responder capabilities you will need to be using r509-middleware-validity, as well as installing one more gem. Time to gem install r509-ocsp-responder.
We are going to use the root to sign responses for our OCSP responder. This is not considered best practice for a variety of reasons, but it will simplify our setup. We’ll cover OCSP signing delegates in another post. For now, let’s set up the responder’s config yaml. The text below should be placed in a file named config.yaml, but not in the same directory as your CA. Let’s create an ocsp directory and place it in there. You’ll also want to copy the root cert and key from the r509_howto_ca directory into the ocsp directory (or you can change the paths below).
copy_nonce: true cache_headers: true max_cache_age: 60 certificate_authorities: { r509_howto_ca: { ca_cert: { cert: "root.cer", key: "root.key" } } } |
The first 3 params are described in the responder documentation and the certificate_authorities node is just the single CA we want to respond for at this time.
Next we’ll need the rackup file (written to config.ru just like last time, but in the same ocsp directory as the new config.yaml you just created)
require "r509" require "dependo" require 'r509/ocsp/responder/server' Dependo::Registry[:log] = Logger.new(STDOUT) require "r509/validity/redis" require 'redis' begin gem "hiredis" Dependo::Registry[:log].warn "Loading redis with hiredis driver" redis = Redis.new(:driver => :hiredis) rescue Gem::LoadError Dependo::Registry[:log].warn "Loading redis with standard ruby driver" redis = Redis.new end Dependo::Registry[:validity_checker] = R509::Validity::Redis::Checker.new(redis) R509::OCSP::Responder::OCSPConfig.load_config R509::OCSP::Responder::OCSPConfig.print_config responder = R509::OCSP::Responder::Server run responder |
If you want higher performance lookups you can optionally install the hiredis gem.
We can now start up the responder! rackup -p 9291
W, [2012-11-06T16:24:12.816341 #57647] WARN -- : Loading redis with standard ruby driver W, [2012-11-06T16:24:12.818524 #57647] WARN -- : Config loaded W, [2012-11-06T16:24:12.818575 #57647] WARN -- : Copy Nonce: true W, [2012-11-06T16:24:12.818624 #57647] WARN -- : Cache Headers: true W, [2012-11-06T16:24:12.818672 #57647] WARN -- : Max Cache Age: 60 W, [2012-11-06T16:24:12.818710 #57647] WARN -- : Config: W, [2012-11-06T16:24:12.818824 #57647] WARN -- : CA Cert:/C=US/ST=Illinois/L=Chicago/O=r509 LLC/CN=r509 CA Howto Root W, [2012-11-06T16:24:12.818915 #57647] WARN -- : OCSP Cert (may be the same as above):/C=US/ST=Illinois/L=Chicago/O=r509 LLC/CN=r509 CA Howto Root W, [2012-11-06T16:24:12.818965 #57647] WARN -- : OCSP Validity Hours: 168 W, [2012-11-06T16:24:12.819011 #57647] WARN -- : [2012-11-06 16:24:12] INFO WEBrick 1.3.1 [2012-11-06 16:24:12] INFO ruby 1.9.3 (2012-10-12) [x86_64-darwin12.2.0] [2012-11-06 16:24:12] INFO WEBrick::HTTPServer#start: pid=57647 port=9291 |
Awesome! If we were running a full CA here we’d want to update our r509-ca-http config yaml to have the correct OCSP URI, but the config from earlier is perfect for testing. Speaking of testing…let’s test this thing out!
We need to issue a new certificate using the test interface. First create a new directory called test_server and cd into it, then do the following:
r509 --keyout localhost.key --subject "/CN=localhost/O=r509 test"localhost.cerYou should now have two files (localhost.cer and localhost.key) in your test_server directory. Inside that directory create a third file named server.rb with the following data in it:
require 'webrick/https' require 'r509' cert = R509::Cert.load_from_file('localhost.cer').cert key = R509::PrivateKey.load_from_file('localhost.key').key s = WEBrick::HTTPServer.new( :Port => 8443, :DocumentRoot => Dir::pwd, :SSLEnable => true, :SSLCertificate => cert, :SSLPrivateKey => key ) trap("INT"){ s.shutdown } s.start |
You should also add this line to your /etc/hosts file:
127.0.0.1 ocsp.myca.com
Finally, we need to set up a web browser to trust our new root CA. Let’s use Firefox10. Open the preferences, click the advanced tab, then encryption, then click the view certificates button. You now have a list of certificates in various categories. Click the authorities tab at the top, then click “Import…” and choose the root CA we created (root.cer). Do not select localhost.cer. Mark the certificate as trusted for websites and then exit out of the preferences.
We’re now ready to test! Make sure your redis server is running (it should have been running when you issued the certificate! If not, issue another one) along with r509-ocsp-responder (rackup -p 9291 in the ocsp directory), then type ruby server.rb inside the test_server directory we just created above. Now open Firefox and navigate to https://localhost:8443 . If all goes well you should see it load the page with no problems and some log lines indicating the OCSP request occurred should show up in the ocsp responder output! Congratulations, you now have a functioning CA.
You can also download a zip file of the configurations and directory setup required to get started. Grab it and then check out the README.txt to get started. You’ll still need to generate your own certs, but this will give you all the config.ru and config.yaml files already in place.
r509 consists of quite a few gems. Here are all their Github project pages. Fork, contribute, complain (maybe not that last one)!
r509
r509-ocsp-responder
r509-ca-http
r509-middleware-validity
r509-middleware-certwriter
r509-validity-redis
I just tagged v0.8 of r509 and v0.3 of r509-ocsp-responder. Here’s what’s new!
r509-validity-redis was also updated to work with the latest OCSP responder release if you’re using the redis backend.
I’ve been remiss in not mentioning this project, but along with the open source ruby certificate authority project r509 we’ve also built and released a full open source OCSP responder written in ruby. r509-ocsp-responder, as we’ve catchily named it, is designed to conform to RFC 2560 and 5019 and allow you to quickly and easily get a full OCSP responder up and running with a (relative) minimum of configuration.
r509-ocsp-responder is designed to be a “known good” responder. This means it will respond with an UNKNOWN if the responder is queried about a certificate it is unaware of even if it is configured to respond for that CA. This is in contrast to many responders, which are designed as “known bad” and will reply VALID unless the certificate is known to be revoked. We have written a redis-based validity checker (r509-validity-redis) that you can use, or you can easily write your own writer/checker backend.
If you happen to want to build an entire CA stack, we’ve got one last project (+ some middleware) for you. r509-ca-http is a RESTful interface for issuing certificates. It also works with r509-middleware-validity to automatically write issuance/revocation data to the OCSP responder’s redis DB.
We’ll be improving the documentation around these ancillary projects shortly, but feel free to dive in now (as several of you already have!).
r509 v0.6 is out. There were 39 commits encompassing the following changes from 0.5 to 0.6.
* Gemfile updated to set proper versions of supporting gems for doc generation
* Fixes to load_yaml in Config
* OCSP handling extensively refactored and most of the logic moved into the r509 OCSP responder project.
* Cert#subject_component no longer improperly upcases elements
* SAN is now supported in self-signed certificates
* Cert#san_names returns an empty array if no san_names exist
* Cert#fingerprint is now an available method
* Cert#subject_names returns a concatenation (de-duped) of CN and SANs
* General refactoring in several areas to improve code quality
* Csr now takes :san_names in constructor instead of :domains. This is more consistent
* Files renamed to lowercase to match Ruby conventions
* Cert#in_validity_range? method added
* Fixed some order dependent tests
The majority of work went into improving the OCSP codebase, which was moved into the r509-ocsp-responder project, but that’s a subject for another blog entry!