OpenSSL Cheatsheet
Hashing and HMAC
Use this OpenSSL reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Hash a file or a string
dgst takes an algorithm flag and any number of files. Each digest algorithm is
also its own subcommand shortcut.
openssl dgst -sha256 file.txt openssl sha256 file.txt # same thing openssl dgst -sha256 a.txt b.txt c.txt # one line per file echo -n "hello" | openssl dgst -sha256 # stdin, no filename in output printf 'hello' | openssl sha256
$ openssl dgst -sha256 f.txt
SHA2-256(f.txt)= 5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e...OpenSSL 3.x prints SHA2-256(...), not SHA256(...). Any script grepping
for the old label breaks on upgrade. Use -r for the coreutils format instead,
which is stable and diffable against sha256sum:
$ openssl dgst -sha256 -r f.txt 5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03 *f.txt openssl dgst -sha256 -r f.txt | cut -d' ' -f1 # bare hex
Algorithms
openssl dgst -list prints every digest the build supports.
| Flag | Output bits | Use |
|---|---|---|
-sha256 | 256 | The default choice for everything |
-sha384, -sha512 | 384, 512 | Longer digests, faster than SHA-256 on 64-bit |
-sha512-256 | 256 | SHA-512 truncated, avoids length extension |
-sha3-256, -sha3-512 | 256, 512 | Keccak family, different construction |
-shake128, -shake256 | variable | Needs -xoflen N |
-blake2b512, -blake2s256 | 512, 256 | Fast, not in TLS |
-sm3 | 256 | Chinese national standard |
-sha1 | 160 | Collisions are practical, legacy verification only |
-md5 | 128 | Broken, checksums against old tools only |
openssl dgst -shake256 -xoflen 64 file.txt openssl dgst -list | tr -s ' ' '\n' | grep sha3
Do not hash passwords with any of these. A digest is not a password hash. Use Argon2id, scrypt, or bcrypt in your application, or
openssl kdfwithSCRYPTorPBKDF2if you must stay inside OpenSSL.
HMAC
HMAC is a keyed digest, which is what you want for webhook signatures and API request signing.
openssl dgst -sha256 -hmac "secretkey" file.txt echo -n "payload" | openssl dgst -sha256 -hmac "secretkey" echo -n "payload" | openssl dgst -sha256 -hmac "secretkey" -binary | base64
$ openssl dgst -sha256 -hmac "secretkey" f.txt HMAC-SHA2-256(f.txt)= e306479813b3b6fb2a859ffa8f465f69bae4deed367b91...
The mac subcommand is the 3.x way and takes a hex key, so it handles binary
keys that -hmac cannot express. The MAC name comes last, after every option.
openssl mac -digest SHA256 -macopt key:secretkey -in f.txt HMAC openssl mac -digest SHA256 -macopt hexkey:0011deadbeef -in f.txt HMAC openssl mac -digest SHA256 -macopt key:k -in f.txt -binary HMAC | base64
-macopt accepts key: for ASCII and hexkey: for hex. Other MAC names:
CMAC (with -cipher AES-256-CBC), KMAC128, KMAC256, GMAC, Poly1305.
Reproducing a webhook signature, the thing people actually come here for:
# Stripe / GitHub style: HMAC-SHA256 of the raw body, hex printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1 # base64 flavour (AWS, Slack legacy) printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -binary | openssl base64 -A
Sign and verify with a key
Same subcommand, but with a private key instead of an HMAC secret. This is a real digital signature, verifiable by anyone holding the public key.
# sign openssl dgst -sha256 -sign key.pem -out file.sig file.txt # verify with the public key openssl pkey -in key.pem -pubout -out pub.pem openssl dgst -sha256 -verify pub.pem -signature file.sig file.txt # verify with the private key (it contains the public parts) openssl dgst -sha256 -prverify key.pem -signature file.sig file.txt
Verified OK Verification failure
Base64 armour the signature for anything text based, and use RSA-PSS rather than the default PKCS#1 v1.5 padding on new systems:
openssl dgst -sha256 -sign key.pem file.txt | openssl base64 -A > file.sig.b64 openssl dgst -sha256 -sign key.pem \ -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 \ -out file.sig file.txt openssl dgst -sha256 -verify pub.pem \ -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 \ -signature file.sig file.txt
Ed25519 signs the whole message and refuses a pre-hash, so use pkeyutl:
openssl pkeyutl -sign -inkey ed.key -rawin -in file.txt -out file.sig openssl pkeyutl -verify -pubin -inkey ed.pub -rawin -in file.txt \ -sigfile file.sig
Gotchas
- echo adds a newline. echo "hello" | openssl sha256 hashes 6 bytes, not
5, and will never match the hash your language library produced. Use
echo -n or printf '%s'.
- -hmac puts the secret in your shell history and process list. Read it
from a file or an environment variable in anything real.
- HMAC comparison must be constant time. Comparing in a shell with = is
fine for debugging, never for a request handler.
- Verified OK goes to stdout, and the exit code is what you check. 0 for
a valid signature, 1 otherwise.
- The digest used to sign and the digest used to verify must match. -sha256
signing verified with -sha512 always fails, with a message that says nothing
about the mismatch.
- -sigopt must be repeated identically on both sides. A PSS signature checked
without the PSS options reads as a plain failure.