論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
電子署名
Digital Signature
セキュリティ
本人性・完全性・否認防止をハッシュ+公開鍵暗号で達成

🔖 キーワード索引

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

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 鍵不正

🎨 概念図で押さえる

電子署名は「秘密鍵で署名 / 公開鍵で検証」する公開鍵暗号の応用。 ここでは「署名と検証の流れ」「ハッシュ関数の役割」「PKI 信頼チェーン」を視覚化する。

電子署名の生成と検証フロー
図 A. 電子署名のライフサイクル。 署名者は「文書をハッシュ → 秘密鍵で暗号化」、 検証者は「受信文書を再ハッシュ → 公開鍵で署名を復号 → 一致確認」。 一致すれば「改竄なし + 秘密鍵保有者が署名」が保証される。
ハッシュ関数の役割
図 B. ハッシュ関数は文書を 256bit ダイジェストに圧縮する一方向関数。 文書を 1 文字変えるだけでダイジェスト全体が激変するため、 改竄検出に必須。 大文書を直接署名すると遅いので「ハッシュを署名する」のが現代の標準。
PKI 信頼チェーン
図 C. PKI 信頼チェーンは「OS/ブラウザに事前登録されたルート CA → 中間 CA → エンドユーザ証明書」の 3 段階。 個人の公開鍵を直接信用するのではなく、 信頼できる第三者 (CA) の署名を辿る。 銀行系の電子署名や HTTPS もこの仕組み。

🎮 やってみよう — 署名・検証・改ざん検知

封蝋 (指紋) のデジタル版を手で動かして体感します。 「メッセージ→簡易ハッシュを計算→秘密鍵で署名→受信側が公開鍵で検証」という流れを追い、 受信メッセージを1 文字でも改ざんするとハッシュが激変し検証が失敗する様子を確かめられます。

⚠️ これは教材用の簡易実装です。 ここで使う簡易ハッシュ (FNV-1a 系 32bit・決定的で雪崩効果あり) と簡易鍵 (加法で対にした玩具の鍵ペア) は仕組みを目で見るための教材で、 暗号強度はありません。 実運用は必ず SHA-256 + RSA-PSS / ECDSA / Ed25519 を使ってください (実装は下の 🐍 Python / OpenSSL 節を参照)。 攻撃コードは扱いません。

① 送信者 — メッセージを書いて秘密鍵で署名

簡易ハッシュ H(m) = --
秘密鍵 d = --送信者だけが持つ)
署名 σ = Sign(d, H(m)) = 未署名
⬇ 送信パケット = { メッセージ + 署名 σ + 公開鍵 e }

② 受信者 — 改ざんを試して公開鍵で検証

受信メッセージを編集すると「通信路での改ざん」を再現できます。 1 文字変えるだけでハッシュがどう変わるか観察しましょう。

受信ハッシュ H(m′) = --
公開鍵 e = --誰でも持てる)
検証待ち — まず署名し、 検証してみましょう。

検証が成功すると、 電子署名の 3 つの性質 が同時に満たされます:

🛡 完全性
改ざんされていない
👤 真正性
確かに本人の秘密鍵
📝 否認防止
後から否定できない

仕組みの直感は封蝋 + 指紋: ハッシュが「文書の指紋」、 秘密鍵での署名が「本人だけが押せる封蝋」です。 公開鍵で公開鍵基盤 (PKI) の信頼チェーン (図 C)を辿れば、 その公開鍵が本物かまで確かめられます。 なお実際の鍵は公開鍵から秘密鍵を逆算できませんが、 この玩具は分かりやすさ優先で加法の対にしてあります。 発展は RSA-PSS / ECDSA / EdDSA・タイムスタンプ・ブロックチェーン を参照。 落とし穴は鍵の秘匿・ハッシュ衝突・証明書の信頼連鎖です。

📐 数式または定義

🍰 まずはやさしく

数学的なルールで決まった署名の仕組みです。

鍵を使って、誰でも正しさを判定できるようにします。

パスポートのスタンプのように、特別な鍵で印を付けます。

ここでは署名を作るための計算式について読みます。

電子署名スキームは 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 は ③ が破られているため電子署名には使えません。

📐 数学的詳細 — RSA 署名の中身

RSA 署名の数学を一段深掘りします。 鍵生成は次の手順で:

  1. 大きな素数 $p, q$ を 2 つ選ぶ (各 1024 ビット程度)。 計算量: $O(\log^k n)$ の確率的素数判定。
  2. $n = pq$、 $\phi(n) = (p-1)(q-1)$ を計算。
  3. $\gcd(e, \phi(n)) = 1$ を満たす $e$ を選ぶ (通常 $e = 65537$)。
  4. $d \equiv e^{-1} \pmod{\phi(n)}$ を拡張ユークリッド互除法で計算。
  5. 公開鍵 $(e, n)$、 秘密鍵 $(d, n)$ (実装では $p, q$ も保持)。

署名・検証の正しさは Euler の定理 $a^{\phi(n)} \equiv 1 \pmod n$ から導かれます: $\sigma^e = (H(m)^d)^e = H(m)^{ed} = H(m)^{k\phi(n)+1} = H(m) \cdot 1^k = H(m) \pmod n$。 つまり $\sigma^e \mod n = H(m)$ で検証が成立。 セキュリティは「$n$ を素因数分解する困難さ」に依存します。 $n = 2048$ ビットなら現在の古典コンピュータでは現実時間内に解けない、 とされます。

📄 文書フォーマットでの電子署名

電子署名は単独のバイナリではなく、 文書フォーマット内に埋め込まれて運用されることが多い:

PDF (PAdES)

PDF/A-3 や PAdES (PDF Advanced Electronic Signatures, ETSI 規格) で電子署名を埋め込み。 Adobe Acrobat が読むと「署名済み」 と表示。 改ざんがあれば即座に検知。

CMS / PKCS#7 / S/MIME

電子メール (S/MIME) や任意ファイルの署名で使われる共通形式。 「データ + 署名 + 証明書チェーン」 を 1 つのバイナリに包装。 OpenSSL の cms コマンドで操作可能。

XML Signature (XAdES)

XML 文書の特定要素に署名する。 SAML 認証や SOAP セキュリティで使われる。 ETSI XAdES が拡張仕様。

JSON Web Signature (JWS)

JWT (JSON Web Token) の基盤。 認証・認可情報を電子署名で改ざん防止。 OAuth 2.0 / OpenID Connect の核心。

CAdES

CMS Advanced Electronic Signatures。 PKCS#7/CMS の拡張で、 長期署名 (LTV) を含む。 政府文書で使われる。

🧰 主要ライブラリ・ツール

ツール言語用途
OpenSSLCLI / C汎用、 鍵生成・署名・検証・X.509
cryptography (Python)Python高水準 API、 PSS, Ed25519 等対応
PyCryptodomePythonレガシー、 学習用にも良い
GPG / GnuPGCLIOpenPGP、 メール暗号、 ファイル署名
pyHankoPythonPDF 署名 (PAdES) 専用
Java JCA/JCEJavaエンタープライズ向け
Bouncy CastleJava/C#独立系の包括的ライブラリ
go-cryptoGoクラウドネイティブで人気
Web Crypto APIJavaScriptブラウザ標準
YubiKey / HSMハードウェア秘密鍵を物理的に守る

📋 OpenSSL チートシート

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# RSA 鍵ペア生成
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -pubout -out public.pem

# Ed25519 鍵ペア生成
openssl genpkey -algorithm ed25519 -out ed_private.pem
openssl pkey -in ed_private.pem -pubout -out ed_public.pem

# ファイル署名 (RSA-PSS + SHA-256)
openssl dgst -sha256 -sign private.pem -sigopt rsa_padding_mode:pss \\
       -out sig.bin data/raw/SSDSE-B-2026.csv

# 検証
openssl dgst -sha256 -verify public.pem -sigopt rsa_padding_mode:pss \\
       -signature sig.bin data/raw/SSDSE-B-2026.csv

# 自己署名 X.509 証明書
openssl req -x509 -new -key private.pem -days 365 -out cert.pem \\
       -subj '/C=JP/O=SSDSE Test/CN=test.example.jp'

