security encryption and JCA

Author: Chris Zheng  (z@caudate.me)
Date: 27 November 2018
Repository: https://github.com/zcaudate/hara
Version: 3.0.2

1    Introduction

hara.security provides an intuitive interface around the JCA suite of tools for securing your applications. 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.

1.1    Installation

Add to project.clj dependencies:

[hara/security "3.0.2"]

All functions are in the hara.security namespace.

 (use (quote hara.security))

2    General



->key ^

idempotent function converting input into a key

v 3.0
(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

decrypt ^

decrypts a byte array using a key

v 3.0
(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"

encrypt ^

encrypts a byte array using a key

v 3.0
(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"

generate-key ^

generates a key according to algorithm

v 3.0
(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 3.0
(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->map ^

returns a map representation of a key

v 3.0
(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    Verify



digest ^

creates a digest out of a byte array

v 3.0
(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) => (contains ["MD2" "MD5" "SHA" "SHA-224" "SHA-256" "SHA-384" "SHA-512"] :in-any-order :gaps-ok) (-> (digest (.getBytes "hello world") "SHA") (encode/to-hex)) => "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"

hmac ^

creates a key encrypted digest

v 3.0
(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"

sign ^

creates a signature using a private key

v 3.0
(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) ;; => (contains ["MD2withRSA" "MD5andSHA1withRSA" "MD5withRSA" ;; "NONEwithDSA" "NONEwithECDSA" "SHA1withDSA" ;; "SHA1withECDSA" "SHA1withRSA" "SHA224withDSA" ;; "SHA224withECDSA" "SHA224withRSA" "SHA256withDSA" ;; "SHA256withECDSA" "SHA256withRSA" "SHA384withECDSA" ;; "SHA384withRSA" "SHA512withECDSA" "SHA512withRSA"] ;; :in-any-order) (-> (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 3.0
(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

4    Provider



digest ^

creates a digest out of a byte array

v 3.0
(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) => (contains ["MD2" "MD5" "SHA" "SHA-224" "SHA-256" "SHA-384" "SHA-512"] :in-any-order :gaps-ok) (-> (digest (.getBytes "hello world") "SHA") (encode/to-hex)) => "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"

hmac ^

creates a key encrypted digest

v 3.0
(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"

sign ^

creates a signature using a private key

v 3.0
(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) ;; => (contains ["MD2withRSA" "MD5andSHA1withRSA" "MD5withRSA" ;; "NONEwithDSA" "NONEwithECDSA" "SHA1withDSA" ;; "SHA1withECDSA" "SHA1withRSA" "SHA224withDSA" ;; "SHA224withECDSA" "SHA224withRSA" "SHA256withDSA" ;; "SHA256withECDSA" "SHA256withRSA" "SHA384withECDSA" ;; "SHA384withRSA" "SHA512withECDSA" "SHA512withRSA"] ;; :in-any-order) (-> (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 3.0
(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