# The body exactly as sent: printf adds no newline (echo would, and change the MAC)printf '%s' 'Hello, World!' > body.bin # HMAC-SHA256 keyed with the shared secret; -r prints "<hex> *<file>"mac=$(openssl dgst -sha256 -hmac "It's a Secret to Everybody" -r body.bin | cut -d' ' -f1)echo "X-Hub-Signature-256: sha256=$mac"# Check a captured delivery: its raw body (body.bin) and the header it came withheader='sha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17'expected="sha256=$(openssl dgst -sha256 -hmac "It's a Secret to Everybody" -r body.bin | cut -d' ' -f1)"if [ "$header" = "$expected" ]; then echo "signature OK"; else echo "signature MISMATCH"; fi# OpenSSL 3's MAC command gives the same MAC, in upper-case hexopenssl mac -digest SHA256 -macopt key:"It's a Secret to Everybody" -in body.bin HMAC # Binary secrets (e.g. from openssl rand -hex 32) go in as hexkey:secret=$(openssl rand -hex 32)openssl mac -digest SHA256 -macopt hexkey:$secret -in body.bin HMACHow it works
- Save the body exactly as it was sent —
printf '%s'writes it without a trailing newline — and runopenssl dgst -sha256 -hmac "<secret>" -r body.bin;-rprints<hex> *body.bin, andcutkeeps the hex. - For GitHub's example (
Hello, World!with the secretIt's a Secret to Everybody) the result is757107ea…3e17, so the header issha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17. - To verify, recompute over the captured body and compare with the header value; the run also shows a one-character change in the body producing a mismatch.
openssl mac -digest SHA256 -macopt key:<secret> -in body.bin HMACis OpenSSL 3's dedicated MAC command;-macopt hexkey:<hex>passes a binary secret such as the output ofopenssl rand -hex 32.
Gotchas
echo 'Hello, World!' | openssl dgst …signsHello, World!\n: the MAC becomes8fde2e97…2325instead of757107ea…3e17. Useprintf '%s'or the captured file, neverecho.openssl mactakes the algorithm name as its last argument: without the trailingHMACit stops withmac: Missing argument: MAC name.openssl dgstprints lower-case hex andopenssl macupper-case (757107EA…); providers send lower-case, so a plain string comparison againstopenssl macoutput fails unless you normalise the case.-hmacuses its argument's characters as the key. A secret generated as hex (openssl rand -hex 32) gives a different MAC as text than as the 32 bytes it encodes — the run shows both; when the provider means the bytes, pass-macopt hexkey:<hex>.