# 証明書の中身を確認
openssl x509 -in cert.pem -text -noout

🌍 実例ケーススタディ — 各国の電子署名インフラ

日本: マイナンバーカード + JPKI

2016 年運用開始のマイナンバーカードは IC チップに 2 つの RSA 2048 ビット鍵を内蔵。 (1) 署名用 (e-Tax 等での電子署名)、 (2) 利用者証明用 (本人確認)。 公的個人認証サービス JPKI として、 民間サービスでも利用可能。

EU: eIDAS 規則

2014 年に欧州議会が採択。 「Advanced Electronic Signature (AES)」 「Qualified Electronic Signature (QES)」 を法的に定義。 加盟国間で相互認証。 民間契約でも紙の押印と同等の効力。

米国: ESIGN Act

2000 年に成立。 電子署名と紙の署名を同等扱い。 DocuSign、 Adobe Sign が普及の中心。 連邦政府文書でも採用。

エストニア: e-Estonia

電子政府の先進国。 全国民が ID カードを持ち、 投票・税申告・医療記録すべて電子署名で運用。 X-Road という ID 連携基盤を構築。

中国: 電子署名法 (2005)

国家暗号管理局が認証局を統括。 SM2/SM3/SM9 という独自の暗号アルゴリズム。 アリババ・テンセントが民間サービスを展開。

🔮 未来 — ポスト量子電子署名

量子コンピュータが大規模化すると、 Shor のアルゴリズムで RSA / ECDSA が破られます。 NIST は 2022 年に Dilithium (格子問題ベース)、 Falcon (格子問題ベース、 署名サイズ小)、 SPHINCS+ (ハッシュベース、 最も安全だが遅い) を標準化候補に選定。 2024 年に FIPS 204/205/206 として正式公開予定。

既存システムの移行は 2025-2035 年に進行中。 「暗号アジリティ (Cryptographic Agility)」 つまり「アルゴリズムを柔軟に差し替えられる設計」 が重要キーワードになっています。 SSDSE-B-2026 のようなデータに 30 年後にも検証可能な署名を付けたいなら、 今からポスト量子の準備をしておくのが賢明。

📌 まとめ — 電子署名の本質

  1. 3 つの保証: 完全性 (Integrity)、 真正性 (Authenticity)、 否認防止 (Non-repudiation)。
  2. 仕組み: 秘密鍵で署名 → 公開鍵で検証 の非対称性。
  3. 必須前処理: SHA-256 でハッシュ化してから署名。
  4. 現代標準: RSA-PSS (互換性) または Ed25519 / ECDSA (高速)。
  5. PKI: X.509 + CA で公開鍵の真正性を担保。
  6. SSDSE-B 文脈: 公開データに署名を付ければ、 「正規版」 を暗号学的に証明可能。 オープンデータ時代の信頼基盤。

🚀 実プロジェクトでの電子署名フロー

フロー1: SSDSE-B-2026 公開データに信頼性を付与

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes, serialization
import hashlib

# 0. 公開機関の鍵ペア (実際は HSM 等で保護)
sk = rsa.generate_private_key(65537, 2048)
pk = sk.public_key()

# 1. SHA-256 ハッシュを計算
with open('data/raw/SSDSE-B-2026.csv','rb') as f:
    data = f.read()
hash_hex = hashlib.sha256(data).hexdigest()
print(f'SHA-256: {hash_hex}')

# 2. 署名生成
sig = sk.sign(data,
              padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
              hashes.SHA256())

# 3. 配布パッケージ作成 (CSV + ハッシュ + 署名 + 公開鍵)
import zipfile
with zipfile.ZipFile('outputs/SSDSE-B-2026-signed.zip','w') as zf:
    zf.write('data/raw/SSDSE-B-2026.csv', 'data/raw/SSDSE-B-2026.csv')
    zf.writestr('SHA256SUM',  hash_hex + '  SSDSE-B-2026.csv')
    zf.writestr('signature.bin', sig)
    pem = pk.public_bytes(serialization.Encoding.PEM,
                          serialization.PublicFormat.SubjectPublicKeyInfo)
    zf.writestr('public_key.pem', pem)

print('配布パッケージ完成')
print('受信者は public_key.pem + signature.bin + SSDSE-B-2026.csv で検証可能')
📤 実行例(実測) SHA-256: 0fdbe5f603bb8e1ee72d83cca77e674cf9b45865d88c8f4fda5eb8046ea6f463 配布パッケージ完成 受信者は public_key.pem + signature.bin + SSDSE-B-2026.csv で検証可能

フロー2: 受信者の検証手順

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from cryptography.hazmat.primitives.serialization import load_pem_public_key

with open('public_key.pem','rb') as f:
    pk = load_pem_public_key(f.read())
with open('SSDSE-B-2026.csv','rb') as f:
    data = f.read()
with open('signature.bin','rb') as f:
    sig = f.read()

try:
    pk.verify(sig, data,
              padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
              hashes.SHA256())
    print('✅ 署名検証成功 - データは公的機関が発行した正規版です')
except Exception:
    print('❌ 署名検証失敗 - データが改ざんされているか、 鍵が間違っています')

この仕組みは Linux ディストリビューションの ISO ファイル、 Apple のアプリ署名、 e-Tax の申告書など、 ありとあらゆる場面で同じパターンが使われている。

🎟 JWT (JSON Web Token) — 電子署名の身近な例

Web API の認証で使われる JWT は、 電子署名を使った最も身近な例です。 「ヘッダ.ペイロード.署名」 の 3 つを . でつなげた文字列。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import jwt   # pip install PyJWT
from cryptography.hazmat.primitives.asymmetric import rsa

sk = rsa.generate_private_key(65537, 2048)
pk = sk.public_key()

# 1. ペイロード (主張)
payload = {
    'user_id': 'user_123',
    'email': 'test@example.com',
    'role': 'admin',
    'iat': 1700000000,
    'exp': 1700003600,    # 1時間有効
}

# 2. 署名付き JWT 生成 (RS256 アルゴリズム)
token = jwt.encode(payload, sk, algorithm='RS256')
print(token[:80] + '...')

# 3. 検証
try:
    decoded = jwt.decode(token, pk, algorithms=['RS256'])
    print('✅ JWT 検証成功:', decoded)
except jwt.ExpiredSignatureError:
    print('❌ 期限切れ')
except jwt.InvalidSignatureError:
    print('❌ 署名不正 (改ざん)')
📤 実行例(実測) eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidXNlcl8xMjMiLCJlbWFpbCI6InR... ❌ 期限切れ

OAuth 2.0 / OpenID Connect は JWT を中核として動作。 改ざんできないため、 サーバー側でセッション情報を保存しなくても安全に認証情報を渡せる。

🔐 鍵管理のベストプラクティス

⛓ 証明書チェーンの理解

ルート CA (Root Certificate)
  │ (自己署名)
  │
  ▼
中間 CA (Intermediate CA)
  │ (ルート CA が署名)
  │
  ▼
エンドエンティティ証明書 (Server/User Certificate)
  │ (中間 CA が署名)
  │
  ▼
あなたの公開鍵

検証の流れ:
1. エンド証明書の署名を中間 CA の公開鍵で検証 → OK
2. 中間 CA 証明書の署名をルート CA の公開鍵で検証 → OK
3. ルート CA は OS/ブラウザの信頼ストアに登録済み → OK
4. 失効リスト (CRL/OCSP) を確認 → 失効していない → OK
→ 全 OK なら「公開鍵は真正」 と確認できる
  

この階層構造のおかげで、 個別の公開鍵を一つ一つ信頼する必要なく、 「信頼できるルート CA から派生したもの」 として一括検証できる。

✅ 電子署名導入チェックリスト

🎓 学習ロードマップ

  1. Week 1: ハッシュ関数 (SHA-256) を理解。 hashlib で手を動かす。
  2. Week 2: 公開鍵暗号 (RSA) の数学を学ぶ。 鍵生成・署名・検証を Python で。
  3. Week 3: OpenSSL CLI で鍵・証明書・署名を扱う。
  4. Week 4: X.509 証明書、 PKI、 CA の概念を理解。
  5. Week 5: ECDSA / Ed25519 を試す。 RSA との違いを実感。
  6. Week 6: JWT で Web API 認証を作る。 OAuth 2.0 を学ぶ。
  7. Week 7: PDF 署名 (pyHanko) を実装。 PAdES に触れる。
  8. Week 8: HSM / Cloud KMS で鍵管理を学ぶ。 ポスト量子暗号入門。

🌟 サマリーカード

🛡 攻撃と防御 — セキュリティの実情

攻撃1: Bleichenbacher 攻撃 (1998)

PKCS#1 v1.5 パディングの脆弱性を利用したパディングオラクル攻撃。 SSL/TLS サーバーへの数百万回のクエリで秘密鍵を抽出可能。 防御: RSA-PSS への移行、 一定時間応答 (timing attack 対策)。

攻撃2: SHAttered (2017)

Google が SHA-1 の衝突を初めて実証。 同じ SHA-1 値を持つ異なる PDF を作成し、 SHA-1 署名なら同じ署名値で 2 つの内容を「同一」と認証可能。 防御: SHA-256 以上を使う。

攻撃3: 量子コンピュータ (将来)

Shor のアルゴリズムが大規模量子計算機で実用化されると RSA / ECDSA が崩壊。 2030-2040 年に深刻化と予想。 防御: ポスト量子暗号 (Dilithium 等) への計画的移行。

攻撃4: サイドチャネル攻撃

消費電力、 電磁波、 処理時間などの「副次的情報」 から秘密鍵を推定する攻撃。 スマートカードでも問題に。 防御: コンスタント時間アルゴリズム、 ノイズ注入、 ハードウェア対策。

攻撃5: 中間者攻撃 (MITM)

攻撃者が公開鍵を入れ替えて受信者に渡す。 「これが本物の公開鍵」 と偽装。 防御: X.509 証明書、 Certificate Transparency、 鍵ピンニング。

🧪 さらにレシピ — 実務で使う場面

レシピA: Apple Codesign (macOS)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Apple Developer 証明書でアプリに署名
codesign --force --deep --sign 'Developer ID Application: Your Name' MyApp.app

# 公証 (Notarization) — Apple の中央サーバーに送信
xcrun notarytool submit MyApp.zip --apple-id user@example.com --team-id ABC123 --wait

# 公証スタンプを貼る
xcrun stapler staple MyApp.app

# 検証
spctl --assess --verbose=4 MyApp.app

レシピB: Windows Authenticode

1
2
3
4
5
# SignTool で署名 (Windows SDK)
signtool sign /fd SHA256 /a /tr http://timestamp.digicert.com /td SHA256 MyApp.exe

# 検証
signtool verify /pa /v MyApp.exe

レシピC: Git のコミット署名

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# GPG 鍵生成
gpg --gen-key

# Git に鍵を登録
git config --global user.signingkey YOUR_KEY_ID
git config --global commit.gpgsign true

# コミット (自動的に署名される)
git commit -m 'feat: add SSDSE analysis'

# GitHub では 'Verified' バッジが付く
git log --show-signature

レシピD: Docker イメージ署名

1
2
3
4
# cosign で OCI 互換のコンテナ署名
cosign generate-key-pair                    # 鍵ペア生成
cosign sign --key cosign.key registry.io/myimg:v1
cosign verify --key cosign.pub registry.io/myimg:v1

レシピE: メール署名 (S/MIME)

1
2
3
# メール本文に S/MIME 署名を付ける
openssl smime -sign -in mail.txt -signer cert.pem -inkey key.pem -out signed.eml
# Outlook / Apple Mail で 'verified' マークが付く

⚖ 法的位置づけ — 日本の電子署名法

日本の電子署名法 (正式名「電子署名及び認証業務に関する法律」、 2001 年施行) は、 一定の要件を満たす電子署名に押印と同等の法的効力を与えています。 主要ポイント:

これにより、 紙の契約書を作らずに、 クラウドサインや DocuSign で締結した契約も法的効力を持ちます。 ただし、 不動産登記、 公正証書など一部の文書は依然紙が必要。 SSDSE-B のような公開データに署名を付けることに法的義務はありませんが、 オープンデータの信頼性を高める手段としては有効です。

📝 補遺 — よくある質問

🌟 最後に — 電子署名の哲学

電子署名は「信頼を数学に置き換える技術」 です。 紙の印鑑は人間の目で見て「これは山田さんの印鑑だ」 と判断していました。 電子署名はその判断を、 公開鍵暗号という数学的構造に委ねます。 大規模合成数の素因数分解の困難性、 楕円曲線上の離散対数の困難性 ─ これらの「計算量的困難性」 が信頼の基盤になっているのです。

SSDSE-B-2026 のような公的データに署名する習慣を持てば、 「このデータは確かに公的機関が発行した正規版である」 ことを暗号学的に証明できます。 オープンデータ時代の信頼基盤として、 電子署名は今後ますます重要になります。 公開鍵暗号の数学を学び、 OpenSSL や Python の cryptography ライブラリで実際に手を動かす ─ この経験が、 デジタル社会を理解する基礎体力になります。

🏛 PKI 完全ガイド — 信頼の階層構造

PKI (Public Key Infrastructure) は電子署名・暗号化の社会的基盤です。 構成要素:

  1. ルート CA: 信頼の頂点。 自己署名証明書を発行。 OS / ブラウザに事前登録。
  2. 中間 CA: ルート CA から認可された下位 CA。 実運用で多用。
  3. 登録局 (RA): 申請者の本人確認を担当。 CA とは別組織のことも。
  4. 証明書発行サービス: 公開鍵を含む証明書を生成・配布。
  5. 失効リスト (CRL): 失効済み証明書のリスト。 定期的に配信。
  6. OCSP レスポンダ: 個別証明書の有効性をリアルタイムに問い合わせる。
  7. タイムスタンプ局 (TSA): 時刻認証を担当。
  8. リポジトリ: 証明書・CRL を公開するディレクトリ。

日本の代表的な認証局: 日本電子認証 (NDN)、 セコムトラストシステムズ、 GMOグローバルサイン、 JPRS、 サイバートラスト。 政府関連は GPKI (政府認証基盤)、 LGPKI (地方公共団体認証基盤) が運用されています。

📄 PDF 電子署名の内部

PDF への電子署名は ISO 32000-2 と PAdES (ETSI EN 319 142) で規格化されています。 内部構造:

  1. シグネチャ辞書: PDF 内の特別な辞書オブジェクト。 /ByteRange で署名対象範囲を指定。
  2. 署名値: PKCS#7 (CMS) 形式のバイナリ。 PDF ファイル内に埋め込み。
  3. 証明書チェーン: PKCS#7 構造に含まれる。 中間 CA まで全て同梱。
  4. タイムスタンプ: 別途追加可能。 RFC 3161 形式。
  5. 失効情報: LTV (Long Term Validation) 対応のため、 CRL/OCSP レスポンスを PDF 内に同梱。

これにより、 数十年経って CA が消滅しても、 PDF 単体で署名検証が可能になります。 公文書のアーカイブで重要な特性。

❓ 追加 FAQ

Q. 1 つの鍵を複数のアルゴリズムで使える?
A. 原則 NG。 1 鍵 1 用途 (署名用 / 暗号化用) が原則。 用途別に鍵を分けることでセキュリティが向上。
Q. 漏洩した秘密鍵の対処
A. (1) すぐに失効申請、 (2) 失効リストに掲載、 (3) 新規鍵で再発行、 (4) 過去署名はタイムスタンプで救済可能 (漏洩前の署名は有効)。
Q. 量子コンピュータが現実になったら過去の署名はどうなる?
A. 「Harvest now, decrypt later」 攻撃の懸念。 今のうちにポスト量子暗号でも署名し直す「ハイブリッド署名」 が推奨される。
Q. CA を信頼しなくて済む方法は?
A. (1) Web of Trust (PGP の方式)、 (2) Trust on First Use (SSH の方式)、 (3) ブロックチェーン (公開台帳)、 (4) DNSSEC (DNS の署名)。
Q. マイナンバーカードを使った電子署名の限界は?
A. (1) IC リーダー必要、 (2) PIN 入力面倒、 (3) Mac / モバイルでの対応が限定的、 (4) 子供は持てない (15 歳以上)。 ただし e-Tax 等で確実な本人認証手段として有用。

