暗号の基礎(共通鍵・公開鍵・ハッシュ・署名)
暗号は「隠す」だけの道具ではない。秘匿(共通鍵/公開鍵)・改ざん検知(ハッシュ)・本人保証(署名)という別目的の道具を、用途で正しく使い分けるのが要点。
- 1.暗号の道具は大きく4つ。共通鍵・公開鍵(隠す)、ハッシュ(指紋=改ざん検知)、電子署名(本人&改ざん防止)で、それぞれ目的が違う。
- 2.共通鍵は速いが鍵の配り方が課題。公開鍵は遅いが鍵を安全に渡せる。だから実務は両者を組み合わせる(ハイブリッド)。
- 3.「署名」は中身を隠さない。改ざん検知と本人確認のための仕組みで、秘匿が要るなら別途“暗号化”する――ここが一番の誤解ポイント。
そもそも何を守るのか
「暗号で守る」と一括りにされますが、守る対象は3つに分かれます。ここを混同すると「暗号化したのに安全じゃない」が起きます。
- 機密性(Confidentiality):第三者に読まれない。盗聴対策。→ 暗号化(共通鍵・公開鍵)
- 完全性(Integrity):途中で書き換えられていない。改ざん検知。→ ハッシュ・署名
- 真正性(Authenticity):確かにその相手が作った。なりすまし防止。→ 電子署名・証明書
暗号化が守るのは機密性だけです。暗号化された通信でも、相手が偽サーバなら(真正性なし)意味がなく、暗号文を途中で破壊・差し替えられても(完全性なし)気づけないことがあります。だから実システムは、暗号化に改ざん検知(ハッシュ/署名)と相手確認(証明書)をセットで使います。「隠す」と「改ざん防止」と「本人確認」は別の問題だと押さえてください。
共通鍵暗号(対称):速いが鍵配送が課題
暗号化と復号に同じ1本の鍵を使う方式です。送り手と受け手が同じ鍵を共有しておけば、その鍵で隠したり戻したりできます。仕組みが単純なぶん非常に高速で、大量データの暗号化に向きます(代表例は AES-GCM)。
弱点はただ一つ、**「その共通鍵をどうやって相手に安全に渡すか」**です。鍵そのものが盗まれたら全て解読されてしまうのに、鍵を送る経路がまだ暗号化されていない――という鶏と卵の問題(鍵配送問題)が生じます。
// 共通鍵暗号:同じ鍵で暗号化も復号もする(双方向)
const key = randomKey(); // この1本を相手と「安全に」共有できるかが課題
const enc = encryptAES(plaintext, key); // 暗号化
const dec = decryptAES(enc, key); // 同じ鍵で復号 → 元に戻る
公開鍵暗号(非対称):鍵配送を解く代わりに遅い
鍵を**「公開鍵」と「秘密鍵」のペアにしたのが公開鍵暗号です。公開鍵で暗号化したものは、対になる秘密鍵でしか復号できません。公開鍵はその名のとおり誰に配ってもよい**ので、共通鍵暗号の弱点だった鍵配送問題を解けます。
- 受け手は公開鍵を世界中に公開し、秘密鍵は自分だけが厳重に保管する。
- 送り手は受け手の公開鍵で暗号化して送る。復号できるのは秘密鍵を持つ受け手だけ。
ただし計算が重く、共通鍵に比べて桁違いに遅い。そのため大量データをそのまま公開鍵で暗号化することはせず、**「最初の鍵交換だけ」**に使うのが定石です(代表例は RSA / ECDH)。
速い共通鍵と、鍵を安全に渡せる公開鍵を組み合わせるのが現実解です。「遅いが安全な公開鍵で“共通鍵そのもの”を安全に共有し、以降の大量データは速い共通鍵で暗号化する」。TLS のハンドシェイクはまさにこれで、最初だけ公開鍵方式で鍵を共有し、本データは共通鍵で流します。
両者は性質がほぼ対照的です。
| 観点 | 共通鍵暗号(対称) | 公開鍵暗号(非対称) |
|---|---|---|
| 鍵 | 1本の共通鍵を共有 | 公開鍵+秘密鍵のペア |
| 速度 | 速い(大量データ向き) | 遅い(少量・鍵交換向き) |
| 鍵配送 | 鍵を安全に渡すのが難題 | 公開鍵は配ってよい=渡しやすい |
| 主な用途 | 実データの暗号化 | 鍵交換・電子署名 |
| 代表例 | AES-GCM / ChaCha20 | RSA / ECDH / ECDSA |
ハッシュ:戻せない「データの指紋」
ハッシュ関数は、任意長のデータを固定長の短い値(ハッシュ値)に潰す一方向の関数です。暗号化と決定的に違うのは、元に戻せないこと。鍵もなく、ハッシュ値から元データを復元する手段はありません。
良いハッシュ関数(SHA-256 など)には次の性質があります。
- 一方向性:ハッシュ値から元データを求められない。
- 衝突困難性:違う入力が同じハッシュ値になるペアを、現実的に作れない。
- 雪崩効果:入力が1ビット変わるだけで、出力が全く別物になる。
sha256("hello") // → 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
sha256("hellp") // → 一文字違うだけで全く別の値になる(雪崩効果)
// ハッシュ値から "hello" を逆算する方法は無い(一方向)
この「少しでも変われば指紋が変わる」性質から、ハッシュは**改ざん検知(完全性)**やファイルの同一性チェックに使われます。配布ファイルのチェックサム、Git のコミットID なども実体はハッシュです。
パスワードの保存に SHA-256 などの汎用ハッシュをそのまま使うのは危険です。汎用ハッシュは高速なため、攻撃者が大量の候補を片端から試す総当たりや、事前計算したレインボーテーブルに弱い。パスワードには、利用者ごとに異なるソルトを足し、**わざと計算を遅くした専用関数(bcrypt / scrypt / Argon2)**を使うのが正解です。詳しくは パスワードの安全な保存 を参照。
電子署名:本人+改ざんを同時に保証
ここまでの「秘匿(暗号化)」とは目的が違います。電子署名が証明するのは、「確かにこの人が作った(真正性/なりすまし防止)」かつ「作られてから書き換えられていない(完全性/改ざん防止)」の2点です。中身を隠すものではないことに注意。
仕組みは公開鍵の「逆向きの使い方」です。
- 署名(送り手):データのハッシュを計算し、それを自分の秘密鍵で変換する。これが署名。データに添えて送る。
- 検証(受け手):受け取ったデータから自分でもハッシュを計算。一方、署名を送り手の公開鍵で復元し、両者が一致するか比べる。
// 署名する側(秘密鍵は本人だけが持つ)
const h = sha256(message); // まずハッシュ(=指紋)を取り
const sig = signWithPrivateKey(h); // 自分の秘密鍵で署名
// 検証する側(公開鍵は誰でも持てる)
const ok =
sha256(message) === recoverWithPublicKey(sig); // 一致すれば「本人&未改ざん」
// → 1ビットでも改ざんされればハッシュがずれ、検証に落ちる
// → 秘密鍵を持たない第三者は正しい署名を作れない=なりすませない
秘密鍵を持つ本人しか作れない署名が、誰でも持てる公開鍵で検証できる。だから「本人性」が示せます。同時に、署名はハッシュに対して作られているので、中身が1ビットでも変わればハッシュがずれ、改ざんが必ずバレます。なお JWT の署名や、TLS のサーバ証明書(CA が秘密鍵で署名し、ブラウザが CA の公開鍵で検証する)も、この電子署名そのものです。
署名は中身を隠しません。JWT のペイロードが「署名付きだから安全」と思い込み、見られて困る情報(パスワード・個人情報)を入れてしまうのは典型的な事故です。署名済み=改ざんは検知できるが、中身は誰でも読める。秘匿が必要なら別途暗号化してください。「署名(完全性・真正性)」と「暗号化(機密性)」は別の道具です。
4つの道具を一望する
守る対象(機密性・完全性・真正性)と鍵の持ち方で整理すると、役割の違いがはっきりします。
| 道具 | 守るもの | 鍵 | 戻せる? | 代表例 |
|---|---|---|---|---|
| 共通鍵暗号 | 機密性(秘匿) | 共通鍵を共有 | 復号できる(双方向) | AES-GCM |
| 公開鍵暗号 | 機密性+鍵配送 | 公開鍵/秘密鍵 | 復号できる(双方向) | RSA / ECDH |
| ハッシュ | 完全性(指紋) | 鍵なし | 戻せない(一方向) | SHA-256 |
| 電子署名 | 真正性+完全性 | 秘密鍵で署名/公開鍵で検証 | 戻すものではない | RSA / ECDSA |
つまずきポイント
実務でハマりやすい誤解を、ここまでの要点として整理します。
- 「暗号化したから安全」ではない:暗号化が守るのは機密性だけ。改ざん検知には AEAD(AES-GCM 等)や署名、なりすまし防止には署名/証明書が別途要ります。
- 鍵を自分で実装しない:暗号の事故は「アルゴリズムが弱い」より「使い方を間違える」(IV の使い回し、ECB モード、自前のソルト生成ミスなど)で起きます。標準ライブラリ・確立した実装に任せ、自作しないのが鉄則です。
- 乱数の質が命:鍵やソルト、IV は**暗号論的に安全な乱数(CSPRNG)**で生成します。
Math.random()のような予測可能な乱数を鍵に使うと、暗号強度がゼロになります。 - 鍵の寿命と保管:鍵やパスワードのハッシュは漏洩を前提に設計します。ローテーション(定期更新)と、秘密鍵を**秘密管理(KMS / シークレットマネージャ)**に置くことを前提に。
隠したい(読まれたくない)→ 暗号化(少量・鍵交換なら公開鍵、大量データは共通鍵、実務はハイブリッド)。改ざんを検知したい → ハッシュ/署名。本人だと証明したい → 電子署名。パスワードを保存したい → bcrypt / scrypt / Argon2(汎用ハッシュ不可)。応用形として、これらを通信路で束ねたものが TLS / HTTPS です。
関連トピックとして、署名済みトークンの扱いは Cookie・セッション・JWT、暗号化通信の全体像は TLS / SSL、パスワードの保存実務は パスワードの安全な保存 を参照してください。
セキュリティ Article
暗号の基礎(共通鍵・公開鍵・ハッシュ・署名)を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
暗号
比較で見る軸
難易度: basic / カテゴリ: セキュリティ / タグ数: 4
導入後に効く点
共通鍵は速いが鍵の配り方が課題。公開鍵は遅いが鍵を安全に渡せる。だから実務は両者を組み合わせる(ハイブリッド)。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- basic
- カテゴリ
- セキュリティ
- タグ数
- 4
判断チェックリスト
- 自社の用途が「暗号 / 公開鍵」に近いか確認する。
- 強みである「暗号の道具は大きく4つ。共通鍵・公開鍵(隠す)、ハッシュ(指紋=改ざん検知)、電子署名(本人&改ざん防止)で、それぞれ目的が違う。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。