OpenSSL Cheatsheet
Random and Passwords
Use this OpenSSL reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Generate random bytes
rand takes a byte count and writes to stdout. The count is bytes, never
characters, which matters the moment you add an encoding.
openssl rand -hex 32 # 64 hex chars, 256 bits openssl rand -base64 32 # 44 base64 chars, 256 bits openssl rand -base64 24 # 32 base64 chars, no = padding openssl rand 32 > key.bin # raw binary openssl rand -out key.bin 32 # same, explicit
| Bytes | -hex chars | -base64 chars | Typical use |
|---|---|---|---|
| 16 | 32 | 24 | Session id, IV, salt |
| 24 | 48 | 32 | API key, no padding |
| 32 | 64 | 44 | Secret key, JWT signing secret |
| 48 | 96 | 64 | Long-lived credential |
| 64 | 128 | 88 | HMAC-SHA512 key |
Base64 output length is 4 × ceil(n/3). Multiples of 3 bytes give no =
padding, which is why 24 and 48 look tidier than 32.
Size suffixes work for large amounts:
openssl rand 1K > 1kb.bin openssl rand 10M > 10mb.bin
Passwords people will type
Base64 contains + and /, which break shell quoting, URLs, and connection
strings. Filter them out when the value gets pasted somewhere.
# URL and shell safe, 32 chars openssl rand -base64 48 | tr -d '+/=' | cut -c1-32 # hex only, unambiguous over the phone openssl rand -hex 16 # alphanumeric, exactly 20 chars LC_ALL=C tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 20; echo
tr -d after generating shortens the string, so generate extra. Asking for
48 bytes and trimming to 32 characters keeps well over 128 bits of entropy even
in the worst case.
A few one-liners worth keeping:
openssl rand -base64 32 # generic app secret openssl rand -hex 32 # JWT / cookie signing key openssl rand -base64 24 | tr -d '+/=' # DB password openssl rand -hex 16 # request id, correlation id openssl rand -base64 32 | tr -d '\n' # strip trailing newline
Password hashes for config files
passwd produces crypt-format hashes for /etc/shadow, Apache htpasswd, and
similar files. This is not for application passwords.
openssl passwd -6 'plaintext' # SHA-512 crypt, $6$ openssl passwd -5 'plaintext' # SHA-256 crypt, $5$ openssl passwd -apr1 'plaintext' # Apache MD5, $apr1$ openssl passwd -1 'plaintext' # MD5 crypt, $1$ openssl passwd -6 -stdin # read from stdin, keeps it out of history
$ openssl passwd -6 'hunter2' $6$C0oOWA6yqvNz3Jke$RVeLbCPfoiYPPxfHqEZfmz0S...
| Flag | Scheme | Verdict |
|---|---|---|
-6 | SHA-512 crypt | Use this if you must pick one here |
-5 | SHA-256 crypt | Acceptable |
-apr1 | Apache MD5 | Only for Apache basic auth |
-1 | MD5 crypt | Legacy, weak |
-aixmd5 | AIX MD5 crypt | AIX interop only |
OpenSSL has no bcrypt and no Argon2 in passwd, and -crypt (classic DES
crypt) was removed in 3.0. For application passwords use your language's
Argon2id or bcrypt binding, or mkpasswd / htpasswd -B.
Key derivation from a password
When you need a key from a password rather than a random one, kdf is the right
subcommand. Length is in bytes.
# PBKDF2, 600k iterations, 32-byte key openssl kdf -keylen 32 -kdfopt digest:SHA256 \ -kdfopt pass:mypassword -kdfopt hexsalt:0011223344556677 \ -kdfopt iter:600000 PBKDF2 # scrypt, memory hard, better against GPUs openssl kdf -keylen 32 -kdfopt pass:mypassword \ -kdfopt hexsalt:0011223344556677 \ -kdfopt n:32768 -kdfopt r:8 -kdfopt p:1 SCRYPT # HKDF, for expanding an existing high-entropy secret openssl kdf -keylen 32 -kdfopt digest:SHA256 \ -kdfopt hexkey:00112233 -kdfopt hexsalt:aabb \ -kdfopt info:app-v1 HKDF
The KDF name goes last, after every -kdfopt. Output is colon separated
uppercase hex, so strip the colons for anything downstream:
openssl kdf -keylen 32 -kdfopt digest:SHA256 -kdfopt pass:pw \ -kdfopt hexsalt:00112233 -kdfopt iter:600000 PBKDF2 | tr -d ':\n'
Entropy and where randomness comes from
OpenSSL 3.x seeds from the operating system CSPRNG (getrandom on Linux,
getentropy on BSD and macOS, BCryptGenRandom on Windows) and reseeds
automatically.
openssl rand -hex 32 # already cryptographically secure cat /dev/urandom | head -c 32 | xxd # same source, no OpenSSL needed
- ~/.rnd and -rand are historical. Modern builds do not need a seed
file, and RANDFILE in openssl.cnf is ignored.
- unable to write 'random state' is harmless on 1.x builds without a
writable home directory. It does not mean the output was weak.
- Inside a container or a freshly booted VM, the kernel CSPRNG may block briefly
before it is seeded. getrandom waits rather than returning weak bytes, which
is the correct behaviour.
Gotchas
- -hex and -base64 do not change the entropy, only the encoding.
rand -hex 16 and rand -base64 16 are both 128 bits.
- rand -base64 N output can contain +, /, and =. Unescaped in a YAML
value, a URL, or a Postgres connection string, any of them break something.
- openssl rand writes a trailing newline for -hex and -base64. Pipe
through tr -d '\n' before putting it in a file that must not have one.
- openssl passwd 'secret' puts the password in your shell history. Use
-stdin and pipe it in.
- openssl passwd with no algorithm flag defaults to the weakest scheme the
build still offers. Always pass -6 explicitly.
- Never use $RANDOM, date +%s, or a language's default Math.random for
anything a user must not guess. They are not cryptographic.