forked from k2n/saml20-clj
-
Notifications
You must be signed in to change notification settings - Fork 11
/
crypto.clj
116 lines (105 loc) · 5.8 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
(ns saml20-clj.crypto
(:require [saml20-clj.coerce :as coerce])
(:import org.apache.xml.security.Init
org.opensaml.security.credential.Credential
org.opensaml.xmlsec.signature.support.SignatureConstants))
(def signature-algorithms
{:dsa {nil SignatureConstants/ALGO_ID_SIGNATURE_DSA
:sha1 SignatureConstants/ALGO_ID_SIGNATURE_DSA_SHA1
:sha256 SignatureConstants/ALGO_ID_SIGNATURE_DSA_SHA256}
:rsa {nil SignatureConstants/ALGO_ID_SIGNATURE_RSA
:sha1 SignatureConstants/ALGO_ID_SIGNATURE_RSA_SHA1
:ripemd160 SignatureConstants/ALGO_ID_SIGNATURE_RSA_RIPEMD160
:sha256 SignatureConstants/ALGO_ID_SIGNATURE_RSA_SHA256
:sha224 SignatureConstants/ALGO_ID_SIGNATURE_RSA_SHA224
:sha384 SignatureConstants/ALGO_ID_SIGNATURE_RSA_SHA384
:sha512 SignatureConstants/ALGO_ID_SIGNATURE_RSA_SHA512}
:ecdsa {:sha1 SignatureConstants/ALGO_ID_SIGNATURE_ECDSA_SHA1
:sha224 SignatureConstants/ALGO_ID_SIGNATURE_ECDSA_SHA224
:sha256 SignatureConstants/ALGO_ID_SIGNATURE_ECDSA_SHA256
:sha384 SignatureConstants/ALGO_ID_SIGNATURE_ECDSA_SHA384
:sha512 SignatureConstants/ALGO_ID_SIGNATURE_ECDSA_SHA512}})
(def canonicalization-algorithms
{:omit-comments SignatureConstants/ALGO_ID_C14N_OMIT_COMMENTS
:with-comments SignatureConstants/ALGO_ID_C14N_WITH_COMMENTS
:excl-omit-comments SignatureConstants/ALGO_ID_C14N_EXCL_OMIT_COMMENTS
:excl-with-comments SignatureConstants/ALGO_ID_C14N_EXCL_WITH_COMMENTS})
;; TODO -- I'm pretty sure this mutates `object`
(defn sign
^org.w3c.dom.Element [object credential & {:keys [signature-algorithm
canonicalization-algorithm]
:or {signature-algorithm [:rsa :sha256]
canonicalization-algorithm :excl-omit-comments}}]
(when-let [object (coerce/->SAMLObject object)]
(when-let [^Credential credential (try
(coerce/->Credential credential)
(catch Throwable _
(coerce/->Credential (coerce/->PrivateKey credential))))]
(let [signature (doto (.buildObject (org.opensaml.xmlsec.signature.impl.SignatureBuilder.))
(.setSigningCredential credential)
(.setSignatureAlgorithm (or (get-in signature-algorithms signature-algorithm)
(throw (ex-info "No matching signature algorithm"
{:algorithm signature-algorithm}))))
(.setCanonicalizationAlgorithm (or (get canonicalization-algorithms canonicalization-algorithm)
(throw (ex-info "No matching canonicalization algorithm"
{:algorithm canonicalization-algorithm})))))
key-info-gen (doto (new org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory)
(.setEmitEntityCertificate true))]
(when-let [key-info (.generate (.newInstance key-info-gen) credential)] ; No need to test X509 coercion first
(.setKeyInfo signature key-info))
(.setSignature object signature)
(let [element (coerce/->Element object)]
(org.opensaml.xmlsec.signature.support.Signer/signObject signature)
element)))))
(defn decrypt! [sp-private-key element]
(when-let [sp-private-key (coerce/->PrivateKey sp-private-key)]
(when-let [element (coerce/->Element element)]
(com.onelogin.saml2.util.Util/decryptElement element sp-private-key))))
(defn recursive-decrypt! [sp-private-key element]
(when-let [sp-private-key (coerce/->PrivateKey sp-private-key)]
(when-let [element (coerce/->Element element)]
(when (= (.getNodeName element) "saml:EncryptedAssertion")
(decrypt! sp-private-key element))
(doseq [i (range (.. element getChildNodes getLength))
:let [child (.. element getChildNodes (item i))]
:when (instance? org.w3c.dom.Element child)]
(recursive-decrypt! sp-private-key child)))))
(defn ^:private secure-random-bytes
(^bytes [size]
(let [ba (byte-array size)
r (java.security.SecureRandom.)]
(.nextBytes r ba)
ba))
(^bytes []
(secure-random-bytes 20)))
(defn new-secret-key ^javax.crypto.spec.SecretKeySpec []
(javax.crypto.spec.SecretKeySpec. (secure-random-bytes) "HmacSHA1"))
(defonce ^:private -init
(do
(Init/init)
nil))
(defn signed? [object]
(when-let [object (coerce/->SAMLObject object)]
(.isSigned object)))
(defn signature [object]
(when-let [object (coerce/->SAMLObject object)]
(.getSignature object)))
(defn assert-signature-valid-when-present
[object credential]
(when-let [signature (signature object)]
(when-let [credential (coerce/->Credential credential)]
;; validate that the signature conforms to the SAML signature spec
(try
(.validate (org.opensaml.saml.security.impl.SAMLSignatureProfileValidator.) signature)
(catch Throwable e
(throw (ex-info "Signature does not conform to SAML signature spec"
{:object (coerce/->xml-string object)}
e))))
;; validate that the signature matches the credential
(try
(org.opensaml.xmlsec.signature.support.SignatureValidator/validate signature credential)
(catch Throwable e
(throw (ex-info "Signature does not match credential"
{:object (coerce/->xml-string object)}
e))))
:valid)))