論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
電子署名
Digital Signature
セキュリティ
本人性・完全性・否認防止をハッシュ+公開鍵暗号で達成
🔖 索引 💡 30秒 📍 文脈 🎨 直感 📐 数式 🔬 読み解き 🧮 実値 🐍 Python ⚠️ 落とし穴 🌐 関連手法 🔗 関連用語 📚 グループ

🔖 キーワード索引

このページの主要な見どころ。 気になる項目から読み始めてください。

30秒結論封蝋のアナロジーSign / Verify の形式記号一覧SSDSE データ整合性cryptography で署名鍵管理・MD5 の落とし穴RSA-PSS / ECDSA / EdDSA実世界事例FAQ公開鍵暗号など前提AI 倫理・クラウド

💡 30秒で分かる結論

電子署名 = メッセージのハッシュを送信者の秘密鍵で暗号化したもの。 受信者は公開鍵で復号し、 本文のハッシュと一致するかを検証する。

📍 あなたが今見ているもの

論文・実務レポート・公的統計の解説で、 こんな場面に出会ったはずです。

本研究で配布する SSDSE-B-2026.csvSHA-256 値および電子署名 (RSA-PSS / SHA-256)を併せて公開する。 受信者は公開鍵 (public.pem) で検証し、 ファイル真正性を確認した上で分析を開始した……

この「ハッシュ + 公開鍵で本人性を担保する」発想が電子署名の核です。 本ページでは SSDSE-B-2026.csv を実例として、 署名生成・検証・改ざん検出を Python で実装します。 配布元としても受信者としても、 数学的に「途中で誰かが書き換えていない」と確信できる仕組みを身につけられます。

🎨 直感で掴む

「封蝋 (sealing wax) のデジタル版」です。 中世の手紙では書いた人だけが持つ印章を蝋に押し付け、 ① 「本人が書いた」、 ② 「開封されていない」 を示しました。 電子署名は同じ役割をデジタルで担います。

身近なアナロジー:

暗号化との対比 (超重要):

場面使う鍵目的
暗号化(送る側)受信者の公開鍵受信者しか復号できない(秘密性)
復号(受ける側)受信者の秘密鍵中身を読む
署名(送る側)送信者の秘密鍵本人にしか作れない印
検証(受ける側)送信者の公開鍵本人が作ったか確認

暗号化は「受信者の鍵ペア」、 署名は「送信者の鍵ペア」と覚えてください。 鍵の方向が逆という点が初学者が必ずつまずくポイントです。

3 つの保証を分解: 本人性 (誰がそれを送ったか)、 完全性 (改ざんされていないか)、 否認防止 (送ったことを後から否定できない)。 これら 3 つを 1 つの仕組みで同時に満たすため、 電子契約・電子納税・ブロックチェーンの全てが電子署名に依存します。

SSDSE のような公的統計データでも、 署名付きで配布されると「これは確かに統計局が作ったものですよ」「途中で誰かが書き換えていませんよ」と数学的に保証できます。 1 ビット書き換えればハッシュは全く別物になる「雪崩効果」のおかげです。 これがあるからこそ、 研究者はネットワーク経由で受け取った CSV を疑わずに分析を始められます。

電子署名と紙の判子の違い:紙の印鑑は同じ印影を何度押しても同じになりますが、 電子署名は対象データが変われば全く別の値になります。 これにより「この署名はこの文書専用」になり、 他文書への流用が物理的に不可能になります。

処理の流れ図 (ASCII):

[送信者]
  m (SSDSE-B-2026.csv)
    │
    ├─ H(m) = SHA-256 ハッシュ
    │
    ├─ Sign(sk, H(m)) = σ (署名値)
    │
    └─ 送る: {{m, σ, 公開鍵証明書}}
                     │
                     ▼
[受信者]
  ① 証明書を CA で検証
  ② H(m) を再計算
  ③ Verify(pk, m, σ) == 1 か確認
       └─ OK → 信頼して分析開始
       └─ NG → 改ざん or 鍵不正

📐 数式または定義

電子署名スキームは 3 つのアルゴリズム の組 $(\mathsf{KeyGen}, \mathsf{Sign}, \mathsf{Verify})$ で定義されます。

① 鍵生成:

$$ (pk, sk) \leftarrow \mathsf{KeyGen}(1^\lambda) $$

$\lambda$ はセキュリティパラメータ(鍵長)。 $pk$ は公開鍵、 $sk$ は秘密鍵。

② 署名生成:

$$ \sigma \leftarrow \mathsf{Sign}(sk, m) = \mathsf{Enc}_{sk}\bigl(H(m)\bigr) $$

$m$ はメッセージ、 $H(\cdot)$ は SHA-256 などの暗号学的ハッシュ関数、 $\sigma$ は署名値。

③ 検証:

$$ \mathsf{Verify}(pk, m, \sigma) = \begin{cases} 1 & \text{if } \mathsf{Dec}_{pk}(\sigma) = H(m) \\ 0 & \text{otherwise} \end{cases} $$

RSA 署名の具体例:

$$ \sigma = H(m)^{d} \bmod N, \qquad \mathsf{Verify}: \sigma^{e} \equiv H(m) \pmod{N} $$

$N = pq$ は大きな合成数、 $(e, N)$ が公開鍵、 $d$ が秘密鍵で $ed \equiv 1 \pmod{\phi(N)}$。

ECDSA (楕円曲線署名):

$$ r = (kG)_x \bmod n, \quad s = k^{-1}\bigl(H(m) + r \cdot d\bigr) \bmod n $$

$G$ は曲線の基点、 $d$ は秘密鍵、 $k$ は使い捨て乱数。 $(r, s)$ が署名値。

EdDSA (Ed25519):

$$ R = rG, \quad S = r + H(R \| pk \| m) \cdot a \pmod{\ell} $$

決定的に $r$ を生成する。 サイドチャネル攻撃に強い。

セキュリティ要件 (EUF-CMA):

$$ \Pr\bigl[\mathsf{Verify}(pk, m^*, \sigma^*) = 1 \wedge m^* \notin Q\bigr] \le \mathsf{negl}(\lambda) $$

適応的選択メッセージ攻撃下で、 鍵を持たない攻撃者が新しい署名 $(m^*, \sigma^*)$ を偽造できる確率は無視できる。

ハッシュ関数の必要性質:

SHA-256 はいずれも満たしますが、 MD5・SHA-1 は ③ が破られているため電子署名には使えません。

🔬 数式を言葉で読み解く

記号読み方意味・例
$m$message署名対象データ。 SSDSE-B-2026.csv の全バイト列
$H(m)$hashSHA-256 等で得られる 256 bit の指紋
$sk, pk$secret/public key$sk$ は本人だけが保管、 $pk$ は配布可
$\sigma$signature署名値。 RSA-2048 なら 256 bytes
$N, e, d$RSA modulus / public / private exp.$(e, N)$ を公開、 $d$ は厳重保管
$\phi(N)$Euler's totient$\phi(pq) = (p-1)(q-1)$
$G, n$基点 / 位数楕円曲線の点とその位数。 secp256r1 など
$k$nonce署名ごとに新しく取る乱数。 再利用厳禁
$\ell$order (EdDSA)群の位数
$\mathsf{negl}(\lambda)$negligible$\lambda$ について多項式逆数より速く 0 へ

数式の核心は 「秘密鍵でしか作れない値だが、 公開鍵で誰でも検証できる」 という非対称性です。 これが本人性と否認防止を支える数学的根拠です。

$\sigma^e \equiv H(m) \pmod N$ という式は、 RSA の世界で「秘密鍵 $d$ で作った署名は、 公開鍵 $e$ で戻すと元のハッシュに戻る」という関係を表しています。 もし $\sigma$ を秘密鍵なしに作ろうとすると、 大きな数 $N$ を素因数分解する必要があり、 2048 bit RSA であれば現実時間では不可能です。

同様に ECDSA では、 楕円曲線上の離散対数問題が困難であることを安全性の根拠としています。 ECDSA の方が鍵長が短いのに同等の安全性を持つため、 モバイル・IoT で広く使われます。

"確率 $\mathsf{negl}(\lambda)$" という表現は暗号理論で頻出します。 これは「鍵長を増やせば、 攻撃成功確率は急速に 0 に近づく」ことを意味し、 セキュリティパラメータ $\lambda$ を大きくすれば計算量上現実的に破られない、 という保証になります。

