Crypto recipe

Hashing with SHA-256

Compute a SHA-256 digest for integrity, deduplication, and content addressing.

A cryptographic hash maps any input to a fixed-size fingerprint. SHA-256 is the safe default; SHA-512 and SHA-3 are fine too. MD5 and SHA-1 are broken — never use them for anything security-relevant.

Hashing is for integrity, deduplication, and content addressing. It is NOT how you store passwords: a raw hash is far too fast to compute. For passwords see the password hashing guide; when a secret key is involved, use HMAC.

Get it right

  • Default to SHA-256 (or SHA-512 / SHA-3). Never MD5 or SHA-1.
  • A hash is not encryption and not a MAC — anyone can recompute it.
  • Never hash passwords with a plain hash; use Argon2id.
  • Hash raw bytes — fix your text encoding (UTF-8) before hashing strings.

Implementation

Setup Standard library (crypto/sha256). For SHA-512 use crypto/sha512.

Go
package main

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
)

func main() {
	data := []byte("hello, world")

	sum := sha256.Sum256(data) // [32]byte
	fmt.Println(hex.EncodeToString(sum[:]))
}

Setup Standard library (hashlib).

Python
import hashlib

data = b"hello, world"

digest = hashlib.sha256(data).digest()     # 32 raw bytes
hexsum = hashlib.sha256(data).hexdigest()  # 64-char hex string

For large files, stream with .update() in a loop instead of loading it all into memory.

Setup Built in (node:crypto).

Node.js
import { createHash } from 'node:crypto';

const data = 'hello, world';

const hex = createHash('sha256').update(data).digest('hex'); // 64-char hex string
const raw = createHash('sha256').update(data).digest();      // 32-byte Buffer

In the browser use WebCrypto (async): await crypto.subtle.digest("SHA-256", bytes).

Setup Built in. SHA256.HashData is .NET 5+.

.NET
using System.Security.Cryptography;
using System.Text;

byte[] data = Encoding.UTF8.GetBytes("hello, world");

byte[] hash = SHA256.HashData(data); // 32 bytes; one-shot, nothing to dispose
string hex = Convert.ToHexString(hash).ToLowerInvariant();

Setup libsodium.

C++
#include <sodium.h>
#include <string>

int main() {
    if (sodium_init() < 0) return 1;

    std::string data = "hello, world";
    unsigned char hash[crypto_hash_sha256_BYTES]; // 32

    crypto_hash_sha256(hash,
        reinterpret_cast<const unsigned char *>(data.data()), data.size());
}

When you don't specifically need SHA-256, libsodium's crypto_generichash (BLAKE2b) is faster and the recommended default.

Setup Built in (java.security.MessageDigest).

Java
import java.security.MessageDigest;
import java.nio.charset.StandardCharsets;

byte[] data = "hello, world".getBytes(StandardCharsets.UTF_8);

MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest(data); // 32 bytes

MessageDigest is not thread-safe — get a fresh instance per use (or per thread).

Setup Cargo.toml: sha2 = "0.10".

Rust
use sha2::{Digest, Sha256};

fn main() {
    let data = b"hello, world";

    let hash = Sha256::digest(data); // 32-byte output
    println!("{:x}", hash);
}