SHA-256 hash in Python

hashlib.sha256(text.encode()).hexdigest()

hashlib.sha256(text.encode()).hexdigest() returns the SHA-256 of a string as 64 hex characters. .encode() gives the text’s UTF-8 bytes, which is what every other tool hashes.

import hashlib

hashlib.sha256("hello".encode()).hexdigest()
# '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'

The tool below shows the same digest for hello in its SHA-256 row.

Strings must be encoded

hashlib hashes bytes. A str is refused:

hashlib.sha256("hello")
# TypeError: Strings must be encoded before hashing

Encode with UTF-8, which is what .encode() does unless you name another codec. A string from input() or a file read in text mode is already a str; a line read with readline() keeps its newline, and that changes everything:

hashlib.sha256("hello\n".encode()).hexdigest()
# '5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03'

hexdigest or digest

.hexdigest() is the 64-character hex text that checksum files and APIs show. .digest() is the raw 32 bytes, for when the hash goes on to another step, such as Base64:

import base64

hashlib.sha256(b"hello").digest()
# b',\xf2M\xba_\xb0\xa3\x0e&\xe8;*\xc5\xb9\xe2\x9e\x1b\x16\x1e\\\x1f\xa7B^s\x043b\x93\x8b\x98$'
base64.b64encode(hashlib.sha256(b"hello").digest()).decode()
# 'LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ='

Calling .update() more than once hashes the pieces as one stream: update(b"hel") then update(b"lo") gives the same 2cf24dba… as hello.

Files

Open the file in binary mode and feed it in chunks, so a 4 GB image never sits in memory. Python 3.11 and later do this for you:

with open("ubuntu.iso", "rb") as f:
    print(hashlib.file_digest(f, "sha256").hexdigest())

On older versions, loop:

def sha256_file(path):
    h = hashlib.sha256()
    with open(path, "rb") as f:
        for chunk in iter(lambda: f.read(65536), b""):
            h.update(chunk)
    return h.hexdigest()

A file holding hello and nothing else gives 2cf24dba… with either. Text mode would hand you a str and turn Windows line endings into \n, so the bytes hashed wouldn’t be the file’s.

Keys and passwords

A hash with a secret mixed in is an HMAC, not sha256(secret + message), which SHA-256’s length-extension property makes forgeable:

import hmac

hmac.new(b"secret", b"hello", hashlib.sha256).hexdigest()
# '88aab3ede8d3adf94d26ab90d3bafd4a2083070c3bcce9c014ee04a443847c0b'

Don’t store passwords as SHA-256. It’s fast by design, so leaked hashes can be guessed in bulk; use a slow, salted function such as hashlib.scrypt, bcrypt or Argon2.

Try it in the Hash Generator

Nothing leaves your browser: every digest is worked out here, in the page.

Case and spaces are ignored, so a hash from a checksum file goes straight in.

Hashing the 5 bytes of UTF-8 text.

AlgorithmDigestCopy
MD5 · 128 bits · broken5d41402abc4b2a76b9719d911017c592
SHA-1 · 160 bits · brokenaaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
SHA-256 · 256 bits2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
SHA-384 · 384 bits59e1748777448c69de6b800d7a33bbfb9ff1b463e44354c3553bcdb9c666fa90125a3c79f90397bdf5f6a13de828684f
SHA-512 · 512 bits9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043

MD5 and SHA-1 are broken: collisions can be built on purpose, so a digest that matches proves nothing about who wrote a file. Use SHA-256 to check a file you downloaded. For passwords, use neither: bcrypt or Argon2 is slow on purpose and salts every hash, so a stolen list can’t be cracked in bulk.

Runs in your browser. Nothing you enter is sent anywhere.