hara.security cryptography that makes sense

Author: Chris Zheng  (z@caudate.me)
Date: 14 March 2017
Repository: https://github.com/zcaudate/hara
Version: 2.5.2

1    Introduction

hara.security provides an intuitive interface around the JCA suite of tools for securing your applications

1.1    Installation

Add to project.clj dependencies:

[im.chit/hara.security "2.5.2"]
All functionality is found contained in the hara.security namespace
(require '[hara.security :as security])

1.2    Motivation

hara.security aims to integrate easily with the JSE/JCA framework. As many other libraries such as Bouncy Castle also extend this framework, the methods allow better exploration and usage of the options. Furthermore, deliberate effort was made to allow keys to be expressed as data so that encryption could be handled more explictly and with more understanding.

2    Index

3    API

3.1    Providers



list-providers ^

list all security providers

v 2.4
(defn list-providers
  []
  (->> (Security/getProviders)
       (seq)
       (map #(.getName ^Provider %))
       (sort)))
link
(list-providers) => ["Apple" "SUN" "SunEC" "SunJCE" "SunJGSS" "SunJSSE" "SunPCSC" "SunRsaSign" "SunSASL" "XMLDSig"]

list-services ^

lists all services that are available

v 2.4
(defn list-services
  ([]
   (->> (Security/getProviders)
        (mapcat #(.getServices ^Provider %))
        (map #(.getType ^Provider$Service %))
        (set)
        (sort)))
  ([type]
   (list-services type nil))
  ([type provider]
   (->> (if (nil? provider)
          (Security/getProviders)
          [(Security/getProvider provider)])
        (mapcat #(.getServices ^Provider %))
        (sort-services type))))
link
(list-services) => ("AlgorithmParameterGenerator" "AlgorithmParameters" ...) (list-services "Cipher") => ("AES" "AESWrap" "AESWrap_128" ...) (list-services "KeyGenerator" "SunJCE") => ("AES" "ARCFOUR" "Blowfish" "DES" "DESede" ...)

cipher ^

lists or returns available `Cipher` implementations

v 2.4
(defn cipher
  ([]
   (list-services "Cipher"))
  ([name]
   (cipher name nil))
  ([^String name ^String provider]
   (if (nil? provider)
     (Cipher/getInstance name)
     (Cipher/getInstance name provider))))
link
(cipher) => ("AES" "AESWrap" "AESWrap_128" ...) (cipher "AES") => javax.crypto.Cipher

key-generator ^

lists or returns available `KeyGenerator` implementations

v 2.4
(defn key-generator
  ([]
   (list-services "KeyGenerator"))
  ([name]
   (key-generator name nil))
  ([^String name ^String provider]
   (if (nil? provider)
     (KeyGenerator/getInstance name)
     (KeyGenerator/getInstance name provider))))
link
(key-generator) => ("AES" "ARCFOUR" "Blowfish" ...) (key-generator "Blowfish") => javax.crypto.KeyGenerator

key-pair-generator ^

lists or returns available `KeyPairGenerator` implementations

v 2.4
(defn key-pair-generator
  ([]
   (list-services "KeyPairGenerator"))
  ([name]
   (key-pair-generator name nil))
  ([^String name ^String provider]
   (if (nil? provider)
     (KeyPairGenerator/getInstance name)
     (KeyPairGenerator/getInstance name provider))))
link
(key-pair-generator) => ("DSA" "DiffieHellman" "EC" "RSA") (key-pair-generator "RSA") => java.security.KeyPairGenerator

key-store ^

lists or returns available `KeyStore` implementations

v 2.4
(defn key-store
  ([]
   (list-services "KeyStore"))
  ([name]
   (key-store name nil))
  ([^String name ^String provider]
   (if (nil? provider)
     (KeyStore/getInstance name)
     (KeyStore/getInstance name provider))))
link
(key-store) => ("CaseExactJKS" "DKS" "JCEKS" "JKS" "KeychainStore" "PKCS12") (key-store "JKS") => java.security.KeyStore

mac ^

lists or returns available `Mac` implementations

v 2.4
(defn mac
  ([]
   (list-services "Mac"))
  ([name]
   (mac name nil))
  ([^String name ^String provider]
   (if (nil? provider)
     (Mac/getInstance name)
     (Mac/getInstance name provider))))
link
(mac) => ("HmacMD5" "HmacPBESHA1" "HmacSHA1" ...) (mac "HmacMD5") => javax.crypto.Mac

message-digest ^

lists or returns available `MessageDigest` implementations

v 2.4
(defn message-digest
  ([]
   (list-services "MessageDigest"))
  ([name]
   (message-digest name nil))
  ([^String name ^String provider]
   (if (nil? provider)
     (MessageDigest/getInstance name)
     (MessageDigest/getInstance name provider))))
link
(message-digest) => ("MD2" "MD5" "SHA" "SHA-224" "SHA-256" "SHA-384" "SHA-512") (message-digest "MD2") => java.security.MessageDigest$Delegate

signature ^

lists or returns available `Signature` implementations

v 2.4
(defn signature
  ([]
   (list-services "Signature"))
  ([name]
   (signature name nil))
  ([^String name ^String provider]
   (if (nil? provider)
     (Signature/getInstance name)
     (Signature/getInstance name provider))))
link
(signature) => ("MD2withRSA" "MD5andSHA1withRSA" "MD5withRSA" ...) (signature "MD2withRSA") => java.security.Signature$Delegate

3.2    Keys



generate-key ^

generates a key according to algorithm

v 2.4
(defn generate-key
  ([] (provider/key-generator))
  ([algo {:keys [provider] :as opts}]
   {:pre [(:length opts)]}
   (-> ^KeyGenerator (provider/key-generator algo provider)
       (doto (init-key-generator opts))
       (.generateKey))))
link
(generate-key) => ("AES" "ARCFOUR" "Blowfish" "DES" "DESede" "HmacMD5" "HmacSHA1" "HmacSHA224" "HmacSHA256" "HmacSHA384" "HmacSHA512" ...) (generate-key "AES" {:length 128}) ;;=> #key {:type "AES", ;; :mode :secret, ;; :format "RAW", ;; :encoded "AQgv8l+vJNfnEWuhHs55wg=="} (generate-key "HmacSHA224" {:length 40}) ;;=> #key {:type "HmacSHA224", ;; :mode :secret, ;; :format "RAW", ;; :encoded "0qQkmic="}

generate-key-pair ^

creates a public and private key pair

v 2.4
(defn generate-key-pair
  ([]
   (provider/key-pair-generator))
  ([type {:keys [provider] :as opts}]
   {:pre [(:length opts)]}
   (-> ^KeyPairGenerator (provider/key-pair-generator type provider)
       (doto (init-key-pair-generator opts))
       (.generateKeyPair)
       ((juxt #(.getPublic ^KeyPair %)
              #(.getPrivate ^KeyPair %))))))
link
(generate-key-pair) => ("DSA" "DiffieHellman" "EC" "RSA") (generate-key-pair "RSA" {:length 512}) ;;=> [#key {:type "RSA", ;; :mode :public, ;; :format "X.509", ;; :encoded "...." } ;; #key {:type "RSA", ;; :mode :private, ;; :format "PKCS#8", ;; :encoded "..."}]

->key ^

idempotent function converting input into a key

v 2.4
(defn ->key
  [k]
  (cond (map? k)
        (map->key k)

        :else k))
link
(-> {:type "AES", :mode :secret, :format "RAW", :encoded "euHlt5sHWhRpbKZHjrwrrQ=="} (->key) (->key)) => java.security.Key

key->map ^

returns a map representation of a key

v 2.4
(defn key->map
  [^Key k]
  (cond (map? k) k

        :else
        {:type    (.getAlgorithm k)
         :mode    (key-mode k)
         :format  (.getFormat k)
         :encoded (encode/to-base64 (.getEncoded k))}))
link
(key->map (generate-key "AES" {:length 128})) => (contains {:type "AES", :mode :secret, :format "RAW", :encoded string?})

3.3    Encryption



encrypt ^

encrypts a byte array using a key

v 2.4
(defn encrypt
  ([bytes key]
   (encrypt bytes key {}))
  ([bytes key {:keys [algorithm params random iv] :as opts}]
   (operate Cipher/ENCRYPT_MODE bytes key opts)))
link
(-> (encrypt (.getBytes "hello world") {:type "AES", :mode :secret, :format "RAW", :encoded "euHlt5sHWhRpbKZHjrwrrQ=="}) (encode/to-hex)) => "30491ab4427e45909f3d2f5d600b0f93"

decrypt ^

decrypts a byte array using a key

v 2.4
(defn decrypt
  ([bytes key]
   (decrypt bytes key {}))
  ([bytes key {:keys [algorithm params random iv] :as opts}]
   (operate Cipher/DECRYPT_MODE bytes key opts)))
link
(-> (decrypt (encode/from-hex "30491ab4427e45909f3d2f5d600b0f93") {:type "AES", :mode :secret, :format "RAW", :encoded "euHlt5sHWhRpbKZHjrwrrQ=="}) (String.)) => "hello world"

3.4    Digest



digest ^

creates a digest out of a byte array

v 2.4
(defn digest
  ([]
   (provider/message-digest))
  ([bytes algo]
   (digest bytes algo {}))
  ([bytes algo {:keys [provider] :as opts}]
   (-> ^java.security.MessageDigest
       (provider/message-digest algo provider)
       (doto (.update bytes))
       (.digest))))
link
(digest) => ["MD2" "MD5" "SHA" "SHA-224" "SHA-256" "SHA-384" "SHA-512"] (-> (digest (.getBytes "hello world") "SHA") (encode/to-hex)) => "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"

hmac ^

creates a key encrypted digest

v 2.4
(defn hmac
  ([]
   (provider/mac))
  ([bytes key]
   (hmac bytes key {}))
  ([bytes key {:keys [algorithm provider] :as opts}]
   (-> (provider/mac (or algorithm
                         (key/key-type key))
                     provider)
       (doto (init-hmac (key/->key key) opts))
       (.doFinal bytes))))
link
(-> (hmac (.getBytes "hello world") {:type "HmacSHA1", :mode :secret, :format "RAW", :encoded "wQ0lyydDSEFRKviwv/2BoWVQDpj8hbUiUXytuWj7Yv8="}) (encode/to-hex)) => "a6f9e08fad62f63a35c6fd320f4249c9ad3079dc"

3.5    Signature



sign ^

creates a signature using a private key

v 2.4
(defn sign
  ([]
   (provider/signature))
  ([bytes key {:keys [algorithm provider] :as opts}]
   (-> (provider/signature algorithm provider)
       (doto (.initSign (key/->key key)) (.update bytes))
       (.sign))))
link
(sign) => ["MD2withRSA" "MD5andSHA1withRSA" "MD5withRSA" "NONEwithDSA" "NONEwithECDSA" "SHA1withDSA" "SHA1withECDSA" "SHA1withRSA" "SHA224withDSA" "SHA224withECDSA" "SHA224withRSA" "SHA256withDSA" "SHA256withECDSA" "SHA256withRSA" "SHA384withECDSA" "SHA384withRSA" "SHA512withECDSA" "SHA512withRSA"] (-> (sign (.getBytes "hello world") {:type "RSA", :mode :private, :format "PKCS#8", :encoded (apply str ["MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAmrOdqA5ZGMJ6" "55meZCnj44B65ZUXnAscXu7GJcNQO91Z7B9NmWX/P59BBUC/yJ6s/ugEffhP" "wCYJt013GkV6tQIDAQABAkBJxzV+C3G0XDOvNlUSoeO8AO8bhJIg6i+amrdH" "FTGzimwp/eyOGZlXpHcaK57kSBK4npXgfWCFFLNuvAtCrQ91AiEA0McEFHMS" "MTVU/78kDYSsJ+lty6izxkONp/XZ4+T6BDsCIQC9sWmBYAFDfiHvLnv2NT7O" "08LR+UnNuDalduukc649zwIhAKXHEadHRA/M4GR/Gxqc2bKLeUJ4/98TrvzK" "jCyYmioXAiAiOg2wY1M3C14yGvARB6ByjzD61AEmFlP93Qw9mwXYbwIhALR/" "Uv4DvJJbR7mpRXcRCo9Me1wawdCndM5ZyF7Hvpu4"])} {:algorithm "MD5withRSA"}) (encode/to-hex)) => (apply str ["5ba863c3e24c73f09d50749698ae82406490c" "edc4566810461480e37da661754b7bf33cc6b" "bf0f48646304c8994202d2fd7094e7420049f" "eaa512c8cd72d7000"])

verify ^

verifies a signature using a public key

v 2.4
(defn verify
  ([]
   (provider/signature))
  ([bytes signature key {:keys [algorithm provider] :as opts}]
   (-> (provider/signature algorithm provider)
       (doto (.initVerify (key/->key key)) (.update bytes))
       (.verify signature))))
link
(verify (.getBytes "hello world") (->> ["5ba863c3e24c73f09d50749698ae82406490c" "edc4566810461480e37da661754b7bf33cc6b" "bf0f48646304c8994202d2fd7094e7420049f" "eaa512c8cd72d7000"] (apply str) (encode/from-hex)) {:type "RSA", :mode :public, :format "X.509", :encoded (apply str ["MFwwDQYJKoZIhvcNAQEBBQADSwAwSA" "JBAJqznagOWRjCeueZnmQp4+OAeuWV" "F5wLHF7uxiXDUDvdWewfTZll/z+fQQ" "VAv8ierP7oBH34T8AmCbdNdxpFerUC" "AwEAAQ=="])} {:algorithm "MD5withRSA"}) => true