Encryption is one of those library that I use infrequently. It seems that every time I need to work with some encryption algorithm, it passes memory expiration date of my last implementation usage of some encryption algorithm. Recently, I had to implement some password encryption tool in Java and again, I had to revisit Java’s encryption library. In the past, for exploring unfamiliar libraries, I would normally write some code sprinkled with print statements, compile it and run the compiled code and adjust my code afterwards. But in the past couple years, I’ve become a big fan of REPL development environment for purposes of library exploration. In the .NET environment, I would use F# Interactive, Ruby & Python have their own REPL environment. For Java, I could have picked either Groovy or Clojure. I’ve scripted Groovy in the past and certainly liked the shorthand expressions to a lot of the Java code. However, I have heard a lot of positive blog posts about Clojure such that I wanted to try it out.
I never had the chance to program in Lisp in the past. But I have read some of Paul Graham’s articles which seems to make Lisp a programmer’s great secret weapon in becoming a better than average programmer. So I figure Clojure can get my feet wet with another variant of Lisp and help me accomplish my day job goals at the same time. The hardest part about learning Clojure was learning the API. The program structure was not hard to figure out. I used an RPN based HP 11C calculator while in college and Clojure program structure reminds me a lot of working with RPN calculators (albeit with a lot more parenthesis). Once I got the hang of the basic Clojure syntax, it became a great environment to work in. I would define a function, run the code and then interactively explore properties or call methods. After exploring in Clojure, it became fairly trivial task to translate that to the Java code.
While it was certainly fun to work in Clojure, I'm not sure I can substantiate Paul Graham's claim that knowing Lisp makes you a better than average programmer. Maybe it's more of the concept that a polyglot programmer generally makes a better programmer simply because the programmer that goes and learns multiple programming languages has a self driven desire to become a better programmer and therefore becomes a better programmer. I'll leave that discussion to the programmer philosophers out there and return to the more pragmmatic code construction.
Here’s the prototype AES encryption code in Clojure:
(import (javax.crypto KeyGenerator SecretKey Cipher)) (import (javax.crypto.spec SecretKeySpec)) (import (java.io File FileOutputStream DataInputStream FileInputStream)) (import (java.util Properties)) (import (org.apache.commons.codec.binary Base64)) (def msg "Hello encryption world!") (defn encode-base64 [raw] (. (new Base64) encode raw)) (defn decode-base64 [coded] (. (new Base64) decode coded)) (def aes (. KeyGenerator getInstance "AES")) (def cipher (. Cipher getInstance "AES")) (def encrypt (. Cipher ENCRYPT_MODE)) (def decrypt (. Cipher DECRYPT_MODE)) (defn writekey [rawkey filename] ( let [out (new FileOutputStream (new File filename))] (do (. out write rawkey) (. out close)))) (defn readkey [filename] (let [file (new File filename) rawkey (byte-array (. file length)) in (new DataInputStream (new FileInputStream file))] (do (. in readFully rawkey) (. in close) rawkey ))) (defn get-propfile [filename] (let [prop (new Properties)] (do (. prop load (new FileInputStream filename))) prop)) (defn genkey [keygen] (do (. keygen init 128) (. (. keygen generateKey ) getEncoded) ) ) (defn do-encrypt [rawkey plaintext] (let [cipher (. Cipher getInstance "AES")] (do (. cipher init encrypt (new SecretKeySpec rawkey "AES")) (. cipher doFinal (. plaintext getBytes))))) (defn do-decrypt [rawkey ciphertext] (let [cipher (. Cipher getInstance "AES")] (do (. cipher init decrypt (new SecretKeySpec rawkey "AES")) (new String(. cipher doFinal ciphertext))))) (defn get-password [key rawkey filename] (let [ props (get-propfile filename) coded (. props getProperty key) cipher (decode-base64 coded)] (do (do-decrypt rawkey cipher)))) (comment "Example usage" (get-password "jms" (readkey "test.key") "data.out") )