はじめに 「SHA256生成」は“文字列に指紋をつける”行為
SHA256 は、どんな長さのデータでも「256ビット(32バイト)の固定長のハッシュ値」に変換する仕組みです。
イメージとしては、
「中身は見せないけど、“同じものかどうか”だけは確実に判定できる指紋を作る」
という感じです。
業務では、例えば次のような用途で使われます。
ログIDやトラッキングIDを、元の値を隠したまま追跡したい
ファイルや文字列が改ざんされていないかチェックしたい
キャッシュキーや重複チェック用のキーを固定長にしたい
ここでは、「文字列からSHA256を生成するC#ユーティリティ」を、
初心者向けに、仕組みからコードまでかみ砕いて説明していきます。
SHA256生成の基本の流れを言葉でつかむ
3ステップで考える
文字列からSHA256を作る流れは、たった3ステップです。
1つ目は、「文字列をバイト配列に変換する」。
2つ目は、「そのバイト配列に対してSHA256を計算する」。
3つ目は、「結果のバイト配列を、人間が扱いやすい形(16進文字列など)に変換する」。
C#では、この3つをそれぞれ、
Encoding.UTF8.GetBytes(...)SHA256.Create().ComputeHash(...)
バイト配列 → 16進文字列への変換
として実装していきます。
C#での基本実装:文字列 → SHA256(16進文字列)
まずは完成形のユーティリティを見てみる
いきなりですが、「業務でそのまま使えるレベル」のユーティリティを書いてみます。
using System;
using System.Security.Cryptography;
using System.Text;
public static class Sha256Util
{
public static string ComputeSha256Hex(string? text)
{
if (string.IsNullOrEmpty(text))
{
return string.Empty;
}
byte[] bytes = Encoding.UTF8.GetBytes(text);
using var sha = SHA256.Create();
byte[] hashBytes = sha.ComputeHash(bytes);
return ToHex(hashBytes);
}
private static string ToHex(byte[] bytes)
{
var sb = new StringBuilder(bytes.Length * 2);
foreach (byte b in bytes)
{
sb.Append(b.ToString("x2"));
}
return sb.ToString();
}
}
C#これを一行ずつ、ちゃんと意味が分かるように分解していきます。
各ステップを丁寧に分解する
文字列をバイト配列に変換する
byte[] bytes = Encoding.UTF8.GetBytes(text);
C#SHA256は「バイト列」に対して計算されます。
なので、まずは文字列をバイト配列に変換する必要があります。
ここで重要なのが「エンコーディングを固定する」ことです。
同じ "こんにちは" でも、UTF-8 と UTF-16 ではバイト列が変わり、ハッシュ値も変わります。
業務で「他システムと比較する」「ファイルに保存する」といった用途を考えると、
エンコーディングは UTF-8 に固定しておくのが安全です。
Encoding.UTF8.GetBytes(text); をユーティリティの中に埋め込んでしまうことで、
呼び出し側はエンコーディングを意識しなくて済むようになります。
SHA256を計算する
using var sha = SHA256.Create();
byte[] hashBytes = sha.ComputeHash(bytes);
C#SHA256.Create() は、SHA256アルゴリズムのインスタンスを作る工場メソッドです。using var を付けているのは、IDisposable なオブジェクトなので、
使い終わったら自動的に破棄してもらうためです。
ComputeHash(bytes) に、先ほどのバイト配列を渡すと、
長さ32バイト(256ビット)のハッシュ値が返ってきます。
この時点では「ただのバイト配列」なので、
そのままだとログに出したり、文字列として扱うのが不便です。
バイト配列を16進文字列に変換する
private static string ToHex(byte[] bytes)
{
var sb = new StringBuilder(bytes.Length * 2);
foreach (byte b in bytes)
{
sb.Append(b.ToString("x2"));
}
return sb.ToString();
}
C#ここでは、1バイトを2桁の16進数(00〜ff)に変換しています。
例えば、バイト値 15 は "0f"、255 は "ff" になります。
bytes.Length * 2 で StringBuilder の初期容量を決めているのは、
「32バイト → 64文字」になることが分かっているからです。
こうしておくと、余計な再確保が減って、少しだけ効率が良くなります。
結果として、ComputeSha256Hex("Hello") のように呼ぶと、
64文字の16進文字列が返ってくる、というわけです。
実際に動かしてみるイメージ
いくつかの文字列で試してみる
Console.WriteLine(Sha256Util.ComputeSha256Hex("Hello"));
Console.WriteLine(Sha256Util.ComputeSha256Hex("hello"));
Console.WriteLine(Sha256Util.ComputeSha256Hex("こんにちは"));
Console.WriteLine(Sha256Util.ComputeSha256Hex(""));
Console.WriteLine(Sha256Util.ComputeSha256Hex(null));
C#ポイントは次の通りです。
"Hello" と "hello" は、1文字違うだけですが、ハッシュ値はまったく違う
日本語("こんにちは")も問題なくハッシュ化できる(UTF-8でバイト列にしているため)
空文字や null は、ユーティリティ側で「空文字を返す」という方針にしている
この「nullや空文字をどう扱うか」は設計の好みですが、
業務ユーティリティとしては「例外を投げず、空文字を返す」ほうが呼び出し側が楽なことが多いです。
バイト配列のまま扱いたい場合のバリエーション
16進文字列ではなく、バイト配列で返す版
用途によっては、「16進文字列にせず、バイト配列のまま扱いたい」こともあります。
その場合は、こういうメソッドも用意できます。
public static byte[] ComputeSha256Bytes(string? text)
{
if (string.IsNullOrEmpty(text))
{
return Array.Empty<byte>();
}
byte[] bytes = Encoding.UTF8.GetBytes(text);
using var sha = SHA256.Create();
return sha.ComputeHash(bytes);
}
C#これを使えば、
ハッシュ値をそのままバイナリとしてファイルに書きたい
別の暗号処理や署名処理に渡したい
といった場面に対応できます。
SHA256ユーティリティを業務でどう使うか
「中身は見せたくないが、同じものかどうかは知りたい」場面
例えば、こんな使い方が考えられます。
ログにユーザーIDをそのまま残したくないので、SHA256にして保存する
外部から受け取ったファイルの内容をハッシュ化しておき、後で「同じファイルかどうか」を判定する
キャッシュのキーとして、「元のキー文字列のSHA256」を使う(キーを固定長にしたいとき)
どの場合も、「元の文字列は復元できないが、同じ入力なら同じハッシュになる」という性質を利用しています。
注意:パスワードには“そのままSHA256”を使わない
とても重要な注意点として、
ユーザーパスワードを扱うときに、
var hash = Sha256Util.ComputeSha256Hex(password);
C#のように「そのままSHA256にかける」のは、セキュリティ的には不十分です。
パスワードには、ソルト・ストレッチング・専用アルゴリズム(PBKDF2, bcrypt, Argon2 など)が必要になります。
ここは「SHA256ユーティリティ」の範囲を超えるので深掘りしませんが、
「SHA256は“パスワード以外”の文字列に使う」
と覚えておいてください。
まとめ 「SHA256生成ユーティリティ」は“文字列に安全な指紋をつけるための定番ツール”
ここまでのポイントを整理すると、こうなります。
SHA256は「どんな長さの文字列でも、256ビットの固定長ハッシュにする」仕組みである。
C#では、Encoding.UTF8.GetBytes → SHA256.Create().ComputeHash → 16進文字列化、という3ステップで実装する。
エンコーディング(UTF-8)とアルゴリズム(SHA-256)をユーティリティ側で固定しておくと、結果が安定し、呼び出し側が迷わない。
nullや空文字の扱いも、ユーティリティ側でルール化しておくと業務コードがすっきりする。
パスワードには“そのままSHA256”を使わず、専用の仕組みを使う。
ここまで理解できれば、「なんとなくハッシュを取っている」状態から一歩進んで、
“用途と安全性を意識したSHA256生成ユーティリティ”を、自分のC#コードの中に自然に組み込めるようになっていきます。
