Fork me on GitHub
#babashka
<
2023-04-07
>
mjhika05:04:01

Hey friends, I've found myself putting bb on more and more Windows computers in my job role as a sysadmin. So, rather than just pulling the binary from GitHub every time, I wrote a little install script. For now it just supports downloading to a specific directory or to program files and puts the babashka dir on the path. https://github.com/mjhika/babashka-dl

🎉 8
borkdude07:04:47

Great job! We can link to this project from the babashka README - or if the script matures, maybe we can just put it into babashka's repo eventually

mjhika11:04:04

Sure! I'd love to mature the script some more so that it has much more feature parity

💯 2
jmv19:04:27

i noticed that CertificateFactory is not in babashka. is that on purpose? is there a better way to create an X509Certificate from a file?

user=> (import java.security.cert.X509Certificate)
java.security.cert.X509Certificate
user=>  (import java.security.cert.CertificateFactory)
java.lang.Exception: Unable to resolve classname: java.security.cert.CertificateFactory [at <repl>:2:2]
user=>

borkdude19:04:31

@UDXEK491P What is the context of your question? I think we could add support for this to bb but it would be good to know more about the why

jmv19:04:23

i am writing a script to automate some cert management for my team. it comes split apart and i need to assemble it in a specific order so i want to parse the certs. basically the flow is zip -> cert -> chained cert -> install into gcp

borkdude19:04:38

it would be useful to know what more you need after adding this class. would it be possible to give a full example? then I'll make sure it will work with bb

jmv19:04:04

sure, this is what i'm working with at the moment

(ns certificate
  (:import
   (java.time.format DateTimeFormatter)
   (java.security.cert X509Certificate CertificateFactory))
  (:require
   [ :as io]
   [com.stuartsierra.dependency :as dep]
   [clojure.string :as str]))

(defn x509-certificate
  ^X509Certificate
  [f]
  (let [input   (io/input-stream f)
        factory (CertificateFactory/getInstance "X.509")]
    (.generateCertificate factory input)))

(defn not-before
  [certificate]
  (.toInstant (.getNotBefore certificate)))

(defn not-after
  [certificate]
  (.toInstant (.getNotAfter certificate)))

(defn valid-dates
  [certificate]
  ((juxt not-before not-after) certificate))

(defn issuer
  [certificate]
  (.getName (.getIssuerX500Principal certificate)))

(defn subject
  [certificate]
  (.getName (.getSubjectX500Principal certificate)))

(defn subject-alternative-names
  [certificate]
  (.getIssuerAlternativeNames certificate))

(defn chain-sort-step
  [graph current others]
  (reduce (fn [graph other]
            (if (= (issuer current) (subject other))
              (dep/depend graph current other)
              graph))
          graph
          others))

(defn chain-sort
  [certificates]
  (let [certs (set certificates)]
    (dep/topo-sort
     (reduce (fn [graph cert] (chain-sort-step graph cert (disj certs cert)))
             (dep/graph)
             certs))))

;; Text Utils for working with X.509 principal strings

(defn common-name
  [string]
  (second (re-find  #"CN=(?<name>\S+)" string)))


(defn- inst->iso-date-string
  [instant]
  (let [formatter (.withZone DateTimeFormatter/BASIC_ISO_DATE (java.time.ZoneId/of "UTC"))]
    (.format formatter instant)))
probably won't need all those functions in the end since they are more exploratory. but the key ones are x509-certificate and chain-sort

borkdude19:04:12

And perhaps some example function calls, so I can test it locally?

jmv19:04:57

something like this would demo it

(chain-sort
   [(x509-certificate "child.crt")
    (x509-certificate "root.crt")])

jmv19:04:04

i think being able to just create an instance of X509Certificate form the CertificateFactory would be sufficient though. since the rest of the code definitely works in bb

👍 2
borkdude19:04:22

@UDXEK491P what OS are you on?

borkdude19:04:36

m1 or intel?

borkdude19:04:39

if m1, you can download a binary from here: https://cirrus-ci.com/task/5896904853159936 to test with. Note that you need to do sudo xattr -c ~/path/to/bb to clear some quarantaine stuff after downloading.

borkdude19:04:50

If successful, I'll merge

jmv19:04:08

Sweet! Will check after lunch 🙏

jmv20:04:23

ran into

clojure.lang.ExceptionInfo: Method getSubjectX500Principal on class sun.security.x509.X509CertImpl not allowed! [at <repl>:26:1]

jmv20:04:50

(import java.security.cert.X509Certificate)
(import java.security.cert.CertificateFactory)
(require '[ :as io])
(defn x509-certificate
  ^X509Certificate
  [f]
  (let [input   (io/input-stream f)
        factory (CertificateFactory/getInstance "X.509")]
    (.generateCertificate factory input)))
(def cert (x509-certificate (io/file "certificate.crt")))
(.getSubjectX500Principal cert)

borkdude20:04:09

ok, just a moment

borkdude20:04:54

do you have a .crt file for me I can work with?

jmv20:04:55

here's a good one 😄

jmv20:04:19

in jvm clojure

user=> 
(import java.security.cert.X509Certificate)
(import java.security.cert.CertificateFactory)
(require '[ :as io])
(defn x509-certificate
  ^X509Certificate
  [f]
  (let [input   (io/input-stream f)
        factory (CertificateFactory/getInstance "X.509")]
    (.generateCertificate factory input)))
java.security.cert.X509Certificate
user=> java.security.cert.CertificateFactory
user=> nil
user=> #'user/x509-certificate
user=> (def cert (x509-certificate (io/file "clojure.crt")))
#'user/cert
user=> (.getSubjectX500Principal cert)
#object[javax.security.auth.x500.X500Principal 0x344426bf "CN="]

borkdude20:04:34

Thanks. A workaround for now:

(prn (let [^X509Certificate cert cert]
       (.getSubjectX500Principal cert)))

borkdude20:04:48

The type inference in bb isn't so great, but I'll push a fix so it will work without this workaround

borkdude21:04:25

ok, try this binary, it should work with your previous code: https://cirrus-ci.com/task/5336053058371584

borkdude21:04:05

merged to master. when the current master build finishes, you can install the dev build with:

bash <(curl ) --dev-build --dir /tmp

jmv15:04:07

this is great! works on my end

👍 2