/
crypto.clj
132 lines (115 loc) · 4.47 KB
/
crypto.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
;; Copyright (c) James Reeves. All rights reserved.
;; The use and distribution terms for this software are covered by the Eclipse
;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which
;; can be found in the file epl-v10.html at the root of this distribution. By
;; using this software in any fashion, you are agreeing to be bound by the
;; terms of this license. You must not remove this notice, or any other, from
;; this software.
(ns compojure.crypto
"Functions for cryptographically signing, verifying and encrypting data."
(:use compojure.encodings)
(:use clojure.contrib.def)
(:use clojure.contrib.java-utils)
(:import java.security.SecureRandom)
(:import javax.crypto.Cipher)
(:import javax.crypto.KeyGenerator)
(:import javax.crypto.Mac)
(:import javax.crypto.spec.SecretKeySpec)
(:import javax.crypto.spec.IvParameterSpec)
(:import java.util.UUID))
(defvar hmac-defaults
{:algorithm "HmacSHA256"}
"Default options for HMACs.")
(defvar encrypt-defaults
{:algorithm "AES"
:key-size 128
:mode "CBC"
:padding "PKCS5Padding"}
"Default options for symmetric encryption.")
(defn secure-random-bytes
"Returns a random byte array of the specified size. Can optionally supply
an PRNG algorithm (defaults is SHA1PRNG)."
([size]
(secure-random-bytes size "SHA1PRNG"))
([size algorithm]
(let [seed (make-array Byte/TYPE size)]
(.nextBytes (SecureRandom/getInstance algorithm) seed)
seed)))
(defn gen-secret-key
"Generate a random secret key from a map of encryption options."
([]
(gen-secret-key {}))
([options]
(secure-random-bytes (/ (options :key-size) 8))))
(defn gen-uuid
"Generate a random UUID."
[]
(str (UUID/randomUUID)))
(defn- to-bytes
"Converts its argument into an array of bytes."
[x]
(cond
(string? x) (.getBytes x)
(sequential? x) (into-array Byte/TYPE x)
:else x))
(defn hmac-bytes
"Generate a HMAC byte array with the supplied key on a byte array of data.
Takes an optional map of cryptography options."
[options key data]
(let [options (merge hmac-defaults options)
algorithm (options :algorithm)
hmac (doto (Mac/getInstance algorithm)
(.init (SecretKeySpec. key algorithm)))]
(.doFinal hmac data)))
(defn hmac
"Generate a Basc64-encoded HMAC with the supplied key on a byte array or
string of data. Takes an optional map of cryptography options."
[options key data]
(base64-encode-bytes (hmac-bytes options key (to-bytes data))))
(defn- make-algorithm
"Return an algorithm string suitable for JCE from a map of options."
[options]
(str "AES/" (options :mode) "/" (options :padding)))
(defn- make-cipher
"Create an AES Cipher instance."
[options]
(Cipher/getInstance (make-algorithm options)))
(defn encrypt-bytes
"Encrypts a byte array with the given key and encryption options."
[options key data]
(let [options (merge encrypt-defaults options)
cipher (make-cipher options)
secret-key (SecretKeySpec. key (options :algorithm))
iv (secure-random-bytes (.getBlockSize cipher))]
(.init cipher Cipher/ENCRYPT_MODE secret-key (IvParameterSpec. iv))
(to-bytes (concat iv (.doFinal cipher data)))))
(defn decrypt-bytes
"Decrypts a byte array with the given key and encryption options."
[options key data]
(let [options (merge encrypt-defaults options)
cipher (make-cipher options)
[iv data] (split-at (.getBlockSize cipher) data)
iv-spec (IvParameterSpec. (to-bytes iv))
secret-key (SecretKeySpec. key (options :algorithm))]
(.init cipher Cipher/DECRYPT_MODE secret-key iv-spec)
(.doFinal cipher (to-bytes data))))
(defn encrypt
"Encrypts a string or byte array with the given key and encryption options."
[options key data]
(base64-encode-bytes (encrypt-bytes options key (to-bytes data))))
(defn decrypt
"Base64 encodes and encrypts a string with the given key and algorithm."
[options key data]
(String. (decrypt-bytes options key (base64-decode-bytes data))))
(defn seal
"Seal a data structure into a cryptographically secure string. Ensures no-one
looks at or tampers with the data inside."
[key data]
(let [data (encrypt {} key (marshal data))]
(str data "--" (hmac {} key data))))
(defn unseal
"Read a cryptographically sealed data structure."
[key data]
(let [[data mac] (.split data "--")]
(if (= mac (hmac {} key data))
(unmarshal (decrypt {} key data)))))