署名検証の数式を「言葉」で読むと:

  1. 受信者は $m$ から自分で $H(m)$ を計算する
  2. 送信者の公開鍵 $pk$ で署名 $\sigma$ を「復号」する
  3. その結果が手元の $H(m)$ と一致するか比較する
  4. 一致 → 「秘密鍵を持つ本人が、 この $m$ に対して署名した」と確信できる

もし攻撃者が $m$ を改ざんしたら、 ステップ 1 で計算するハッシュが変わり、 ステップ 3 で不一致になる。 もし攻撃者が $\sigma$ を改ざんしたら、 ステップ 2 の結果がランダムになり、 やはり不一致になる。 だから両方守られます。

🧮 実値で計算してみる(SSDSE-B 2023, n=47)

SSDSE-B-2026 を配布する立場で、 ファイルが配布途中で改ざんされていないことを保証するシナリオを考えます。

  1. 配布側で SSDSE-B-2026.csv の SHA-256 を計算する
  2. その 32 バイトのハッシュを RSA-2048 秘密鍵で署名し、 SSDSE-B-2026.csv.sig として配布する
  3. 受信者は公開鍵 (public.pem) を入手し、 CSV と署名値から検証する
  4. もし誰かが CSV の北海道の人口を 1 だけ書き換えていれば、 ハッシュが変わり検証が失敗する

RSA-1024 の小さな手計算例 ($p=61, q=53$):

$N = 61 \times 53 = 3233$, $\phi(N) = 60 \times 52 = 3120$, 公開指数 $e = 17$, 秘密指数 $d = 2753$ (拡張ユークリッドより)。

仮にハッシュ値が $H(m) = 123$ だったとすると:

$$ \sigma = 123^{2753} \bmod 3233 = 855 $$

検証側は $\sigma^{e} = 855^{17} \bmod 3233 = 123$ を計算し、 これがハッシュと一致するので OK。

ファイル想定 SHA-256 (例)検証結果
SSDSE-B-2026.csv (正規)4f3a...c12e✅ 一致 → 改ざんなし
SSDSE-B-2026.csv (北海道人口 +1)9b71...8e02❌ 不一致 → 改ざん検出
SSDSE-B-2026.csv (BOM 追加のみ)2c44...11ab❌ 不一致 (BOM もデータ)
SSDSE-B-2026.csv (改行を CRLF→LF)87bc...4f29❌ 不一致 (改行もバイト)

「1 ビット変えてもハッシュは大幅に変わる」性質を 雪崩効果 (avalanche effect) と呼びます。

鍵長 / アルゴリズム想定セキュリティ署名サイズ署名速度 (1 ファイル)用途
RSA-2048112 bit 相当256 B~5 msTLS 証明書・電子契約
RSA-3072128 bit 相当384 B~15 ms長期保管
RSA-4096140 bit 相当512 B~40 ms政府機関の長期署名
ECDSA-P256128 bit 相当~72 B~1 msモバイル・IoT
EdDSA (Ed25519)128 bit 相当64 B~0.5 msSSH / Tor
Dilithium2ポスト量子 NIST L22.4 KB~3 ms長期 (量子計算機対策)

SSDSE-B-2026 は数 MB のファイルなので、 SHA-256 計算は 100 ms 以下、 署名生成も 10 ms 以下で済みます。 通信オーバヘッドは無視できる軽さです。

SSDSE-B-2026 の都道府県別 「行ハッシュ」 でデータ品質保証する例:47 都道府県 × 約 30 年 = 約 1,400 行に対して、 各行をシリアライズしてハッシュ化すれば、 「どの都道府県のどの年度が書き換わったか」 まで特定できます。 配布時に row_fingerprint.csv を添付しておくと、 監査時にとても便利です。

🐍 Python 実装

① SHA-256 ハッシュを計算

import hashlib

with open('data/raw/SSDSE-B-2026.csv', 'rb') as f:
    raw_bytes = f.read()

digest = hashlib.sha256(raw_bytes).hexdigest()
print('SHA-256:', digest)
print('ファイルサイズ:', len(raw_bytes), 'bytes')

