Skip to content

Instantly share code, notes, and snippets.

@samuraisam
Created July 5, 2012 03:05
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save samuraisam/3050902 to your computer and use it in GitHub Desktop.
Signs objects (strings or whatever) into url-safe strings that can be used as CSRF tokens or whatnot
(ns myns [:use lazy.utils.signing :as signing])
(signing/with-keypair-filename "private.pem"
(signing/dumps {:my "hash" :of "any size"}))
(ns lazy.utils.signing
(import
(java.io StringReader)
(java.security Security Signature)
(org.apache.commons.codec.binary Base64)
(org.bouncycastle.jce.provider BouncyCastleProvider)
(org.bouncycastle.openssl PEMReader))
(:use [cheshire.core :as json])
(:require [clojure.string :as string]))
;; hook up the BouncyCastleProvider which makes PEM things easier for us
(Security/addProvider (BouncyCastleProvider.))
(defn get-keypair
"Get a KeyPair from a PEM in a string"
[pem]
(let [sr (StringReader. pem) ;; have to create a StringReader
pemreader (PEMReader. sr)] ;; and then create a PEMReader
(.readObject pemreader))) ;; and then read that. point. java
(defn get-keypair-filename
"Return a keypair from the file named `fn`"
[filename]
(get-keypair (slurp filename)))
(defn sign
"Sign bytes `b` given a KeyPair `keypair` and a String"
[keypair b]
(let [sig (doto
(Signature/getInstance "SHA1withRSA")
(.initSign (.getPrivate keypair)) ;; initSign w/ the private key from the keypair
(.update b))] ;; and add the bytes to it
(.sign sig)))
(defn verify
"Verify that a base64 encoded byte array (v) is a valid signed string"
[keypair b v]
(let [sig (doto
(Signature/getInstance "SHA1withRSA")
(.initVerify (.getPublic keypair)) ;; initSign w/ the private key from the keypair
(.update b))] ;; and add the bytes to it
(.verify sig v))) ;; now verify the bytes passed in `v`
(def ^:dynamic keypair)
(defmacro with-keypair
"Run functions that expect a pre-defined keypair"
[kp & body]
`(binding [keypair ~kp]
(do ~@body)))
(defn sign-obj
"Signs a map as a url safe base64 encoded string"
[ob]
(Base64/encodeBase64URLSafeString (sign keypair (json/generate-smile ob))))
(defn dumps
"Dump an object into a signed string. Serializes the object into the
string so it can later be retrieved."
[ob]
(string/join ":"
[(Base64/encodeBase64URLSafeString (json/generate-smile ob))
(sign-obj ob)]))
(defn loads
"Load an object from a signed string and verify it.
Returns nil if not valid, otherwise returns the deserialized object."
[s]
(let [val (string/split s #":" 2)
b (Base64/decodeBase64 (nth val 0))]
(cond
(verify keypair b (Base64/decodeBase64 (nth val 1))) (json/decode-smile b)
:else nil)))
@adamneilson
Copy link

This was really useful!

One thing on line 49 I found this change actually loaded the keys in (binding [keypair (get-keypair-filename ~kp)]

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment