-
Notifications
You must be signed in to change notification settings - Fork 50
Expand file tree
/
Copy pathsignature.clj
More file actions
131 lines (113 loc) · 4.45 KB
/
signature.clj
File metadata and controls
131 lines (113 loc) · 4.45 KB
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
(ns
#^{:author "Matt Revelle"
:doc "OAuth client library for Clojure."}
oauth.signature
(:import org.apache.commons.codec.binary.Base64
org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter)
(:require [oauth.digest :as digest])
(:use [clojure.string :only [join]]))
(declare rand-str
base-string
sign
url-encode
oauth-params)
(defn- named? [a]
(instance? clojure.lang.Named a))
(defn as-str [a]
(if (named? a)
(name a)
(str a)))
(def secure-random (delay (java.security.SecureRandom/getInstance "SHA1PRNG")))
(defn rand-str
"Random string for OAuth requests."
[length]
(. (new BigInteger (int (* 5 length)) ^java.util.Random @secure-random) toString 32))
(defn msecs->secs
"Convert milliseconds to seconds."
[millis]
(int (/ millis 1000)))
(def signature-methods {:hmac-sha1 "HMAC-SHA1"
:hmac-sha256 "HMAC-SHA256"
:rsa-sha1 "RSA-SHA1"
:plaintext "PLAINTEXT"})
(defn url-form-encode [params]
(join "&" (map (fn [[k v]]
(str (url-encode (as-str k))
"=" (url-encode (as-str v)))) params )))
(defn base-string
([method base-url c t params]
(base-string method base-url
(assoc params
:oauth_consumer_key (:key c)
:oauth_token (:token t)
:oauth_signature_method (or (params :oauth_signature_method)
(signature-methods (:signature-method c)))
:oauth_version "1.0")))
([method base-url params]
(join "&" [method
(url-encode base-url)
(url-encode (url-form-encode (sort params)))])))
(defmulti sign
"Sign a base string for authentication."
{:arglists '([consumer base-string & [token-secret]])}
(fn [c & r] (:signature-method c)))
(defmethod sign :hmac-sha1
[c base-string & [token-secret]]
(let [key (str (url-encode (:secret c)) "&" (url-encode (or token-secret "")))]
(digest/hmac-sign key base-string "HmacSHA1")))
(defmethod sign :hmac-sha256
[c base-string & [token-secret]]
(let [key (str (url-encode (:secret c)) "&" (url-encode (or token-secret "")))]
(digest/hmac-sign key base-string "HmacSHA256")))
(defmethod sign :plaintext
[c base-string & [token-secret]]
(str (url-encode (:secret c)) "&" (url-encode (or token-secret ""))))
(def ^:private pem-converter (delay
(doto (JcaPEMKeyConverter.)
(.setProvider "BC"))))
(defmethod sign :rsa-sha1
[c ^String base-string & [token-secret]]
(java.security.Security/addProvider
(org.bouncycastle.jce.provider.BouncyCastleProvider.))
(let [key-pair (-> (:secret c)
java.io.StringReader.
org.bouncycastle.openssl.PEMParser.
.readObject)
private-key (-> ^JcaPEMKeyConverter @pem-converter
(.getKeyPair key-pair)
.getPrivate)
signer (doto (java.security.Signature/getInstance "SHA1withRSA" "BC")
(.initSign private-key (java.security.SecureRandom.))
(.update (.getBytes base-string)))
raw-sig (.sign signer)]
(String. (Base64/encodeBase64 raw-sig))))
(defn verify [sig c base-string & [token-secret]]
(let [token-secret (url-encode (or token-secret ""))]
(= sig (sign c base-string token-secret))))
(defn url-encode
"The java.net.URLEncoder class encodes for application/x-www-form-urlencoded, but OAuth
requires RFC 3986 encoding."
[s]
(-> (java.net.URLEncoder/encode s "UTF-8")
(.replace "+" "%20")
(.replace "*" "%2A")
(.replace "%7E" "~")))
(defn url-decode
"The java.net.URLEncoder class encodes for application/x-www-form-urlencoded, but OAuth
requires RFC 3986 encoding."
[s]
(java.net.URLDecoder/decode s "UTF-8"))
(defn oauth-params
"Build a map of parameters needed for OAuth requests."
([consumer nonce timestamp]
{:oauth_consumer_key (:key consumer)
:oauth_signature_method (signature-methods (:signature-method consumer))
:oauth_timestamp timestamp
:oauth_nonce nonce
:oauth_version "1.0"})
([consumer nonce timestamp token]
(assoc (oauth-params consumer nonce timestamp)
:oauth_token token))
([consumer nonce timestamp token verifier]
(assoc (oauth-params consumer nonce timestamp token)
:oauth_verifier (str verifier))))