# 1 文字変えるだけで雪崩効果でハッシュは全く別物に
tampered = raw_bytes.replace(b'\xe5\x8c\x97\xe6\xb5\xb7\xe9\x81\x93',
                              b'\xe5\x8c\x97\xe6\xb5\xb7\xe9\x81\x95')
print('tampered:', hashlib.sha256(tampered).hexdigest())

② 鍵ペアの生成 (RSA-2048)

from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes, serialization

private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()

with open('private.pem', 'wb') as f:
    f.write(private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption()))

with open('public.pem', 'wb') as f:
    f.write(public_key.public_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PublicFormat.SubjectPublicKeyInfo))

③ SSDSE-B-2026.csv に署名する

with open('data/raw/SSDSE-B-2026.csv', 'rb') as f:
    message = f.read()

signature = private_key.sign(
    message,
    padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
                salt_length=padding.PSS.MAX_LENGTH),
    hashes.SHA256()
)

with open('data/raw/SSDSE-B-2026.csv.sig', 'wb') as f:
    f.write(signature)

print('署名長:', len(signature), 'bytes')   # RSA-2048 なら 256 bytes

④ 受信者側で検証する

from cryptography.exceptions import InvalidSignature

with open('public.pem', 'rb') as f:
    pub = serialization.load_pem_public_key(f.read())

with open('data/raw/SSDSE-B-2026.csv', 'rb') as f:
    received_msg = f.read()
with open('data/raw/SSDSE-B-2026.csv.sig', 'rb') as f:
    received_sig = f.read()

try:
    pub.verify(received_sig, received_msg,
               padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
                           salt_length=padding.PSS.MAX_LENGTH),
               hashes.SHA256())
    print('OK: 本物の SSDSE-B-2026 です')
except InvalidSignature:
    print('NG: ファイルが改ざんされているか鍵が違います')

⑤ SSDSE-B-2026 の都道府県別行ハッシュ

import pandas as pd, hashlib

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1)

def row_fingerprint(row):
    serial = '|'.join(str(v) for v in row.values)
    return hashlib.sha256(serial.encode('utf-8')).hexdigest()[:16]

df['fingerprint'] = df.apply(row_fingerprint, axis=1)
print(df[['Prefecture','Year','fingerprint']].head(10))

# 後日同じ df を読み直してハッシュ比較すれば差分検知ができる
df[['Prefecture','Year','fingerprint']].to_csv(
    'data/raw/SSDSE-B-2026.row_fingerprints.csv', index=False)

⑥ ECDSA も併記

from cryptography.hazmat.primitives.asymmetric import ec

ec_priv = ec.generate_private_key(ec.SECP256R1())
ec_pub = ec_priv.public_key()

ec_sig = ec_priv.sign(message, ec.ECDSA(hashes.SHA256()))
print('ECDSA 署名長:', len(ec_sig), 'bytes')

ec_pub.verify(ec_sig, message, ec.ECDSA(hashes.SHA256()))
print('ECDSA 検証 OK')

⑦ EdDSA (Ed25519) を使う

from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey

ed_priv = Ed25519PrivateKey.generate()
ed_pub  = ed_priv.public_key()

ed_sig = ed_priv.sign(message)
print('Ed25519 署名長:', len(ed_sig), 'bytes')  # 64

ed_pub.verify(ed_sig, message)
print('Ed25519 検証 OK')

⚠️ よくある落とし穴

❌ MD5 / SHA-1 を使う
どちらも衝突攻撃が現実化済み。 MD5 は数秒、 SHA-1 は数万 GPU 時間で衝突が見つかる。 電子署名では SHA-256 以上を必ず使う。
❌ 暗号化と署名を混同
「秘密鍵で暗号化して送る」と思っているケースが多い。 署名は「秘密鍵で作って公開鍵で検証」、 暗号化は「公開鍵で作って秘密鍵で復号」と方向が逆。
❌ 秘密鍵の管理が雑
git に private.pem をコミット、 平文でクラウドに置く、 メール添付するのは鍵漏洩事故の典型。 HSM・KMS・暗号化ストレージで保管する。
❌ ECDSA で nonce 再利用
同じ k で 2 回署名すると、 連立方程式から秘密鍵が復元できる (PS3 事件)。 決定的 ECDSA (RFC 6979) を使う。
❌ 公開鍵を盲信
攻撃者が偽の公開鍵を配ったら全てが崩れる。 CA・Web of Trust・TOFU で鍵の真正性を担保する仕組みが必要。
❌ PKCS#1 v1.5 を新規採用
パディングオラクル攻撃に弱い。 新規システムでは PSS を使う。 既存システムは段階的移行。

