Go by Example

SHA256 Hashes

crypto/sha256 for one-shot and streaming SHA-256 hashing, hex encoding the digest, and when not to use SHA256 for passwords.

The crypto/sha256 package computes SHA-256 digests. Use sha256.Sum256 for small byte slices; use sha256.New() with io.Copy for large files or streams.

sha256.Sum256 hashes a byte slice in one call and returns a [32]byte array. hex.EncodeToString converts the raw bytes to a lowercase hex string - the common representation for checksums and content hashes.

package main
 
import (
    "crypto/sha256"
    "encoding/hex"
    "fmt"
)
 
func main() {
    data := []byte("hello, world")
    digest := sha256.Sum256(data)
 
    fmt.Printf("bytes: %x\n", digest)
    fmt.Println("hex:  ", hex.EncodeToString(digest[:]))
}

For large inputs, use sha256.New() to get a hash.Hash and write data incrementally. This avoids loading the entire content into memory and works with any io.Reader.

package main
 
import (
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "strings"
)
 
func main() {
    h := sha256.New()
 
    // Write data in chunks (simulated here with a Reader)
    r := strings.NewReader("hello, world - this could be a large file")
    buf := make([]byte, 8)
    for {
        n, err := r.Read(buf)
        if n > 0 {
            h.Write(buf[:n])
        }
        if err != nil {
            break
        }
    }
 
    fmt.Println(hex.EncodeToString(h.Sum(nil)))
}

h.Sum(nil) appends the current digest to a nil slice, returning a fresh []byte. You can also call h.Sum(existing[:0]) to write into a pre-allocated buffer without an allocation.

package main
 
import (
    "crypto/sha256"
    "fmt"
    "io"
    "os"
)
 
func hashFile(path string) ([]byte, error) {
    f, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer f.Close()
 
    h := sha256.New()
    if _, err := io.Copy(h, f); err != nil {
        return nil, err
    }
    return h.Sum(nil), nil
}
 
func main() {
    digest, err := hashFile("go.sum")
    if err != nil {
        fmt.Println("open go.sum:", err)
        return
    }
    fmt.Printf("%x\n", digest)
}

In production

sha256.Sum256 hashes a byte slice in one call - convenient for small inputs, but it loads the entire content into memory. For files, use sha256.New() + io.Copy so the file is streamed through the hasher. Never use SHA-256 for password storage: it is a general-purpose hash, not a key derivation function. Use bcrypt, argon2id, or scrypt for passwords - they are intentionally slow and include salting. SHA-256 is appropriate for content addressing (deduplication, checksums), HMAC signatures, and certificate fingerprints. When comparing digests in security-sensitive code, use subtle.ConstantTimeCompare from crypto/subtle to prevent timing attacks; a naive bytes.Equal or == can leak timing information.

Enjoyed this? Get more essays on software craft delivered to your inbox.

Subscribe free