Thursday, January 26, 2012

Setting up self-signed SSL certificates for your Jetty instance (experiments with Noir and Clojure)

Hi everyone,
  Recently, Heroku has included support for Clojure (fantastic!) and I have been testing it out with Noir. There are several good tutorials on how to get started with Noir (a route-based website development framework with Clojure) and MongoDB (congomongo) on Heroku: http://thecomputersarewinning.com/post/clojure-heroku-noir-mongo http://devcenter.heroku.com/articles/clojure
  Interestingly, Heroku has also enabled free SSL (PiggyBack SSL), which is useful for testing apps. Free hosting, database and SSL, why wait? =)

  This is to document my steps in setting up a self-signed SSL certificate on your own machine for development purposes (Jetty, not Apache). I thought I would be done in 2 hours.. but I ended up taking 2 days. Sigh.. hopefully, this will help someone else.

  There are 2 ways to do this. You can either use keytool or both keytool and openssl. I used openssl and keytool. In hindsight, keytool seems to be easier (thank you Brenton - http://formpluslogic.blogspot.com/2010/08/securing-clojure-web-applications-with.html) and less problematic than using openssl to create the necessary files, then using keytool to import the files.

  FYI, I'm using Windows 7 (you will need to change the commands and paths accordingly), Noir 1.2.2, CongoMongo 0.1.5-SNAPSHOT, Clojure 1.2.1. The 2 programs that you need are OpenSSL ("../GnuWin32/bin/openssl.exe") and Keytool (which you can find in "../Java/jre6/bin/keytool.exe").
OpenSSL - http://devcenter.heroku.com/articles/csr (download the proper version for your system)

  As an overview, you will need to do the following steps:
Install openssl
Use OpenSSL ("../GnuWin32/bin/openssl.exe") to:
  • generate a private site key (site.key)
  • strip the password from site.key for automatic loading when uploaded to a server
  • generate a self-signed signing request (site.csr) (might need it for Heroku)
  • generate a self-signed certificate (sitex509.crt - in x509 format for loading into the keystore)
  • combine the self-signed certificate (sitex509.crt) and site key (site.key) and export it in pkcs12 format (site.pkcs12)
Use keytool ("../Java/bin/keytool.exe") to:
  • import the file site.pkcs12 into the keystore (sitepkcs12.keystore)

I tripped up along the way for many of these steps, so I will include the error messages too for reference.

Install the appropriate version of openssl for your operating system.

OpenSSL
You might need to enter some pass-phrases or passwords. I suggest that you write them down these up, just in case.


Generate a private site key (site.key)
$ openssl genrsa -des3 -out site.key 2048


Make a copy of site.key and strip the password, so that it can be auto-loaded when uploading to a server
$ copy site.key site.orig.key
$ openssl rsa -in site.orig.key -out site.key


Generate a self-signed signing request (site.csr) (might need it for Heroku)
Error: could not find openssl.cnf in the config
You will need to find a copy of the openssl.cnf. I used the one that was in "GnuWin32\share\openssl.cnf". If you are using Linux or OSX, you should be able to find your version. Btw, the version of openssl.cnf that I used was dated 2005 and it still seems to work.
$ openssl req -new -key site.key -out site.csr -config "..\GnuWin32\share\openssl.cnf"
You will need to key in the information requested (please refer to http://devcenter.heroku.com/articles/csr for an explanation). Please fill the proper info for "Common Name", it should be the secure domain or sub-domain. I suggest that you save this info, as you will need to enter the exact same info for the certificate. You can also skip entering any info for the "extra" attributes.
For example,


  Country Name (2 letter code) [AU]:SG
  State or Province Name (full name) [Some-State]:SG
  Locality Name (eg, city) []:Singapore
  Organization Name (eg, company) [Internet Widgits Pty Ltd]: myapp
  Organizational Unit Name (eg, section) []:
  Common Name (eg, YOUR name) []:localhost
  Email Address []:


  Please enter the following 'extra' attributes
  to be sent with your certificate request
  A challenge password []:
  An optional company name []:


Backup your site.csr.

Generate a self-signed certificate (sitex509.crt - in x509 format for loading into the keystore)
$ openssl req -new -x509 -key site.key -out sitex509.crt -config "..\GnuWin32\share\openssl.cnf"
Enter the same info as above.
Error: not in x509 format..
The certificate needs to be in x509 format or keytool will not be able to import it into the keystore as it cannot recognize it.

Backup your sitex509.crt.

Combine the self-signed certificate (sitex509.crt) and site key (site.key) and export it in pkcs12 format (site.pkcs12)
$ openssl pkcs12 -inkey site.key -in sitex509.crt -export -out site.pkcs12

Backup your site.pkcs12.


Keytool
Copy the file site.pkcs12 to your "..\Java\jre6\bin\" directory


Make sure you have full control (write, read-access) to the Java directory
Error: I had an error initially as I could not write to the Java directory. Go to the folder settings and enable the permissions. For Windows 7, you can add "Everyone" to the users and set "Full Control" for "Everyone".


Import the file site.pkcs12 into the keystore (sitepkcs12.keystore)
$ keytool -importkeystore -srckeystore site.pkcs12 -srcstoretype PKCS12 -destkeystore sitepkcs12.keystore

Double-check the keystore.

$ keytool -list -v -keystore sitepkcs12.keystore

Backup your sitepkcs12.keystore.

Noir

Copy all the files (site.key, site.csr, sitex509.crt, sitepkcs12.keystore) to your Noir project directory ("../myapp/").
I think only the keystore file is needed.
Error: javax.ssl does not .. correspond .. cipher.
You need to convert both the key and the cert (in x509) to pkcs12 format and import them into the keystore. Then, place the keystore in your Noir project folder.

Change your jetty settings
You will need to pass the settings (jetty-options as a map) to Jetty (remember your password!). In server.clj, for example,

(defn -main [& m]
  (let [mode      (keyword (or (first m) :dev))
        port      (Integer. (get (System/getenv) "PORT" "8080"))
        ssl-port  (Integer. "443")]
    (def myappserver (server/start ssl-port
                       {:mode           mode
                        :jetty-options  {:port      port
                                         :ssl-port  ssl-port
                                         :join?     false
                                         :ssl?      true
                                         :keystore "sitepkcs12.keystore"                    
:key-password  "abcdef"}
                        :ns             'myapp
                        :session-cookie-attrs  {:max-age 3600
                                                :secure  true}}))
    (users/db-init)))


Apologies for the weird formatting, please adjust accordingly for your app.

Go to https://localhost:443/myapp and http://localhost:8080/myapp to test.

7 comments:

  1. Its really informative resource about Self Signing Certificate. Thanks for making aware us about self signing certificate request process and also we gone book mark your article.
    Cheap SSL | WildCard SSL | RapidSSL

    ReplyDelete
  2. This was enormously helpful to me. A pitfall you didn't mention that I ran into was the version of noir. If you create a project with lein noir new, it will pull in noir 1.1.0-SNAPSHOT, which doesn't allow you to pass options along to jetty.

    ReplyDelete
    Replies
    1. Hi Chris, awfully sorry about that. Did you manage to make it work? I think you can change the noir dependency in your "project.clj", then run "lein deps". That should do the trick.

      Delete
  3. Thanks for the guide. Running an e-commerce website is incredibly hard when an unskilled worker has to learn the intricacies of setting up ssl certificates . But as far as i'm concerned anything that has been taken down to code level can be incredibly intimidating to a beginner.

    ReplyDelete
  4. Really helpful article. Thanks

    ReplyDelete
  5. Hi Ryan, I know this is an older post; but I was trying to make it work, However I do not know what to put in the server namespace!!! Can you tell what that namespace has to contains?? Do you have the code in GitHub or the actual server.clj file!!!! I do really appreciate your answer!!

    ReplyDelete