Skip to content

文字列をSHA-256でハッシュする snippet

SHA-256 はどんな入力も固定長32バイトのフィンガープリントに変えます。一方向で決定的ですが、暗号化ではありません。2つの落とし穴が繰り返し現れます。ハッシュするのは文字列ではなくUTF-8のバイト列であること。そして16進出力にはゼロ埋めされたバイトペアが必要なことです(b.toString(16) は先頭のゼロを黙って落とし、ダイジェストを壊します)。加えて不変のルールとして、SHA-256 はチェックサムとキーのためのものであり、決してパスワードには使いません。パスワードには bcrypt や argon2 のような遅いハッシュが必要です。SQLは pgcrypto 拡張が必要なため省略されており、C と C++ にはstdlibの暗号機能がありません。

SHA-256 はどんな入力も固定長32バイトのフィンガープリントに変えます。一方向で決定的ですが、暗号化ではありません。2つの落とし穴が繰り返し現れます。ハッシュするのは文字列ではなくUTF-8のバイト列であること。そして16進出力にはゼロ埋めされたバイトペアが必要なことです(b.toString(16) は先頭のゼロを黙って落とし、ダイジェストを壊します)。加えて不変のルールとして、SHA-256 はチェックサムとキーのためのものであり、決してパスワードには使いません。パスワードには bcrypt や argon2 のような遅いハッシュが必要です。SQLは pgcrypto 拡張が必要なため省略されており、C と C++ にはstdlibの暗号機能がありません。

Runnable recipe · 12 languageshash ツールを開く →
Crypto & Encodingsha256hashchecksumdigesthex

Every language

12 languages, copy-ready. One at a time with syntax highlighting, or all inline.

JSJavaScript
// crypto.subtle is async — the Web Crypto API, secure contexts only
const digest = await crypto.subtle.digest('SHA-256',
  new TextEncoder().encode('hello world'));

const hex = [...new Uint8Array(digest)]
  .map((b) => b.toString(16).padStart(2, '0'))
  .join('');
console.log(hex);

Node has the sync one-liner: crypto.createHash('sha256').update(s).digest('hex').

TSTypeScript
async function sha256Hex(input: string): Promise<string> {
  const digest = await crypto.subtle.digest(
    'SHA-256',
    new TextEncoder().encode(input),
  );
  return [...new Uint8Array(digest)]
    .map((b) => b.toString(16).padStart(2, '0'))
    .join('');
}

padStart(2, '0') is load-bearing — toString(16) drops the leading zero on bytes under 0x10 and silently breaks the hex string.

GoGo
sum := sha256.Sum256([]byte("hello world"))

fmt.Println(hex.EncodeToString(sum[:])) // or just fmt.Printf("%x\n", sum)

Sum256 returns a [32]byte array — sum[:] slices it into the []byte hex.EncodeToString wants.

RsRust
use sha2::{Digest, Sha256};

let hex = format!("{:x}", Sha256::digest(b"hello world"));
println!("{hex}");

sha2 is the RustCrypto canonical crate. {:x} on the generic-array output renders lowercase hex directly — no manual byte loop.

PHPPHP
$hex = hash('sha256', 'hello world');      // 64-char hex string
$raw = hash('sha256', 'hello world', true); // 32 raw bytes

echo $hex, PHP_EOL;

hash() is encoding-agnostic because PHP strings are byte strings. The third parameter true switches hex → raw output.

PyPython
import hashlib

hexdigest = hashlib.sha256(b'hello world').hexdigest()
raw = hashlib.sha256(b'hello world').digest()  # bytes, len 32

print(hexdigest)

The b'' literal is UTF-8 already; for runtime strings hash(s.encode()). hexdigest() and digest() are the same bytes, two renderings.

C#C#
using System.Security.Cryptography;

byte[] hash = SHA256.HashData("hello world"u8);
string hex = Convert.ToHexString(hash).ToLowerInvariant();

Console.WriteLine(hex);

SHA256.HashData (.NET 5+) replaced the Create()/TransformFinalBlock dance. Convert.ToHexString returns UPPERCASE — lower it if your consumer expects the common lowercase.

JvJava
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.HexFormat;

byte[] hash = MessageDigest.getInstance("SHA-256")
        .digest("hello world".getBytes(StandardCharsets.UTF_8));

String hex = HexFormat.of().formatHex(hash);

Java 17+ HexFormat killed the hand-rolled %02x StringBuilder loop that haunted every older example.

SwSwift
import CryptoKit

let hex = SHA256.hash(data: Data("hello world".utf8))
    .map { String(format: "%02x", $0) }
    .joined()
print(hex)

CryptoKit is Apple-platform; swift-crypto is the same API as a cross-platform Swift package. %02x — the zero-padding is what keeps the hex length at 64.

KtKotlin
import java.security.MessageDigest
import java.util.HexFormat

val hex = HexFormat.of().formatHex(
    MessageDigest.getInstance("SHA-256")
        .digest("hello world".toByteArray()),
)

Same JVM MessageDigest; toByteArray() is UTF-8 by default in Kotlin.

RbRuby
require 'digest'

hex = Digest::SHA256.hexdigest('hello world')
puts hex

Digest::SHA256.digest gives the 32 raw bytes; .hexdigest is the convention everyone else matches.

ZigZig
const std = @import("std");

pub fn main() !void {
    var out: [std.crypto.hash.sha2.Sha256.digest_length]u8 = undefined;
    std.crypto.hash.sha2.Sha256.hash("hello world", &out, .{});

    std.debug.print("{s}\n", .{std.fmt.fmtSliceHexLower(&out)});
}

The .{} options arg enables streaming mode for large inputs; init/update/final is the incremental API. fmtSliceHexLower formats without allocating.

Keep going

Try the interactive hash tool →