blob: 937bad56f81bf87e5430cd490c41a50a47bdcad7 (
plain)
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
|
;; 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
clojure.contrib.def
clojure.contrib.java-utils)
(:import java.security.SecureRandom
[javax.crypto Cipher KeyGenerator Mac]
[javax.crypto.spec SecretKeySpec IvParameterSpec]
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)))))
|