A command such as openssl rand -hex 32 is often described as generating a “random hash.” The output certainly looks like a SHA-256 digest: 64 hexadecimal characters. But no hash operation has happened. OpenSSL generated 32 random bytes and encoded them as hexadecimal.

That distinction matters. A hash function transforms input into a fixed-size digest. A cryptographically secure random number generator produces unpredictable bytes. If the requirement is a token, session secret, API credential, nonce, or other fresh random value, the random bytes are the important part. Hashing them afterward usually adds no useful unpredictability.

32 random bytes become 64 hexadecimal characters

Hexadecimal encoding represents each byte with two characters. Therefore:

32 random bytes × 2 hex characters per byte = 64 hex characters

OpenSSL can produce that representation directly:

openssl rand -hex 32

A typical result has this shape:

f5df8f0c8f4b2c3c2a3a9b967e327d193b42e87ef3e922c92a6d5b1b315c31ad

The value is random data rendered as hex, not the digest of another value. Assuming the generator is operating correctly, 32 random bytes contain 256 bits of random output before encoding. Hex changes the representation, not the amount of randomness.

Base64 is another representation:

openssl rand -base64 32

It carries the same 32 generated bytes but uses a more compact textual encoding than hex.

Linux already exposes a cryptographic random source

Linux maintains a kernel cryptographically secure pseudorandom number generator (CSPRNG). User-space software can obtain output from it through interfaces including getrandom() and /dev/urandom.

For shell work, reading 32 bytes from /dev/urandom and converting them to hex is straightforward:

head -c 32 /dev/urandom | xxd -p -c 32

The pipeline has two separate jobs:

/dev/urandom -> 32 unpredictable bytes -> hexadecimal encoding

Modern Linux documentation recommends the urandom source for normal cryptographic use. Applications that need randomness during very early boot should prefer getrandom(), because its default behavior waits until the kernel random pool has been initialized.

OpenSSL adds a convenient command-line interface. Its random generator seeds itself from trusted operating-system sources, so this is usually the simplest shell command when OpenSSL is installed:

openssl rand -hex 32

Hashing random bytes solves a different problem

This command is also valid:

head -c 32 /dev/urandom | sha256sum

Here, the data flow is different:

/dev/urandom -> 32 random bytes -> SHA-256 -> 256-bit digest

The SHA-256 step maps the random input to another 256-bit value. It can be useful when a protocol specifically requires a digest, but it does not turn weak randomness into strong randomness and is unnecessary merely to obtain a random-looking 64-character string.

The same distinction applies to:

openssl rand -base64 32 | sha256sum

The command first generates random bytes, encodes them as Base64 text, then hashes that text. If the only requirement is a 256-bit random token represented in hex, openssl rand -hex 32 expresses the intent more directly and avoids the extra transformation.

Length should be chosen in bytes before encoding

A common source of mistakes is choosing a token by its visible string length rather than its random-byte length.

For hex:

16 bytes = 128 bits = 32 hex characters
32 bytes = 256 bits = 64 hex characters
64 bytes = 512 bits = 128 hex characters

The corresponding commands are:

openssl rand -hex 16
openssl rand -hex 32
openssl rand -hex 64

The argument to openssl rand is the number of bytes to generate, not the number of output characters.

For most application secrets, the required entropy should come from the security design or protocol rather than from an arbitrary desire for a long string. More characters are not automatically useful if a protocol expects a specific size.

UUIDs are identifiers, not substitutes for arbitrary secret bytes

uuidgen is convenient when the requirement is a UUID:

uuidgen

A UUID has a defined structure and representation. It should be selected because the application needs UUID semantics, not because the output happens to look random.

Likewise, timestamps hashed with SHA-256 are poor substitutes for a CSPRNG:

date +%s | sha256sum

SHA-256 can hide the textual appearance of a timestamp, but it cannot create entropy that was absent from the input. An attacker who can narrow the timestamp to a small interval can hash the same candidate values.

Match the primitive to the requirement

The useful distinction is between generation, encoding, and hashing:

random generation: produce unpredictable bytes
encoding:          represent bytes as hex or Base64
hashing:           map input data to a fixed-size digest

For a fresh 256-bit value in a shell, the direct form is:

openssl rand -hex 32

Use sha256sum when the operation actually calls for SHA-256. Use a CSPRNG when unpredictability is the requirement. Keeping those operations separate makes shell commands easier to review and prevents a random-looking digest from being mistaken for evidence that its input was unpredictable.