Search code examples
githubclojurehmachmacsha1

Clojure (or Java) equivalent to Ruby's HMAC.hexdigest


When setting a webhook with the Github API, I can provide a secret. When Github sends me a POST request, this secret is used to encode one of the headers:

The value of this header is computed as the HMAC hex digest of the body, using the secret as the key.

On the manual page, they link to this Ruby example

OpenSSL::HMAC.hexdigest(HMAC_DIGEST, secret, body)

I need a way to reproduce this line in Clojure.

Googling around, I found a number of example functions (1,2,3) for this purpose, but none of them worked. I'm clearly doing something wrong, because they all provide the same result, but it doesn't match the header I receive from Github.

For example, this is the simplest implementation I managed to cook up.

(ns website.test
  (:import javax.crypto.Mac
           javax.crypto.spec.SecretKeySpec
           org.apache.commons.codec.binary.Base64))

;;; Used in core.clj to verify that the payload matches the secret.x
(defn- hmac
  "Generates a Base64 HMAC with the supplied key on a string of data."
  [^String data]
  (let [algo "HmacSHA1"
        signing-key (SecretKeySpec. (.getBytes hook-secret) algo)
        mac (doto (Mac/getInstance algo) (.init signing-key))]
    (str "sha1="
         (String. (Base64/encodeBase64 (.doFinal mac (.getBytes data)))
                  "UTF-8"))))

Calling it on a particular body with a particular hook-secret set, gives me "sha1=VtNhKZDOHPU4COL2FSke2ArvtQE=". Meanwhile, the header I get from Github is sha1=56d3612990ce1cf53808e2f615291ed80aefb501.

Clearly, Github is printing in hex, but all my attempt to format the output as hex led to much longer strings than that one. What am I doing wrong?


Solution

  • You are Base64 encoding the digest, whereas you need to convert it to hex. You can do this as @RedDeckWins recommends using map, but it would probably be more efficient to use a Java library. This answer to a similar question uses org.apache.commons.codec.binary.Hex to do the encoding.