🗾 日本の電子署名インフラ詳細

JPKI (公的個人認証サービス)

マイナンバーカードに搭載される RSA 2048 鍵を使った認証基盤。 「署名用電子証明書」 (5 年有効、 引っ越しで失効) と「利用者証明用電子証明書」 (本人確認用) の 2 種類。 民間サービスでも利用可能 (例: クラウドサイン、 LIFULL HOME'S 不動産)。

GPKI (政府認証基盤)

政府機関の職員が公文書に電子署名するための基盤。 中央府省、 独立行政法人で運用。

LGPKI (地方公共団体認証基盤)

都道府県・市区町村の職員向け認証基盤。 e-Tax / eLTAX で住民税・固定資産税の電子申告に使用。

商業認証局

セコムトラストシステムズ、 GMOグローバルサイン、 サイバートラスト、 日本電子認証 (NDN) など。 法人向けの SSL/TLS 証明書、 コードサイニング証明書を発行。 Adobe Approved Trust List (AATL) や Microsoft Root Certificate Program に登録された CA も多い。

🎓 SSDSE-B-2026 への応用 — 完全な署名フロー

本セクションでは、 公開機関の立場で SSDSE-B-2026 を電子署名付きで配布する場合の完全フローを示します。 そのまま教育・研究用途で活用可能:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
"""
SSDSE-B-2026 配布パッケージ生成スクリプト
- データに RSA-PSS-SHA256 署名を付与
- X.509 自己署名証明書を同梱
- SHA-256 ハッシュも個別に生成
- ZIP に全部まとめる
"""
import os, hashlib, zipfile, datetime
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes, serialization
from cryptography import x509
from cryptography.x509.oid import NameOID
from datetime import timedelta

OUT_DIR = 'outputs/ssdse-signed-package'
os.makedirs(OUT_DIR, exist_ok=True)

# 1. 鍵ペア生成
sk = rsa.generate_private_key(65537, 2048)
pk = sk.public_key()

# 2. 自己署名 X.509 証明書 (発行者は仮想公的機関)
subject = issuer = x509.Name([
    x509.NameAttribute(NameOID.COUNTRY_NAME, 'JP'),
    x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, 'Tokyo'),
    x509.NameAttribute(NameOID.ORGANIZATION_NAME, 'SSDSE Distribution Authority (Demo)'),
    x509.NameAttribute(NameOID.COMMON_NAME, 'ssdse-distribution-demo'),
])
cert = (x509.CertificateBuilder()
        .subject_name(subject)
        .issuer_name(issuer)
        .public_key(pk)
        .serial_number(x509.random_serial_number())
        .not_valid_before(datetime.datetime.utcnow())
        .not_valid_after(datetime.datetime.utcnow() + timedelta(days=3650))  # 10年
        .add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True)
        .add_extension(x509.KeyUsage(
            digital_signature=True, content_commitment=True, key_encipherment=False,
            data_encipherment=False, key_agreement=False, key_cert_sign=True,
            crl_sign=True, encipher_only=False, decipher_only=False), critical=True)
        .sign(sk, hashes.SHA256()))

# 3. データ読み込み
with open('data/raw/SSDSE-B-2026.csv','rb') as f:
    data = f.read()

# 4. SHA-256 ハッシュ
sha256_hex = hashlib.sha256(data).hexdigest()

# 5. RSA-PSS 署名
signature = sk.sign(
    data,
    padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
    hashes.SHA256(),
)

# 6. パッケージ書き出し
with open(f'{OUT_DIR}/SSDSE-B-2026.csv','wb') as f:
    f.write(data)
with open(f'{OUT_DIR}/SHA256SUMS','w') as f:
    f.write(f'{sha256_hex}  SSDSE-B-2026.csv\n')
with open(f'{OUT_DIR}/signature.bin','wb') as f:
    f.write(signature)
with open(f'{OUT_DIR}/cert.pem','wb') as f:
    f.write(cert.public_bytes(serialization.Encoding.PEM))
with open(f'{OUT_DIR}/public_key.pem','wb') as f:
    f.write(pk.public_bytes(serialization.Encoding.PEM,
                            serialization.PublicFormat.SubjectPublicKeyInfo))

# 7. 検証スクリプトも同梱
verify_script = '''
import hashlib
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
from cryptography.x509 import load_pem_x509_certificate

with open('data/raw/SSDSE-B-2026.csv','rb') as f: data = f.read()
with open('signature.bin','rb') as f: sig = f.read()
with open('cert.pem','rb') as f: cert = load_pem_x509_certificate(f.read())

assert hashlib.sha256(data).hexdigest() == open('SHA256SUMS').read().split()[0]
cert.public_key().verify(sig, data,
    padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
    hashes.SHA256())
print('OK: 検証成功')
'''
with open(f'{OUT_DIR}/verify.py','w') as f:
    f.write(verify_script)

# 8. ZIP に圧縮
with zipfile.ZipFile(f'{OUT_DIR}.zip','w', zipfile.ZIP_DEFLATED) as z:
    for fname in os.listdir(OUT_DIR):
        z.write(f'{OUT_DIR}/{fname}', fname)

print('完成:', f'{OUT_DIR}.zip')
print('受信者は ZIP を展開し python verify.py で検証可能')
📤 実行例(実測) 完成: outputs/ssdse-signed-package.zip 受信者は ZIP を展開し python verify.py で検証可能

この一連のスクリプトを応用すれば、 自分のチームのレポート・データセットにも署名を付けて配布できる。 ジャーナル投稿の補足資料、 学位論文のデータ、 自治体オープンデータ、 etc.

💎 締め — 暗号で守られた世界

電子署名は、 現代のデジタル社会を支える「見えないインフラ」 です。 普段意識しなくても、 ブラウザのアドレスバーの錠前アイコン、 マイナンバーカードの IC、 銀行アプリの認証、 自動アップデートの正当性検証、 ブロックチェーンの取引 ─ あらゆる場面で電子署名が黙々と働いています。

SSDSE-B-2026 のような公開データに電子署名を付ける習慣を持てば、 オープンデータの信頼性が一段上がります。 公開鍵暗号、 ハッシュ関数、 X.509 証明書、 PKI ─ これらの技術スタックを学び、 OpenSSL や Python の cryptography ライブラリで実際に手を動かす経験は、 セキュリティ感覚を養う最良の手段です。

本ページの内容をすべて実践すれば、 電子署名に関する基礎知識と実装能力が一通り身につきます。 さらに専門領域に進みたければ、 IPA の暗号関連ガイドライン、 NIST 標準文書、 そしてポスト量子暗号の最新動向を追ってください。 デジタル社会の信頼を守る、 重要な役割を担うエンジニア / 研究者になれます。

🔮 ポスト量子暗号 — 30 年後の電子署名

NIST が 2022 年に選定したポスト量子署名アルゴリズム:

アルゴリズム基盤問題鍵サイズ署名サイズ速度
DilithiumModule Lattice1.5 KB2.4 KB速い
FalconNTRU Lattice1.3 KB0.7 KB
SPHINCS+ハッシュベース32 B8-50 KB遅い
比較: RSA-2048素因数分解256 B256 B
比較: Ed25519楕円曲線離散対数32 B64 B最速

ポスト量子の鍵・署名サイズは現行より大きく、 通信量・ストレージへの影響を考慮した設計が必要。 ハイブリッド (ECDSA + Dilithium 両方で署名) で移行期を乗り切るアプローチが現実的。

🧷 補足 — 電子署名の身近な用例

🎯 次の一歩

  1. OpenSSL で RSA 鍵ペアを生成し、 SSDSE-B-2026.csv に署名・検証してみる。
  2. Python の cryptography ライブラリで Ed25519 を試す。
  3. マイナンバーカードを持っていれば、 e-Tax で電子申告を体験。
  4. GitHub の Git コミットに GPG 署名を付けて Verified マークを付ける。
  5. JWT を発行・検証する小さな Web API を作る。
  6. cosign で Docker イメージに署名するワークフローを構築。

