「このメッセージは確かにあなたが書いた」を、第三者が客観的に検証できる形にしたものが ディジタル署名。共有鍵の MAC では「2 者のうちどちらが書いたか」を判別できませんが、署名なら 秘密鍵を持つ署名者しか作れない ので、否認防止が成立します。本講では ハッシュ関数と公開鍵暗号の合わせ技 として署名の仕組みを整理し、RSA-PSS、ECDSA、Ed25519 の主要方式、X.509 証明書、コードサイニング、Git コミット署名、PQC 署名(Dilithium・SPHINCS+) までを扱います。
本講を終えると、以下を達成できるようになります。
| 性質 | MAC(第38回) | ディジタル署名(本講) |
|---|---|---|
| 鍵 | 共有秘密鍵 1 つ | 秘密鍵 + 公開鍵 |
| 署名 | 共有鍵で計算 | 秘密鍵で計算 |
| 検証 | 共有鍵で計算し直す | 公開鍵で計算 |
| 検証可能な相手 | 共有鍵を持つ人だけ | 公開鍵を知っている誰でも |
| 否認防止 | 不可能 | 可能(法的有効性あり) |
| 速度 | 非常に高速 | 遅い(公開鍵演算が重い) |
| 用途 | API 認証、TLS 内部、Webhook 検証 | 証明書、ソフトウェア配布、契約書 |
署名鍵(秘密鍵)は 署名者しか持っていない。検証は誰でもできる(公開鍵が公開されているから)。だから「正しく検証できる署名」が存在する = その秘密鍵の持ち主(本人)が署名した と数学的に保証されます。これが法的に「電子署名」として通用する根拠で、日本の電子署名法(2001 年)、EU の eIDAS 規則(2014 年)などはこの暗号原理に立脚しています。
つながる知識: 否認防止が成立するには 秘密鍵が本人だけに保持されている 前提が必要。盗難・漏洩があれば成立しない。だから HSM(Hardware Security Module)、TPM、Secure Enclave(Apple)、YubiKey などのハードウェア鍵管理が重要視されます。
確認: 次のうち、MAC では実現できないがディジタル署名で成立する、もっとも本質的な性質はどれか。
正解:C。MAC は共有鍵を持つ 2 者ならどちらでも作れるため、署名者を客観的に同定できず否認可能。ディジタル署名は秘密鍵の保有者しか作れないので、第三者にも署名者を立証できる ─ これが MAC との本質的な差で、電子契約・コードサイニングが MAC ではなく署名を使う理由。完全性と認証は両者とも提供する。
図の見方:メッセージは平文で運ばれる(暗号化したいなら別途行う)。重要なのは「署名」だけが秘密鍵で作られ、それを公開鍵で検証する流れ。改ざんされたら h' が変わって検証失敗、偽造には秘密鍵が必要なので作れない。
考えてみよう: もし弱い MD5 のような壊れたハッシュを使ったら?攻撃者は同じハッシュを持つ別の文書を作って「正規の署名付き偽文書」を成立させられる。これが SHA-1 が証明書から廃止された決定的理由。署名の安全性はハッシュの衝突耐性に依存しています。
Q. もしハッシュを挟まず、メッセージそのものに RSA-2048 で直接署名しようとしたら何が起きるか? 具体的な不都合を 2 つ挙げよ。
1. メッセージが大きすぎて RSA に入らない。 RSA-2048 は法 n(2048 bit ≒ 256 バイト)以下の数しか扱えない。それより大きいメッセージは一度に署名不可能で、ブロック分割が必要になる(1 GB のファイルなら数百万ブロック)。
2. 速度が壊滅的。 RSA 署名 1 回は数 ms 〜 数十 ms。直接ブロック署名すれば 1 GB のファイルで数時間。32 バイトのハッシュ 1 回を署名するなら数 ms で終わる。
これが「メッセージ → ハッシュ → 署名」という標準パターンが採用されている根本理由。ECDSA / Ed25519 でも同じ事情(楕円曲線群の位数以下に収まる必要がある)。
| アルゴリズム | 鍵長 | 署名長 | 速度(署名/検証) | 主な用途 |
|---|---|---|---|---|
| RSA-PSS | 2048〜4096 bit | 256〜512 B | 遅い / 速い | X.509 証明書(従来) |
| ECDSA(P-256) | 256 bit | ~72 B | 速い / 速い | X.509 証明書(現代)、Bitcoin |
| Ed25519 | 256 bit | 64 B | 非常に速い / 速い | SSH、Git、TLS、WireGuard |
| EdDSA / Ed448 | 448 bit | 114 B | 速い | 高セキュリティ |
RSA で署名するときは 必ずパディング が必要。古い方式 PKCS#1 v1.5 は決定論的(同じメッセージ → 同じ署名)で、過去に多くの実装脆弱性(Bleichenbacher 攻撃等)が見つかった。RSA-PSS(Probabilistic Signature Scheme) は確率的(ランダムソルトを混ぜる)で、安全性証明もある現代的なパディング。新規システムでは RSA-PSS を使う。
ECDSA は 署名のたびに新しい乱数 k を生成する必要 があり、k を予測されたり再利用したりすると 秘密鍵が漏洩 する。Sony PlayStation 3 の鍵漏洩事件(2010)はこれが原因(同じ k を 2 回使った)。RFC 6979 の「決定論的 ECDSA」 は k をハッシュから決定論的に導出することで乱数生成器の脆弱性を回避します。
2011 年に djb・タンジャ・ランゲらが発表した Ed25519(EdDSA on Curve25519)は ECDSA の問題を構造的に排除した設計:
SSH(OpenSSH 6.5+)、Git(2017+)、WireGuard、Tailscale、Signal など 新しいシステムはほぼ全て Ed25519。RFC 8709(SSH)、RFC 8032(EdDSA)。
確認: Sony PlayStation 3 の署名鍵が漏洩した事件(2010)の直接の原因として、最も適切なものはどれか。
正解:B。ECDSA は署名のたびに新しい乱数 k を生成する必要があり、同じ k で 2 つの異なるメッセージに署名すると、得られる 2 つの署名から連立方程式で秘密鍵が一意に逆算できる。Sony は実装で k を固定値にしていた。Ed25519(EdDSA)や RFC 6979 の決定論的 ECDSA はこの問題を構造的に排除している。
「サーバ証明書 → 中間 CA → ルート CA」の チェーン・オブ・トラスト も、各段が前段の証明書の中身に対するディジタル署名であり、ルート CA だけが 自己署名(ブラウザ / OS に事前 install されている)。つまり PKI の階層構造そのものが、本講のスキームを縦に積み重ねた応用例と読めます。
2015 年、Let's Encrypt が 無料の自動化された証明書発行 を始めるまで、TLS 証明書は年額数千〜数万円する商用サービスでした。これにより HTTPS の普及率が劇的に上がり(2014 年:~30% → 2024 年:~85%)、Web 全体が暗号化された流れの引き金になりました。中身は ECDSA(P-256) または RSA-PSS で、X.509 と CA 階層の仕組みは変わらず ─ 「ディジタル署名の自動化された大量生産」 が起きたと言えます。
Q. なぜ X.509 では「中間 CA」を挟む 3 段構造になっているのか? ルート CA が直接サーバ証明書に署名すれば 2 段で済むのに、わざわざ中間 CA を入れる理由を 2 つ挙げよ。
1. ルート鍵を「使わずに済ませる」ため。 ルート CA の秘密鍵が漏洩すると、信頼されている全証明書が一瞬で疑わしくなり、世界規模の大事故になる。だから日常の証明書発行は 中間 CA で行い、ルート鍵は金庫にしまってオフライン保管(物理隔離)するのが標準運用。
2. 失効・運用の柔軟性。 ある中間 CA に問題が起きたら、その中間だけ失効して別の中間に切り替えれば済む。ルート CA はそのまま運用継続。一方、ルート CA を失効すると OS/ブラウザの更新が必要で、全ユーザに影響するため数年単位の作業になる。
このため Let's Encrypt や DigiCert などの公開 CA は「ルート → 中間 → サーバ」の 3 段階構造が標準。中間 CA は ルート鍵漏洩リスクへの保険であり、運用上のクッション としての存在意義がある。
| OS / 場所 | 仕組み | 強制度 |
|---|---|---|
| macOS / iOS | Apple の Developer ID + notarization(自動マルウェア検査 + Apple 署名) | 署名なしは警告 / iOS は完全ブロック |
| Windows | Authenticode(X.509 で配布物を署名)、SmartScreen が評判ベースで判断 | 未署名は警告。ドライバは強制必要 |
| Android | APK 署名 v2/v3。鍵は開発者管理 | 署名必須(自己署名で OK) |
| Linux パッケージ | RPM/DEB は GPG 署名。リポジトリ所有者の鍵で署名 | apt/dnf は鍵検証必須 |
| Git コミット | SSH 鍵 / GPG 鍵で git commit -S | 任意。GitHub では「Verified」表示 |
Git 2.34 から SSH 鍵でコミット署名できるようになり、GitHub の verified 表示にも対応。Ed25519 の SSH 鍵で:
# SSH 鍵を署名鍵として設定
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
# コミットに署名
git commit -S -m "fix: bug in module"
# 署名を確認
git log --show-signature
つながる知識: 2020 年の SolarWinds 事件、2017 年の NotPetya は 署名されたソフトウェアの配布 が攻撃ベクタになりました(攻撃者がビルドサーバを侵害して署名鍵で偽署名)。コードサイニングが信頼できるのは 署名鍵が安全に管理されている前提 でのみ。HSM・Sigstore のような 証明書透明性ログ を組み合わせる動きが進んでいます。
従来のコードサイニングは「署名鍵を信頼する」モデル。鍵が漏れたら攻撃者が正規ソフトに偽装できる(SolarWinds のシナリオ)。Sigstore(Linux Foundation, 2021〜) はこれを「署名イベントを公開ログに記録する」モデルに転換した。
Kubernetes、Python(PyPI)、npm が順次採用中。重要なのは「鍵を長期保管しない」「署名行為そのものが公開される」という設計の根本的な変更で、攻撃者がこっそり署名できる従来モデルとは前提が違う。
# 鍵を生成(前回作ったものを使う)
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out rsa_priv.pem
openssl rsa -in rsa_priv.pem -pubout -out rsa_pub.pem
# 署名対象
echo "I, Alice, agree to terms" > doc.txt
# 署名(SHA-256 + RSA-PSS)
openssl dgst -sha256 -sign rsa_priv.pem \
-sigopt rsa_padding_mode:pss \
-sigopt rsa_pss_saltlen:32 \
-out doc.sig doc.txt
# 検証(成功すると "Verified OK")
openssl dgst -sha256 -verify rsa_pub.pem \
-sigopt rsa_padding_mode:pss \
-sigopt rsa_pss_saltlen:32 \
-signature doc.sig doc.txt
# 1 文字書き換える
echo "I, Alice, agree to TERMS" > doc.txt
# 検証 → 失敗
openssl dgst -sha256 -verify rsa_pub.pem \
-sigopt rsa_padding_mode:pss \
-sigopt rsa_pss_saltlen:32 \
-signature doc.sig doc.txt
# → "Verification Failure"
# Ed25519 鍵生成
openssl genpkey -algorithm Ed25519 -out ed_priv.pem
openssl pkey -in ed_priv.pem -pubout -out ed_pub.pem
# Ed25519 は内部でハッシュ含むので -sha256 不要(指定するとエラー)
openssl pkeyutl -sign -inkey ed_priv.pem -rawin -in doc.txt -out doc.sig
# 検証
openssl pkeyutl -verify -pubin -inkey ed_pub.pem -rawin -in doc.txt -sigfile doc.sig
# → "Signature Verified Successfully"
# pip install cryptography
from cryptography.hazmat.primitives.asymmetric.ed25519 import (
Ed25519PrivateKey, Ed25519PublicKey
)
# 鍵生成
priv = Ed25519PrivateKey.generate()
pub = priv.public_key()
message = b"I, Alice, agree to terms"
# 署名
signature = priv.sign(message)
print(signature.hex()) # → 64 バイトの署名
# 検証(失敗すると InvalidSignature 例外)
try:
pub.verify(signature, message)
print("Valid")
except Exception:
print("Invalid")
# Web サイトの証明書チェーンを取得
openssl s_client -connect example.com:443 -showcerts < /dev/null \
| openssl x509 -text -noout
# 出力で確認できるもの:
# Subject: CN=example.com
# Issuer: CN=DigiCert TLS Hybrid ECC SHA384 2020 CA1
# Signature Algorithm: ecdsa-with-SHA384
# Public Key Algorithm: id-ecPublicKey (ECDSA P-384)
# Validity: ...
| アルゴリズム | 公開鍵 | 署名 | 備考 |
|---|---|---|---|
| Ed25519(古典) | 32 B | 64 B | 現代主流 |
| RSA-3072(古典) | ~400 B | ~384 B | X.509 で広く使用 |
| ML-DSA-65(PQC, Dilithium) | 1.95 KB | 3.3 KB | NIST 標準 |
| SLH-DSA-128s(PQC, SPHINCS+) | 32 B | ~7.8 KB | 署名はかなり大きい |
| FN-DSA(PQC, Falcon) | ~900 B | ~666 B | サイズ最小 |
「現役の SSH 鍵を 今すぐ Dilithium に移行」までの段階ではないが、長期保存される署名(コードサイニング、法的文書のタイムスタンプ)は次の数年で PQC 移行が進む見込み。
つながる知識: 暗号化(機密性)で「Harvest Now, Decrypt Later」が問題なのと違い、署名は「過去の署名は守れる」のがまだ救い。重要なのは 新規発行する署名 をいつ PQC に切り替えるか。例えば 30 年有効な政府発行のコードサイニング証明書を今 ECDSA で発行すると、20 年後に量子計算機が来た時点で 偽造可能 になる。だからこそ長期署名から先行移行が始まっています。
確認: なぜディジタル署名は「暗号化以上に PQC 移行が急務」と言われるのか? 最も適切な理由を選べ。
正解:C。コードサイニングや法的署名は数年〜数十年検証され続ける。今 30 年有効な署名証明書を ECDSA で発行すると、20 年後に量子計算機が登場した時点で偽造可能になる。「現役の SSH 鍵を急いで Dilithium に」より、「長期保存される署名」を先に移行すべき、というのが現在の優先順位。A は事実だが理由ではない。B は誤り(PQC 暗号化と署名は同時に標準化された)。D は誤り(優先順は使用ケース次第)。
| 用語 | 1行説明 |
|---|---|
| ディジタル署名 | 秘密鍵で印を付け、公開鍵で誰でも検証できる仕組み |
| 否認防止(non-repudiation) | 署名者が「自分が作ったのではない」と否認不可 |
| RSA-PSS | 確率的パディング。新規 RSA 署名の標準 |
| RSA-PKCS#1 v1.5 | 古い決定論的パディング。新規採用は非推奨 |
| ECDSA | 楕円曲線版 DSA。Bitcoin・X.509 で使用 |
| Ed25519 / EdDSA | djb 設計のモダン署名。SSH・Git・WireGuard で標準 |
| X.509 証明書 | 公開鍵 + 識別情報 + CA 署名のセット |
| チェーン・オブ・トラスト | サーバ証明書 → 中間 CA → ルート CA の署名連鎖 |
| ルート CA | OS/ブラウザに同梱の自己署名証明書。信頼の起点 |
| コードサイニング | ソフトウェア配布物への署名(Authenticode 等) |
| Authenticode | Windows のコード署名仕組み |
| notarization | Apple の自動マルウェア検査 + 署名 |
| Sigstore | OSS のコード署名の透明性ログプラットフォーム |
| HSM | Hardware Security Module。署名鍵を機器内で守る |
| ML-DSA(Dilithium) | NIST FIPS 204(2024) 標準の PQC 署名 |
| SLH-DSA(SPHINCS+) | FIPS 205。ハッシュベースの PQC 署名 |