summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2012-06-17 15:08:25 +0200
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2012-06-17 15:08:25 +0200
commit169bd1f29b239d314475bdbe05f0a7a9aa332864 (patch)
tree45ab5f0c2cf3506bc0c9384b00122d46bba71fdf
parentf58ba7296c88ca1c217f0482c18660f701e4a026 (diff)
Implement client certificate generation and signing.
-rw-r--r--project.clj2
-rw-r--r--src/mulk/benki/genkey.clj132
-rw-r--r--src/mulk/benki/main.clj7
-rw-r--r--src/mulk/benki/util.clj17
4 files changed, 157 insertions, 1 deletions
diff --git a/project.clj b/project.clj
index 4a49f2a..4288ab1 100644
--- a/project.clj
+++ b/project.clj
@@ -48,6 +48,8 @@
[jivesoftware/smack "3.1.0"]
[jivesoftware/smackx "3.1.0"]
[joda-time/joda-time "2.1"]
+ [org.bouncycastle/bcprov-jdk15on "1.47"]
+ [org.bouncycastle/bcpkix-jdk15on "1.47"]
]
:plugins [[lein-swank "1.4.3"]]
:exclusions [org.clojure/clojure-contrib] ;you know, the old pre-1.3.0 versions
diff --git a/src/mulk/benki/genkey.clj b/src/mulk/benki/genkey.clj
new file mode 100644
index 0000000..0378181
--- /dev/null
+++ b/src/mulk/benki/genkey.clj
@@ -0,0 +1,132 @@
+(ns mulk.benki.genkey
+ (:refer-clojure)
+ (:use [clojure core repl pprint]
+ [hiccup core page-helpers]
+ [mulk.benki config util db webutil]
+ [clojure.core.match
+ :only [match]]
+ [noir core]
+ [clojure.java.jdbc :only [transaction do-commands]])
+ (:require [noir.session :as session]
+ [noir.response :as response]
+ [noir.request :as request]
+ [clojure.java.jdbc :as sql]
+ [com.twinql.clojure.http :as http])
+ (:import [org.bouncycastle.jcajce.provider.asymmetric.rsa
+ BCRSAPublicKey]
+ [org.bouncycastle.cert
+ X509v3CertificateBuilder
+ X509CertificateHolder]
+ [org.bouncycastle.operator
+ DefaultSignatureAlgorithmIdentifierFinder
+ DefaultDigestAlgorithmIdentifierFinder]
+ [org.bouncycastle.operator.bc
+ BcRSAContentSignerBuilder]
+ [org.bouncycastle.asn1.x509
+ X509Name
+ SubjectPublicKeyInfo]
+ [org.bouncycastle.asn1.x500
+ X500Name]))
+
+
+
+(defonce signing-keypair
+ (-> (doto (org.bouncycastle.crypto.generators.RSAKeyPairGenerator.)
+ (.init (org.bouncycastle.crypto.params.RSAKeyGenerationParameters.
+ (BigInteger. "123") (java.security.SecureRandom.) 512 123)))
+ (.generateKeyPair)))
+
+(defonce cert-signer
+ (let [sig-id (.find (DefaultSignatureAlgorithmIdentifierFinder.) "SHA1withRSA")
+ digest-id (.find (DefaultDigestAlgorithmIdentifierFinder.) sig-id)
+ ;;digest-id (.find (DefaultDigestAlgorithmIdentifierFinder.) "SHA1")
+ ]
+ (-> (BcRSAContentSignerBuilder. sig-id digest-id)
+ (.build (.getPrivate signing-keypair)))))
+
+(defonce cert-serial
+ (atom 1N))
+
+(defn join-lines [s]
+ (apply str (clojure.string/split s #"\n")))
+
+(defn decode-spkac [spkac]
+ (org.bouncycastle.mozilla.SignedPublicKeyAndChallenge.
+ (ring.util.codec/base64-decode (join-lines spkac))))
+
+(defmulti spkac-pubkey->map type)
+
+(defmethod spkac-pubkey->map BCRSAPublicKey [pubkey]
+ {:modulus (.getModulus pubkey)
+ :exponent (.getPublicExponent pubkey)})
+
+(defn spkac-pubkey [creq]
+ (spkac-pubkey->map
+ (.getPublicKey creq "BC")))
+
+(defn now []
+ (java.util.Date.))
+
+(defn twenty-years-from-now []
+ (java.util.Date. (+ (* 20 3600 24 365 1000) (.getTime (now)))))
+
+(defn sign-spkac [spkac]
+ (let [serial (swap! cert-serial inc)
+ pubkeyinfo (-> spkac
+ (.getPublicKeyAndChallenge)
+ (.getSubjectPublicKeyInfo))
+ builder (X509v3CertificateBuilder.
+ (X500Name. "CN=Guenologlyan Mages Association")
+ (java.math.BigInteger. (str serial))
+ (now)
+ (twenty-years-from-now)
+ (X500Name. "CN=Benki User")
+ pubkeyinfo)
+ cert (.build builder cert-signer)]
+ cert))
+
+(defpage "/genkey" []
+ (redirect "/keys"))
+
+(defpage [:post "/keys/register"] []
+ (let [spkac-data (get-in (request/ring-request) [:params :key])
+ spkac (decode-spkac spkac-data)
+ pubkey (spkac-pubkey spkac)]
+ (with-dbt
+ (query "INSERT INTO rsa_keys (modulus, exponent) VALUES (?::NUMERIC, ?::NUMERIC) RETURNING 't'"
+ (str (:modulus pubkey)) (str (:exponent pubkey)))
+ (query "INSERT INTO user_rsa_keys (\"user\", modulus, exponent) VALUES (?, ?::NUMERIC, ?::NUMERIC) RETURNING 't'"
+ *user* (str (:modulus pubkey)) (str (:exponent pubkey)))
+ ;;(redirect (linkrel :keys))
+ {:status 200
+ :headers {"Content-Type" "application/x-x509-user-cert"}
+ :body (.getEncoded (sign-spkac spkac))})))
+
+(defpage "/keys" []
+ (with-auth
+ (layout {} "Key Management"
+ [:p
+ [:h2 "Your Keys"]
+ [:table
+ {:style "border: 1px solid #000"
+ :border "1"}
+ [:thead
+ [:th {:style "text-align: left"} "Exponent"]
+ [:th {:style "text-align: left"} "Modulus"]]
+ [:tbody
+ (with-dbt
+ (for [{e :exponent, m :modulus}
+ (query "select * from user_rsa_keys where \"user\" = ?" *user*)]
+ [:tr
+ [:td e]
+ [:td m]]))]]]
+ [:p
+ [:h2 "Generate a Key Pair"]
+ [:h3 "RSA"]
+ [:form {:method "POST", :action (linkrel :keys :register)}
+ [:keygen {:name "key", :keytype "RSA"}]
+ [:input {:type "submit" :value "Generate"}]]
+ [:h3 "ECDSA"
+ [:form {:method "POST", :href (linkrel :keys :register)}
+ [:keygen {:name "key", :keytype "EC", :keyparams "secp521r1"}]
+ [:input {:type "submit" :value "Generate"}]]]])))
diff --git a/src/mulk/benki/main.clj b/src/mulk/benki/main.clj
index 7bcf332..c5ddfd6 100644
--- a/src/mulk/benki/main.clj
+++ b/src/mulk/benki/main.clj
@@ -5,7 +5,7 @@
[hiccup core page-helpers]
[mulk.benki util config db])
(:require [noir server options]
- [mulk.benki wiki auth book_marx id lazychat xmpp]
+ [mulk.benki wiki auth book_marx id lazychat xmpp genkey]
[ring.middleware.file]
[noir.session :as session]
[noir.request :as request]
@@ -128,8 +128,13 @@
(def server (atom nil))
+(defn init-security! []
+ (java.security.Security/addProvider
+ (org.bouncycastle.jce.provider.BouncyCastleProvider.)))
+
(defn -main [& args]
(do
+ (init-security!)
(future (mulk.benki.xmpp/init-xmpp!))
(future (mulk.benki.lazychat/init-lazychat!))
(future (swap! server (run-server))))
diff --git a/src/mulk/benki/util.clj b/src/mulk/benki/util.clj
index 0bfe5e9..ce9803d 100644
--- a/src/mulk/benki/util.clj
+++ b/src/mulk/benki/util.clj
@@ -69,6 +69,8 @@
[[:lafargue :feed]] (fmt nil "/lafargue/feed")
[[:lafargue :post]] (fmt nil "/lafargue/post")
[[:wiki title & xs]] (fmt nil "/wiki/~a~@[~a~]" title (first xs))
+ [[:keys]] "/keys"
+ [[:keys :register]] "/keys/register"
))
(defn link [& args]
@@ -116,3 +118,18 @@
(defn sanitize-html [html]
(Jsoup/clean html (Whitelist/basic)))
+
+
+;;;; * Debugging
+(defonce logger
+ (org.apache.log4j.Logger/getLogger "eu.mulk.benki"))
+
+(defn info [s]
+ (.info logger s))
+
+(defn log [s]
+ (.info logger s))
+
+(defn debug [s]
+ (.debug logger s))
+