電子署名は概念だけ学んでも身につきません。 実際に鍵を生成し、 ファイルに署名し、 検証する経験を通じて初めて、 PKI のありがたみと注意点が体感できます。 まずは 1 つ動かしてみましょう。

🌍 グローバル動向 — 電子署名の世界地図

各地域の電子署名関連法制度を整理:

国・地域主要法令特徴
日本電子署名法 (2001)マイナンバーカード普及中、 事業者署名型OK
EUeIDAS 規則 (2014)AES/QES の階層、 加盟国相互認証
米国ESIGN Act (2000)州ごとに UETA、 民間活発
エストニア電子身分証法完全電子政府、 全国民 ID カード
中国電子署名法 (2005)独自暗号 SM2/SM3、 国家統制
インドIT Act (2000)Aadhaar との連動、 eSign Service
韓国電子署名法 (2020 全面改正)公認認証制度廃止、 民間多様化

🎬 結び

電子署名は「数学が人類社会の信頼を支える」 という、 20 世紀後半の偉大な発明の 1 つです。 1976 年の Diffie-Hellman 論文から半世紀、 私たちはインターネット、 ブロックチェーン、 マイナンバー、 クラウド契約 ─ あらゆる場面で公開鍵暗号の恩恵を受けています。 SSDSE-B-2026 のようなオープンデータも、 電子署名が支える信頼の生態系の一部です。 この技術を学び、 自分のプロジェクトに組み込むことで、 デジタル社会の信頼構築に貢献できます。 まずは小さなファイルに署名を付けることから、 始めてみてください。

🧱 補遺 — 実装時の細かい注意点

📝 最終チェック — 安全な電子署名運用

本ページの全内容を踏まえた「実プロジェクトで電子署名を導入する際の確認事項」 を 1 ページにまとめます:

  1. アルゴリズム選定 (RSA-PSS / Ed25519 / ECDSA / Dilithium 計画)
  2. 鍵長の決定 (RSA 2048+ / ECC 256+)
  3. ハッシュ選定 (SHA-256 以上)
  4. パディング (PSS) の指定
  5. 秘密鍵保護 (HSM / Cloud KMS / YubiKey)
  6. X.509 証明書取得 (商用 CA か自己署名)
  7. 失効リスト (CRL / OCSP) の運用設計
  8. タイムスタンプ局 (TSA) の利用検討
  9. 鍵ローテーション計画
  10. 監査ログ・モニタリング
  11. 法的要件確認 (電子署名法、 eIDAS 等)
  12. ポスト量子暗号への移行ロードマップ

これらを 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 の結果がランダムになり、 やはり不一致になる。 だから両方守られます。

🔬 数式を言葉で読み解く(4要素構造/1200字以上)

電子署名 (Digital Signature) は「送信者だけが作れて、 誰でも検証できる」 という非対称性を持つ暗号技術です。 RSA 署名の最も基本的な形は:$$\sigma = H(m)^d \mod n, \quad \text{検証}: \sigma^e \equiv H(m) \pmod n$$ ここで $m$ はメッセージ (例: 文書)、 $H$ はハッシュ関数 (SHA-256 等)、 $(e, n)$ は公開鍵、 $d$ は秘密鍵、 $\sigma$ が署名値です。 署名者は秘密鍵 $d$ で $H(m)^d$ を計算し署名 $\sigma$ を生成。 受信者は公開鍵 $(e, n)$ で $\sigma^e$ を計算し $H(m)$ と一致するか確認。 一致すれば「秘密鍵保有者が確かに $m$ に署名した」 と検証成功となります。

要素1: ハッシュ関数 $H$ — 「何に署名するか」

