Signs objects (strings or whatever) into url-safe strings that can be used as CSRF tokens or whatnot
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns myns [:use lazy.utils.signing :as signing]) | |
(signing/with-keypair-filename "private.pem" | |
(signing/dumps {:my "hash" :of "any size"})) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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!