📰 実世界での電子署名の使い方

実世界で電子署名・データ分散処理などの概念がどう適用されているか、 代表的なケーススタディを紹介します。

TLS / HTTPS の証明書

ウェブブラウザの 🔒 マークは、 サーバ証明書を CA が署名し、 ブラウザがその署名を検証して「本物のドメインか」を判定している。

  • CA は自分の秘密鍵でサーバ証明書に署名
  • ブラウザは OS/ブラウザに同梱された CA 公開鍵で検証
  • 中間 CA → ルート CA という証明書チェーンで信頼を辿る
  • Let's Encrypt は ACME プロトコルで証明書発行を自動化

ソフトウェア更新の検証 (Windows Update, apt, PyPI)

配布される実行ファイル・パッケージは開発者の秘密鍵で署名され、 OS/パッケージマネージャが検証してから実行する。 マルウェア混入を防ぐ。

  • Microsoft は自身の鍵で Windows Update を署名
  • apt は GPG 鍵で Debian/Ubuntu パッケージを検証
  • PyPI は Sigstore で署名検証を整備中
  • 署名されていないバイナリは警告 or 実行拒否

ブロックチェーン (Bitcoin / Ethereum)

すべての取引は送信者が秘密鍵で署名し、 受信者と承認ノードが公開鍵で検証する。 ECDSA / Schnorr / BLS が使われる。

  • 財布アドレス = 公開鍵のハッシュ
  • 取引送信 = 「自分の鍵でこの送金に署名する」
  • 署名のないブロックは無効
  • 秘密鍵を失うと資産にアクセスできない

電子契約 (DocuSign, クラウドサイン)

紙の押印に代えて、 電子証明書 + タイムスタンプ署名で「いつ・誰が・何に同意したか」を証明する。 電子帳簿保存法・eIDAS で法的有効性。

  • 署名者の身元確認 → 秘密鍵で署名
  • タイムスタンプ機関 (TSA) が時刻を署名
  • PDF へ PAdES 形式で埋め込み
  • 長期保存は LTV (Long Term Validation) 仕様

公的統計データの配布 (SSDSE, e-Stat)

本ページの題材。 配布元が署名 + ハッシュを公開、 受信者は検証して「正規の配布物だ」と確認できる。 研究の再現性のために重要。

  • CSV / Parquet / SQLite に対して SHA-256
  • 配布サイトに署名値と公開鍵を併記
  • GitHub Actions などで自動署名
  • メタデータ・出典・取得日時もログ化

❓ FAQ

Q. 紙のハンコと電子署名はどちらが強い?

A. 電子署名の方が圧倒的に強い。 紙の印鑑は複製が容易ですが、 電子署名は数学的に偽造不可能。 加えて時刻・対象データ・鍵主体の 3 点まで証明できる。

Q. 電子署名と HMAC の違いは?

A. 電子署名は 非対称鍵 なので否認防止が成立する。 HMAC は対称鍵で、 共有秘密を知る両者が同じ MAC を作れるため否認防止は無い。 API 認証など内部用途で HMAC、 公開配布で電子署名。

Q. 量子計算機が登場したらどうなる?

A. RSA・ECDSA・EdDSA はすべて Shor のアルゴリズムで破られる可能性がある。 NIST が 2024 年に Dilithium・SPHINCS+ を標準化。 長期保存が必要な署名は移行を計画。

Q. SSDSE のような公共データに毎回検証が必要?

A. 研究の再現性・データ ETHICS の観点から推奨。 特に分析結果を公開する場合は「使用したデータの正規性を確認した」と記録する。

Q. プログラムから署名するときの鍵管理ベストプラクティス

A. 本番では HSM (Hardware Security Module) または KMS (AWS KMS, GCP KMS, Azure Key Vault) を使う。 ローカル開発では暗号化されたキーストア。 git にコミットしない。