$H$ はメッセージ $m$ を固定長 (256 ビット = 32 バイト等) のダイジェスト値に圧縮する関数。 SHA-256、 SHA-3-256、 BLAKE2 などが現代的選択肢。 重要性質は (a) 衝突困難性 ($H(m_1) = H(m_2)$ となる $m_1, m_2$ を見つけるのが計算上困難)、 (b) 第二原像困難性 (与えられた $H(m)$ から $m'$ を逆算困難)、 (c) 一方向性 ($H$ から $m$ を逆算困難)。 なぜハッシュを介すのか? → 直接 $m^d \mod n$ にすると大きすぎる文書も小さすぎる文書も問題になるため。 $H$ を介すことで常に固定サイズで処理でき、 セキュリティも向上します。

要素2: 秘密鍵 $d$ — 「署名する力」

秘密鍵 $d$ は署名者だけが持つ「印鑑のような存在」。 RSA では $d$ は $ed \equiv 1 \pmod{\phi(n)}$ を満たす整数 ($\phi$ はオイラー関数)。 $d$ から $n = pq$ の素因数 $p, q$ を計算的に逆算可能で、 そこから他の秘密情報も漏れます。 そのため秘密鍵の保護がセキュリティの要 ─ ハードウェアセキュリティモジュール (HSM)、 スマートカード、 マイナンバーカード IC チップに格納するのが定石。 もし $d$ が漏洩したら、 攻撃者は任意の文書に「あなたの名前」 で署名できてしまう (なりすまし)。

要素3: 公開鍵 $(e, n)$ — 「検証する力」

公開鍵 $(e, n)$ は誰にでも公開して構わない情報。 $e = 65537$ (= $2^{16}+1$) が標準的な選択。 $n$ は 2048 ビット (256 バイト) 程度の合成数。 公開鍵は公開鍵証明書 (X.509 形式)として CA (認証局) に登録され、 「この公開鍵は確かに山田太郎のもの」 と裏付けを得ます。 受信者は X.509 証明書チェーンを辿って CA の署名を確認することで、 公開鍵の真正性を保証。 これが PKI (Public Key Infrastructure) の根幹です。

要素4: 署名値 $\sigma$ — 「結びつける証拠」

$\sigma$ は数値 (バイナリ列) ですが、 単独で意味を持つわけではなく、 元メッセージ $m$ と公開鍵 $(e, n)$ の3 つ揃って初めて検証できます。 PDF, S/MIME, JSON Web Signature (JWS) などのフォーマットは「文書 + 署名値 + 証明書」 をまとめてパッケージ化します。 検証は (1) $\sigma^e \mod n$ を計算、 (2) $H(m)$ を計算、 (3) 一致を確認、 の 3 ステップ。 検証側は秘密鍵を持っていなくても、 公開情報だけで「本物の署名か」 を確認できる ─ これが「非対称性」の本質です。

🧮 実値で計算してみる(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 = 2746 $$

検証側は $\sigma^{e} = 2746^{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 都道府県 × 12 年 = 564 行に対して、 各行をシリアライズしてハッシュ化すれば、 「どの都道府県のどの年度が書き換わったか」 まで特定できます。 配布時に row_fingerprint.csv を添付しておくと、 監査時にとても便利です。

🧮 SSDSE-B-2026 のデータファイルに電子署名する

🎯 このブロックの狙い
SSDSE-B-2026.csv (47都道府県データ) に対し、 RSA 鍵ペアを生成し電子署名を付与、 公開鍵で検証する。 ファイル改ざんを検知する仕組みを体験する。
📥 入力
data/raw/SSDSE-B-2026.csv (約 200 KB の統計データ) + 自前生成の RSA 鍵ペア。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes, serialization

# 1. RSA 鍵ペア生成 (2048 bit)
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key  = private_key.public_key()

# 2. ファイル読み込み
with open('data/raw/SSDSE-B-2026.csv','rb') as f:
    data = f.read()
print(f'ファイルサイズ: {len(data)/1024:.1f} KB')

# 3. 署名生成 (PSS パディング + SHA-256)
signature = private_key.sign(
    data,
    padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
    hashes.SHA256(),
)
print(f'署名サイズ: {len(signature)} バイト')   # 256 バイト (2048 ビット鍵で)

# 4. 公開鍵で検証
try:
    public_key.verify(
        signature, data,
        padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
        hashes.SHA256(),
    )
    print('✅ 検証成功 - 改ざんなし')
except Exception as e:
    print(f'❌ 検証失敗: {e}')

# 5. 改ざんを試してみる
tampered = data[:-10] + b'TAMPERED!!'
try:
    public_key.verify(signature, tampered, padding.PSS(...), hashes.SHA256())
except Exception:
    print('改ざんデータは検証失敗 (期待通り)')
📤 出力(期待値)
署名サイズは 256 バイト (鍵長 2048 ビット = 256 バイト)。 改ざんしないデータは検証成功、 1 バイトでも変えれば即座に検証失敗。 これが「完全性」 (Integrity) の保証。
💬 解説
PSS (Probabilistic Signature Scheme) は RSA 署名の現代標準。 古い PKCS#1 v1.5 はパディングオラクル攻撃に弱い。 SHA-256 は現代最低水準のハッシュ。 SHA-1 は廃止対象。 SSDSE-B のような公的データに署名すれば、 「これは正規版」 と暗号学的に証明できる。

🏭 産業事例 6 件 — 電子署名はどこで動いているか

業界電子署名の役割技術
金融 (証券取引)電子契約・取引指図書に署名。 改ざん不能で監査に耐える。 SWIFT / 金融機関の中核。RSA-PSS, ECDSA
政府・公文書マイナンバーカードでの電子申請。 e-Tax, e-Gov の本人確認。RSA, PKI
ソフトウェア配布アプリのコード署名。 macOS の Notarization、 Windows の Authenticode、 Linux の deb/rpm 署名。RSA, GPG
Web (HTTPS)TLS 証明書で Web サイトの真正性を保証。 全インターネット通信の基盤。ECDSA, RSA
ブロックチェーンBitcoin / Ethereum のトランザクション署名。 「鍵がなければ送金できない」 の核心。ECDSA (secp256k1)
公的統計SSDSE 等の公開データに署名を付与すれば、 「正規データ」 を暗号学的に証明可能。RSA-PSS, GPG

📊 比較表 — 主要電子署名アルゴリズム

アルゴリズム鍵長署名サイズ速度用途
RSA-PSS (2048)2048256 B汎用、 標準
RSA-PKCS#1 v1.52048256 Bレガシー (推奨せず)
RSA-PSS (4096)4096512 B遅い高セキュリティ
ECDSA (P-256)25664 B速いTLS, モバイル
ECDSA (secp256k1)25664 B速いBitcoin, Ethereum
Ed2551925664 B最速SSH, 現代標準
DSA2048~50 B非推奨
Dilithium (PQC)数 KB~2 KB速い耐量子標準候補

現代の新規システムは Ed25519 か ECDSA P-256 を推奨。 RSA は互換性のために残る。 将来は Dilithium 等のポスト量子暗号へ移行予定。

✏️ 演習 5 問 — SSDSE-B-2026 に署名を付与する

  1. 演習1: RSA 2048 ビット鍵ペアを生成し、 SSDSE-B-2026.csv に署名・検証せよ。
  2. 演習2: ファイルを 1 バイト改ざんし、 検証が失敗することを確認せよ。
  3. 演習3: Ed25519 鍵ペアでも同じことを行い、 署名サイズの違いを比較せよ。
  4. 演習4: 自己署名 X.509 証明書を作成し、 公開鍵の真正性を証明書チェーンで確認せよ。
  5. 演習5: openssl dgst -sha256 -sign private.pem -out sig.bin data.csv でコマンドライン版も試せ。

💀 失敗例 — 電子署名の事故

事例1: 秘密鍵を Git にコミット

private.pem をうっかり public リポジトリにコミット。 数分以内に GitHub Secret Scanner で検知され、 SSH キーなら 1 時間以内に攻撃が来る。 対策: .gitignore 必須、 git-secrets / pre-commit hook で防止。

事例2: PKCS#1 v1.5 でパディングオラクル攻撃

古い PKCS#1 v1.5 パディングは攻撃手法 (Bleichenbacher 攻撃) があり、 秘密鍵が漏れる可能性。 対策: RSA-PSS に移行、 既存システムは段階的に。

事例3: SHA-1 のまま運用

2017 年に SHA-1 衝突が実証 (SHAttered)。 SHA-1 ベースの署名は信頼できない。 対策: SHA-256 以上に切り替え。 多くの CA は 2017 年に強制移行済み。

事例4: 公開鍵の信頼性確認なし (MITM)

「これが署名者の公開鍵だよ」 と渡された鍵を盲信すると、 中間者攻撃者の鍵かもしれない。 対策: X.509 証明書チェーン、 Certificate Transparency、 ピンニング。

事例5: タイムスタンプなしで時効問題

10 年後に「この署名はいつ作られた?」 が分からないと法的有効性に疑問。 対策: 時刻認証局 (TSA) のタイムスタンプを併用 (RFC 3161)。

📖 用語ミニ辞典(10語)

公開鍵暗号
鍵を「公開鍵 + 秘密鍵」に分けた暗号方式。 RSA, ECC など。
PKI
Public Key Infrastructure。 認証局を頂点とした公開鍵の信頼基盤。
CA
Certificate Authority、 認証局。 公開鍵証明書を発行する組織。
X.509
公開鍵証明書の標準フォーマット。 ITU-T 規格。
RSA
1977 年発表の代表的公開鍵暗号。 素因数分解の困難性に依拠。
ECDSA
Elliptic Curve DSA。 楕円曲線上の離散対数困難性に依拠。 高速・短鍵。
Ed25519
EdDSA の特定インスタンス。 SSH や近代システムで標準。
SHA-256
ハッシュ関数。 256 ビットダイジェスト。 現代標準。
PSS
RSA 署名の現代的パディング。 PKCS#1 v1.5 より安全。
タイムスタンプ局
RFC 3161。 「いつ署名されたか」を第三者が証明する仕組み。

🕰 歴史的背景 — 紙の印鑑から量子耐性へ

電子署名の理論的基礎は 1976 年の Diffie-Hellman 論文「New Directions in Cryptography」 に始まります。 ここで「公開鍵暗号」 という革命的アイデアが世に出ました。 翌 1977 年に Rivest, Shamir, Adleman の 3 人が RSA を発表し、 実用的な実装が可能に。 1980 年代には DSA (Digital Signature Algorithm) が NIST 標準化、 1990 年代に X.509 証明書と PKI が広く採用されました。

日本では 2001 年に電子署名法が施行され、 電子署名が紙の押印と同等の法的効力を持つようになりました。 マイナンバーカード (2016 年〜) は IC チップに RSA 鍵を内蔵し、 e-Tax / e-Gov での本人認証に使えます。 2018 年からは民間でも本格的に電子契約 (クラウドサイン、 DocuSign、 GMOサイン等) が普及し始め、 紙の契約書を駆逐しつつあります。

2020 年代の課題は「量子計算機への耐性」。 大規模な量子コンピュータが完成すると、 Shor のアルゴリズムで RSA / ECDSA が破られます (2030-2040 年代と予想)。 NIST は 2022 年に Dilithium / Falcon / SPHINCS+ をポスト量子署名標準として選定。 2030 年代には主要システムが順次移行する見込みです。

🔧 高度なレシピ — 実プロジェクト向け

レシピ1: Ed25519 で高速署名

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey

sk = Ed25519PrivateKey.generate()
pk = sk.public_key()

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

sig = sk.sign(data)
print(f'Ed25519 署名サイズ: {len(sig)} バイト')   # 64 バイト

pk.verify(sig, data)   # 成功なら何も起こらず, 失敗で例外
print('検証成功')
📤 実行例(実測) Ed25519 署名サイズ: 64 バイト 検証成功

レシピ2: 自己署名 X.509 証明書

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from cryptography import x509
from cryptography.x509.oid import NameOID
from datetime import datetime, timedelta

sk = rsa.generate_private_key(65537, 2048)
subject = issuer = x509.Name([
    x509.NameAttribute(NameOID.COUNTRY_NAME, 'JP'),
    x509.NameAttribute(NameOID.ORGANIZATION_NAME, 'SSDSE Test'),
    x509.NameAttribute(NameOID.COMMON_NAME, 'test.example.jp'),
])
cert = (x509.CertificateBuilder()
        .subject_name(subject)
        .issuer_name(issuer)
        .public_key(sk.public_key())
        .serial_number(x509.random_serial_number())
        .not_valid_before(datetime.utcnow())
        .not_valid_after(datetime.utcnow() + timedelta(days=365))
        .sign(sk, hashes.SHA256()))

with open('outputs/cert.pem','wb') as f:
    f.write(cert.public_bytes(serialization.Encoding.PEM))

レシピ3: PDF への署名 (pyHanko)

1
2
3
4
5
6
7
8
from pyhanko.sign import signers, fields
from pyhanko.pdf_utils.incremental_writer import IncrementalPdfFileWriter

with open('report.pdf','rb') as f, open('report_signed.pdf','wb') as out:
    w = IncrementalPdfFileWriter(f)
    fields.append_signature_field(w, sig_field_spec=fields.SigFieldSpec('Signature1'))
    signer = signers.SimpleSigner.load_pkcs12('cert.p12', passphrase=b'secret')
    signers.sign_pdf(w, signers.PdfSignatureMetadata(field_name='Signature1'), signer=signer, output=out)

レシピ4: タイムスタンプ局 (TSA) で時刻認証

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import os
import rfc3161ng

os.makedirs('outputs', exist_ok=True)   # 保存先が無いと書けない

# 公開 TSA を利用 (例: Sectigo)
tsa = rfc3161ng.RemoteTimestamper('http://timestamp.sectigo.com', hashname='sha256')
with open('data/raw/SSDSE-B-2026.csv', 'rb') as f:
    data = f.read()

# ブラウザ版 Python は外部のサーバーに接続できない。
# つながらないときは、何が起きたのかを日本語で示して終える。
try:
    tst = tsa(data=data)  # タイムスタンプトークン
    with open('outputs/timestamp.tst', 'wb') as f:
        f.write(tst)
    print('タイムスタンプ取得成功 (法的時効を防ぐ)')
except Exception as e:
    print('外部のタイムスタンプ局に接続できませんでした:', type(e).__name__)
    print('ブラウザ版 Python は外部通信ができないためです。'
          'お手元の Python で実行すれば、そのまま取得できます。')

レシピ5: GPG で署名 (コマンドライン版)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 鍵生成
gpg --gen-key

# ファイルに署名 (デタッチド形式)
gpg --detach-sign --armor data/raw/SSDSE-B-2026.csv
# → SSDSE-B-2026.csv.asc が生成される

# 検証
gpg --verify SSDSE-B-2026.csv.asc SSDSE-B-2026.csv
# → "Good signature from ..." と出れば成功

🚫 アンチパターン集

  1. 秘密鍵を Git に commit → 即漏洩。 .gitignore 必須。
  2. 秘密鍵をパスワード保護なしで保存 → ファイル盗難で破滅。 必ず passphrase で暗号化。
  3. SHA-1 や MD5 を使い続ける → 衝突攻撃で署名偽造可能。 SHA-256 以上に。
  4. PKCS#1 v1.5 を新規システムで採用 → パディングオラクル攻撃。 RSA-PSS に。
  5. 1024 ビット RSA を使い続ける → 短すぎる。 最低 2048、 できれば 3072 以上。
  6. 公開鍵の真正性を確認しない (CA なし) → MITM 攻撃で署名偽装。
  7. 鍵の有効期限を設けない → 漏洩時に永久に被害が広がる。
  8. 鍵を 1 つで全用途に使用 → 役割分離なし。 署名鍵と暗号化鍵は分けるのが原則。
  9. 署名対象を確認せずに「全文書」 を一括署名 → 想定外のものに署名する事故。
  10. 失効リスト (CRL/OCSP) を確認しない → 漏洩した古い鍵での署名を受理。

❓ FAQ

Q1. 電子署名と電子サインの違い
A. 「電子署名」 は暗号学的に厳密な技術 (PKI ベース)。 「電子サイン」 は契約書 PDF にスタイラスペンで書く絵のような署名。 法的効力は前者の方が強い。
Q2. RSA と ECDSA、 どちらを選ぶ?
A. 新規プロジェクトは Ed25519 か ECDSA を推奨。 互換性のため RSA が必要な場合は PSS + SHA-256 + 2048 以上で。
Q3. マイナンバーカードの署名はどう動く?
A. IC チップに RSA 2048 鍵が内蔵。 公的個人認証サービス (JPKI) として e-Tax 等で利用可能。 暗証番号で守られる。
Q4. ポスト量子暗号への移行は?
A. NIST が 2022 年に Dilithium 等を標準化。 主要プラットフォーム (OpenSSH, AWS, etc.) が 2024-2030 年にかけて段階移行中。
Q5. SSDSE のような公的データに署名する意味は?
A. 配布途中で改ざんされていないことを暗号学的に保証。 オープンデータ時代には信頼性確保の重要技術。

🧮 数式に値を入れて手で計算する: RSA 鍵長と署名検証時間

合成データで RSA 鍵長別の署名検証時間 [ms] を計算する。

Step 1: 鍵長別パフォーマンス

鍵長 [bit]署名 [ms]検証 [ms]
10241.20.05
20488.00.20
307220.00.40
409640.00.70

Step 2: スケーリング

2048 → 4096 で署名 5 倍 (8→40)、 検証 3.5 倍 (0.2→0.7) 鍵長 2 倍で署名コストは指数的に増加

🐍 Python で再現

1
2
3
4
5
6
import numpy as np
bits = np.array([1024, 2048, 3072, 4096])
sign = np.array([1.2, 8.0, 20.0, 40.0])
verify = np.array([0.05, 0.20, 0.40, 0.70])
print(f"署名比 2048→4096: {sign[3]/sign[1]:.2f}")
print(f"検証比 2048→4096: {verify[3]/verify[1]:.2f}")

📤 実行結果

署名比 2048→4096: 5.00 検証比 2048→4096: 3.50

💬 手計算 (Step 2) 5 倍 / 3.5 倍と Python 出力が完全一致。

🐍 Python 実装

① SHA-256 ハッシュを計算

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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())
📤 実行例(実測) SHA-256: 0fdbe5f603bb8e1ee72d83cca77e674cf9b45865d88c8f4fda5eb8046ea6f463 ファイルサイズ: 359821 bytes tampered: 0fdbe5f603bb8e1ee72d83cca77e674cf9b45865d88c8f4fda5eb8046ea6f463

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
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 に署名する

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes, serialization
# ↑ このコードは cryptography ライブラリが必要
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
📤 実行例(実測) 署名長: 256 bytes

④ 受信者側で検証する

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
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: ファイルが改ざんされているか鍵が違います')
📤 実行例(実測) OK: 本物の SSDSE-B-2026 です

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

📥 入力例(SSDSE-B-2026 全体:564 行 × 112 列 = 47 都道府県 × 2012〜2023 年) 年度 地域コード 都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数) … 2023 R01000 北海道 5,092,000 1,681,000 24,430 … 2023 R13000 東京都 14,086,000 3,205,000 86,348 … 2023 R47000 沖縄県 1,468,000 350,000 12,549 … …(残り 112 列は住宅・家計・教育・医療など)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import os
os.makedirs('data', exist_ok=True)   # 書き出し先を先に作る
import os
os.makedirs('data/raw', exist_ok=True)  # 保存先のフォルダを作っておく

import pandas as pd, hashlib

# 英字の項目コード(Prefecture)を使うので skiprows=[1] で読む
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df.rename(columns={'SSDSE-B-2026': 'Year'})   # 年度の列名を分かりやすく

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)
📤 実行例(実測) Prefecture Year fingerprint 0 北海道 2023 bee9b2532bfd97e3 1 北海道 2022 72f17321fc93f9eb 2 北海道 2021 fe59f43ff6b2e1d1 3 北海道 2020 a255968decc1c930 4 北海道 2019 f14ebbad0915804e 5 北海道 2018 a99e552ba9ea3194 6 北海道 2017 f36e802053a72ff6 7 北海道 2016 e3555077834b4e9c 8 北海道 2015 5cf2dfcc96d8d7c9 9 北海道 2014 f571c17213ee5497

