Developer Guide

Crypto Digests in Hex: hexdigest vs Raw Bytes

Cryptographic libraries almost always give you two views of the same digest: raw bytes and a hex string (hexdigest). Confusing them causes failed signature checks, doubled hex encoding, and security review findings that look silly but block releases.

Convert display strings carefully with Hex to String (for inspection only) and read Hex vs Base64 when APIs disagree on transport format.

Raw digest vs hexdigest

import hashlib
msg = b"test"
raw = hashlib.sha256(msg).digest()      # 32 bytes
hx = hashlib.sha256(msg).hexdigest()    # 64 hex chars
assert raw.hex() == hx
assert bytes.fromhex(hx) == raw

Never feed a hex string into an API that expects raw bytes without decoding first—and never hex() a value that is already hex text.

The double-encoding bug

# Wrong: hex-encode an already-hex digest
bad = hashlib.sha256(b"test").hexdigest().encode().hex()
# Right: pick one representation
good = hashlib.sha256(b"test").hexdigest()

Symptoms: length doubles (64 → 128 chars for SHA-256), verifiers fail, and the string no longer matches published checksums.

Hex vs Base64 for digests

Git and most Linux distro checksum files use lowercase hex. Some web APIs prefer Base64 or Base64url. Neither is “more secure.” Match the verifier:

  1. Read the API doc for encoding.
  2. Compare lengths (SHA-256: 32 raw bytes, 64 hex chars, 44 Base64 chars with padding).
  3. Normalize case for hex before compare if the spec says case-insensitive.

Keys and secrets

import hmac
def hex_digest_equal(a_hex: str, b_hex: str) -> bool:
    return hmac.compare_digest(bytes.fromhex(a_hex), bytes.fromhex(b_hex))

Language footnotes

Publishing checksums for downloads

Release pages usually show hex digests so users can verify ISOs and archives. Good practice:

If you also offer Base64 digests, label them clearly so nobody compares across encodings.

FAQ

Is a hex digest encrypted?
No. Digests are one-way fingerprints. Hex only prints them.

Should I store password hashes as hex?
Follow your password-hashing library. Many store an algorithm-specific string format, not bare SHA hex. Do not invent a scheme with raw SHA-256(password).

Why don’t two hex digests match?
Different inputs, different algorithms, uppercase/lowercase policies, or one side Base64. Also check for trailing newlines in files you hashed.

Related: hex decode pitfalls · PHP string to hex · hex overview.