121

Is there a bash script to generate a HMAC-SHA1 hash?

I'm looking for something equivalent to the following PHP code:

hash_hmac("sha1", "value", "key");
Ilmari Karonen
  • 49,047
  • 9
  • 93
  • 153
Mark
  • 67,098
  • 47
  • 117
  • 162

5 Answers5

236

I realise this isn't exactly what you're asking for, but there's no point in reinventing the wheel and writing a bash version.

You can simply use the openssl command to generate the hash within your script.

[me@home] echo -n "value" | openssl dgst -sha1 -hmac "key"
57443a4c052350a44638835d64fd66822f813319

Or simply:

[me@home] echo -n "value" | openssl sha1 -hmac "key"
57443a4c052350a44638835d64fd66822f813319

Remember to use -n with echo or else a line break character is appended to the string and that changes your data and the hash.

That command comes from the OpenSSL package which should already be installed (or easily installed) in your choice of Linux/Unix, Cygwin and the likes.

Do note that older versions of openssl (such as that shipped with RHEL4) may not provide the -hmac option.


As an alternative solution, but mainly to prove that the results are the same, we can also call PHP's hmac_sha1() from the command line:

[me@home]$ echo '<?= hash_hmac("sha1", "value", "key") ?>' | php
57443a4c052350a44638835d64fd66822f813319
acj
  • 4,821
  • 4
  • 34
  • 49
Shawn Chin
  • 84,080
  • 19
  • 162
  • 191
  • OpenSSL implementations are very slow. If you need to do it occasionally that's fine, but if you're trying to calculate massive amounts of hashes, you want to investigate different avenues. – Marcin Sep 02 '11 at 16:08
  • 1
    @Marcin: can you quote a source with that? – sehe Sep 12 '11 at 08:49
  • [John The Ripper](http://www.openwall.com/john/) is very fast for CPU implementations and it's Open Source. – Marcin Sep 12 '11 at 22:19
  • 7
    I had the same question with HMAC-SHA256. The same solution, but `sha1` is replaced with `sha256` :-) – mogsie Jan 26 '12 at 14:18
  • If I have the `"value"` in a file, can I just do: `cat signature.txt | openssl sha1 -hmac "key"`? – Kevin Meredith Apr 19 '13 at 12:08
  • 1
    Yes you can, but do watch out for linebreaks within your file as that would also be considered to be part of the value. – Shawn Chin Apr 19 '13 at 12:57
  • 2
    @ShawnChin, in this example, what is the encoding / format of the key? Should it be a base64 encoding like a private key created using `openssl genrsa`? Also, the openssl documentation link results in a 404. – Carlos Macasaet Aug 18 '15 at 05:07
  • How to pass key to openssl when "key" is a binary array that contains special character? – Chong Oct 05 '16 at 11:21
  • Thanks for your post. That extra line break at the end had me going for a while there. – martin Oct 30 '16 at 11:22
  • Do not use `echo -n`. It is not portable. Use printf instead. – josch Mar 09 '18 at 14:01
  • 3
    This worked for me on a Mac, but on a CentOS 7 machine `openssl` was outputting `(stdin)= ` before the hash. Adding `-binary | xxd -plain` to the end of the `openssl` command fixes this (see https://unix.stackexchange.com/a/90242/58450). – Tim Malone Jul 12 '18 at 01:22
  • While working in alpine docker I got the (stdin)= before the hash. I fixed it with: `printf "value" | openssl dgst -sha1 | awk '{ print $2 }'` – iChux Jul 31 '22 at 15:09
42

Here is a bash function that works like hash_hmac from PHP:

#!/bin/bash

function hash_hmac {
  digest="$1"
  data="$2"
  key="$3"
  shift 3
  echo -n "$data" | openssl dgst "-$digest" -hmac "$key" "$@"
}

# hex output by default
hash_hmac "sha1" "value" "key"

# raw output by adding the "-binary" flag
hash_hmac "sha1" "value" "key" -binary | base64

# other algos also work
hash_hmac "md5"  "value" "key"
Martin
  • 37,119
  • 15
  • 73
  • 82
  • That's a nice way to wrap it up. +1 – Shawn Chin Sep 12 '11 at 09:34
  • +1 because unlike the selected answer, this one answers the question asked. (Though both are helpful.) – Alexx Roche Jun 22 '13 at 13:30
  • but, how do you pass the 'data' argument to script if it is multi line? Like an xml or json body without loosing the indentation. – HyperioN Aug 12 '18 at 08:09
  • @HyperioN if you have your json data in a file you could simply do this: `hash_hmac "sha1" "$(cat your-json-file)" "key"`. Alternatively you could just pipe your file through `openssl dgst` without using this `hash_hmac` function. – Martin Aug 12 '18 at 11:27
  • Thank you for the -binary bit. That was the missing piece for me – jgreen Oct 07 '20 at 17:47
13

Thanks for the hash_hmac function! But it was not enough for my application. In case anyone wondered, I had to re-hash stuff several times using a key that was the result of the previous hashing, and therefore is a binary input. (The Amazon AWS authentication signature is created like this.)

So what I needed was a way to supply the binary key in some way that would not break the algorithm. Then I found this: http://openssl.6102.n7.nabble.com/command-line-hmac-with-key-in-hex-td6754.html

Stephen Henson's reply requires the hash_hmac function to return the value in hex format. So it needs to echo the following:

$ echo -n "$data" | openssl dgst "-$digest" -hmac "$key" | sed -e 's/^.* //'

Then the next call would need to provide the key as an hexit:

$ echo -n "$data" | openssl dgst "-$digest" -mac HMAC -macopt "hexkey:$key" | sed -e 's/^.* //'

Hopefully this helps anyone, probably someone who is trying to create bash scripts to invalidate CloudFront entries on AWS (like me!) (I haven't tested it yet, but I think this is the thing that is the cause of why my bash script does not work, and my PHP one does...)

Wouter Thielen
  • 1,016
  • 9
  • 21
0

Having node.js installed you can use HMAC-CLI tool:

npx hmac-cli generate 'value' -h sha1 -s key

returns:

57443a4c052350a44638835d64fd66822f813319
Terite
  • 1,097
  • 13
  • 23
-5

To those who like to explore more JWT on the command line: cool jwt bash script

daparic
  • 3,794
  • 2
  • 36
  • 38