⑥ ECDSA も併記

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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')
📤 実行例(実測) ECDSA 署名長: 71 bytes ECDSA 検証 OK

⑦ EdDSA (Ed25519) を使う

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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')
📤 実行例(実測) Ed25519 署名長: 64 bytes 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 を使う。 既存システムは段階的移行。

🗺 概念マップ — 電子署名の位置

                       ┌──────────────────┐
                       │   暗号技術全体     │
                       └────────┬─────────┘
                                │
            ┌───────────────────┼───────────────────┐
            ▼                   ▼                   ▼
       ┌─────────┐         ┌─────────┐        ┌─────────┐
       │ 共通鍵暗号 │         │公開鍵暗号  │        │ ハッシュ  │
       │AES等     │         │RSA, ECC  │        │SHA-256等 │
       └─────────┘         └────┬────┘        └─────────┘
                                │
              ┌─────────────────┼───────────────┐
              ▼                 ▼               ▼
        ┌──────────┐       ┌──────────┐    ┌──────────┐
        │ 鍵交換    │       │  暗号化   │    │ 電子署名   │
        │ DH, ECDH │       │  RSA encr │    │RSA, ECDSA│
        └──────────┘       └──────────┘    └────┬─────┘
                                                │
                                ┌───────────────┼───────────────┐
                                ▼               ▼               ▼
                          ┌──────────┐    ┌──────────┐    ┌──────────┐
                          │ PKI       │    │タイムスタンプ│    │PDF/CMS署名│
                          │ X.509, CA │    │TSA (RFC3161)│    │S/MIME    │
                          └──────────┘    └──────────┘    └──────────┘
                                ▼
                           ┌────────────┐
                           │ HTTPS, JWT │
                           │ ブロックチェーン│
                           └────────────┘
  
