Skip to content

Hashes, HMAC and Symmetric Encryption

Not everything in the box is certificates. OpenSSL also carries the everyday primitives: hashes that fingerprint data, a keyed variant that proves who sent it, symmetric encryption for files, and the random numbers everything else is built from. This chapter runs each once, honestly — including the one with warts.

Hashes: dgst

A hash turns any input into a fixed-size fingerprint — same input, same fingerprint; any change, a totally different one. (A certificate fingerprint in chapter 4 was exactly this, over the certificate.) On files:

$ printf 'the quick brown fox\n' > message.txt
$ openssl dgst -sha256 message.txt
SHA2-256(message.txt)= 6e459fed18ddb06d57c8e9f0d000c302c7e01389926db6e89884bfbe91a2a5df
$ sha256sum message.txt
6e459fed18ddb06d57c8e9f0d000c302c7e01389926db6e89884bfbe91a2a5df  message.txt

Two programs, one answer — the agreement is the point of a standard algorithm, and the cross-check is the point of the exercise. dgst with no algorithm defaults to SHA-256 in the 3.x line; name it anyway, because a fingerprint that doesn't say its algorithm will eventually be compared against the wrong one. SHA-256 is the workhorse; the MD5 and SHA-1 entries in openssl list -digest-algorithms are for reading old things, never for new fingerprints where collision resistance matters.

A hash proves data hasn't changed if you trust where you got the hash. It says nothing about who made the data — anyone can hash anything. That gap is what the keyed version fills.

HMAC: a hash with a key

HMAC mixes a secret key into the hash. Only someone holding the key can produce or check the result, so a matching HMAC proves the message came from a key-holder and arrived unchanged — authentication without certificates, for parties who already share a secret.

$ openssl dgst -sha256 -hmac "s3cret-key" message.txt
HMAC-SHA2-256(message.txt)= caa5b3091910d2a49213c3d8bcf1455921ac92fb3c9898a55e90675b9c9d7ebc

The 3.x line also has a general-purpose mac command — and a genuine paper cut, kept here because the tool really does it: HMAC through mac refuses to run until you name the digest explicitly.

$ openssl mac -macopt key:s3cret-key -macopt digest:SHA256 HMAC < message.txt
CAA5B3091910D2A49213C3D8BCF1455921AC92FB3C9898A55E90675B9C9D7EBC

Same bytes in, same MAC out (uppercase is cosmetic). Either command serves; dgst -hmac is the one-liner to remember.

Symmetric encryption: enc, with its caveats stated

enc encrypts a file with a password: one secret both encrypts and decrypts (symmetric, unlike the key pairs of chapter 5). The round trip:

$ openssl enc -aes-256-cbc -salt -pbkdf2 -in message.txt -out message.enc -pass pass:correct-horse
$ openssl enc -aes-256-cbc -d -pbkdf2 -in message.enc -pass pass:correct-horse
the quick brown fox

Now the honesty. enc announces its own main caveat, verbatim, the moment you omit -pbkdf2:

*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.

A password is not a key; it must be derived into one. The historical derivation is a single fast hash round — quick for you, and quick for an attacker trying a million passwords. -pbkdf2 derives through many rounds (10,000 by default) and is the least you should accept; -salt makes each file's derivation unique, and its mark is visible at the start of every enc file:

$ head -c 16 message.enc | od -c | head -2
0000000   S   a   l   t   e   d   _   _ 016 276 034 270   q   j 234   m

Salted__ plus eight bytes of salt, then the ciphertext. And the failure mode, with the wrong password:

$ openssl enc -aes-256-cbc -d -in message.enc -pass pass:wrong-horse
[...]
bad decrypt

— followed by garbage bytes on the terminal. enc has no integrity check: it cannot distinguish a wrong password from corrupted data, and a tampered ciphertext decrypts to tampered plaintext without complaint. For a file you toss across a desk it is fine; for anything that matters, use a tool built for the job (an AEAD cipher via a real file encryption program, e.g. age or gpg), where a wrong key fails cleanly and nothing silent slips through. This guide keeps enc because it is everywhere — scripts, tutorials, and legacy backups are full of it — and reading it correctly includes its limits.

Randomness and base64

Everything above stands on unpredictable numbers; rand is the tap:

$ openssl rand -hex 16
23fb607cabe501af651093c450998400
$ openssl rand -base64 24
Gx7aEFiexnATerViStU0r8G7PrIxVty9

This output feeds tokens, salts, and test data everywhere. Use the tool's, never a shell $RANDOM or a timestamp, for anything security-shaped.

Base64 — the encoding that makes PEM PEM — is its own tiny subcommand, worth knowing because half the binary blobs in computing arrive wearing it:

$ echo "hello, world" | openssl base64
aGVsbG8sIHdvcmxkCg==
$ echo "aGVsbG8sIHdvcmxkCg==" | openssl base64 -d
hello, world

The trailing = signs are padding, and a blob that won't decode is usually line-wrapped or truncated — try -A for one-line inputs that arrive without newlines.