電子署名 公開鍵暗号 (RSA/ECDSA) MAC・HMAC ブロックチェーン 電子契約・PDF 署名 SSL/TLS 証明書 ハッシュ関数 (SHA-256)

🔗 隣接手法への橋渡し

「電子署名」は単独で完結する手法ではなく、 隣接領域と連携することで真価を発揮する。 具体的には次の 3 方向と密接につながる:

電子署名は完全性・認証・否認防止を一体で実現する暗号応用の核心。

🌳 手法選択フロー

「電子署名」を実際に使うとき、 何をどう選ぶかを順に判断する。 上から順に答えていくと、 使うべき手法と評価の仕方が決まる。

  1. 何を保証したいのか
    「改ざんされていない」だけなら、 ハッシュ値の公開で足りる場面もある。 「誰が作ったか」まで保証したいなら署名が要る。 秘密鍵を持つ人しか作れない値だから成り立つ。
  2. 鍵をどう守るか
    秘密鍵が漏れれば、 その人になりすまして何でも署名できる。 署名の安全性は暗号の強さではなく、 鍵の保管方法で決まる。
  3. 公開鍵が本物だとどう確かめるか
    公開鍵をすり替えられれば署名検証は無意味になる。 認証局の証明書か、 別経路での指紋確認が要る。
  4. いつ署名したかを示す必要があるか
    必要ならタイムスタンプを併用する。 鍵の有効期限が切れたあとも検証できるようにするには、 長期署名の仕組みが要る。

電子署名は「誰が」「何に」同意したかを、 あとから第三者が確かめられるようにする仕組み。 データの配布では、 ハッシュ値の公開だけでも改ざん検知には役立つ。

🔍 解説深化 — 署名が守るのは「バイト列」であって「意味」ではない

💡 直感

電子署名を実データ運用に持ち込むと、 本文で学んだ「署名 = 封蝋」の比喩に 1 つ重要な但し書きが付きます。 署名が固定するのはファイルの「バイト列」そのものであって、 その中身が表す「意味 (データの値)」ではないという点です。 実際に data/raw/SSDSE-B-2026.csv で測ってみると:

対象 (すべて実測)サイズSHA-256 (実測値)
配布された CSV そのまま (cp932・CRLF 改行)359,821 バイト0fdbe5f603bb8e1e...046ea6f463
先頭 1 バイトの最下位 1 ビットだけ反転 (メモリ上で計算)359,821 バイト934c484a1a425631...4106e9471 の形に激変
pandas で読み込み → to_csv() で保存し直し (UTF-8・LF 改行)358,876 バイト2a43aab790c1320b...59420e22

1 ビットの反転で 256 ビット中 133 ビット (52%)、 64 桁の 16 進表記では 59 桁が変化しました (実測)。 これが雪崩効果の定量的な姿です。 一方 3 行目に注目してください。 pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) で読み込んで保存し直した CSV は、 564 行 × 112 列のデータ値が元と完全に一致する (df.equals() が True) のに、 文字コードが cp932 → UTF-8、 改行が CRLF (実測 566 行分) → LF に変わるためハッシュは全くの別物になります。 つまり「意味は同一・バイト列は別物」という状態が、 悪意ゼロでも日常的に発生するのです。

⚠️ 落とし穴(重要)

① 「検証失敗 = 悪意の改ざん」と即断しない。 上の実測が示す通り、 Excel で開いて上書き保存、 git の autocrlf 設定、 エディタの文字コード自動変換、 pandas での再保存——どれもバイト列を変えて検証を失敗させます。 検証はあくまで「配布されたバイト列と違う」ことしか教えてくれません。 原因の切り分け (再エンコードか改ざんか) は人間の仕事です。 対策は単純で、 署名付き原本は読み取り専用で保管し、 分析はコピーに対して行うこと。

② 逆向きも成り立たない:「検証成功 = 内容が正しい」ではない。 署名が保証するのは「署名者が署名した時点のバイト列のまま」という真正性・完全性だけで、 データそのものの正確さは 1 ミリも保証しません。 署名者が誤った集計値を署名すれば、 誤りが「正規品」として流通します (garbage in, signed garbage out)。 統計データの品質検証 (外れ値・欠測・定義変更の確認) は署名検証とは独立に必要です。

③ 「何をシリアライズして署名したか」の取り決めがないと検証不能。 行単位の指紋を作る場合、 「列の順序・区切り文字・数値の書式・文字コード」を 1 つでも取り決め忘れると、 正しいデータからでもハッシュが再現できません。 署名は正規化 (canonicalization) の規約とセットで初めて機能します。

🚀 発展

正規化署名:この問題への正攻法が「バイト列ではなく正規形に署名する」設計で、 XML C14N (正規化 XML) や JSON の JCS (RFC 8785) が代表です。 CSV には広く合意された正規形がないため、 実務では「配布バイト列そのものを不変の原本とする」運用が主流です。

行単位フィンガープリント (実測):本文の「行ハッシュ」案を実際に計算してみます。 教材用の簡易規約として「年,県名,総人口 を UTF-8 でつないで SHA-256」と決めると (2023 年の実データ A1101 列):

行 (2023 年・実測値)シリアライズ例SHA-256 先頭 16 桁 (実測)
北海道 総人口 5,092,000 人2023,北海道,50920000d2b1cf615689d40
東京都 総人口 14,086,000 人2023,東京都,1408600078111d8a8271473f
広島県 総人口 2,738,000 人2023,広島県,2738000fcd0e661b0396561
北海道の値を 1 だけ増やした場合 (改ざん想定)2023,北海道,5092001059f749a194c4b0b → ❌ 不一致で行単位検出

これをさらにマークル木 (Merkle tree) に組むと、 SSDSE-B-2026 の全 564 行のうち「北海道 2023 の行だけが原本通り」であることを、 全行を渡さずに約 10 個 (log₂ 564 ≈ 9.1 段) の中間ハッシュだけで証明できます。 ルートハッシュ 1 つに署名すれば全行が守られる——ブロックチェーンや Certificate Transparency がこの構造です。 さらに 透明性ログ (CT ログ、 Sigstore の Rekor) は「署名の存在自体を追記専用ログに公開して、 署名者による後出し・差し替えも検知する」仕組みで、 否認防止を運用面から補強します。 データ分析の文脈では、 DVC などのデータバージョン管理ツールが「コンテンツハッシュでデータを指す」という同じ思想の上に立っています。

🔗 関連ページ