🎄 科学と神々株式会社 アドベントカレンダー 2025
License System Day 6: ECDSA P-256署名の仕組み
📖 今日のテーマ
昨日は対称鍵と非対称鍵の基礎を学びました。今日は、このシステムで使用するECDSA P-256という楕円曲線暗号について深掘りします。
「なぜRSAではなくECDSAを選ぶのか?」その答えがここにあります。
🔐 ECDSA とは?
正式名称
ECDSA = Elliptic Curve Digital Signature Algorithm
(楕円曲線デジタル署名アルゴリズム)
3つの要素
1. Elliptic Curve(楕円曲線)
数学的な曲線を使った暗号化
2. Digital Signature(デジタル署名)
データの真正性を証明する
3. Algorithm(アルゴリズム)
署名生成と検証の手順
📐 楕円曲線とは何か?
数学的な定義
y² = x³ + ax + b
例: P-256 カーブ
y² = x³ - 3x + b
ビジュアルイメージ
y
↑
│ ╱╲
│ ╱ ╲
│ ╱ ╲
──┼──╱──────╲────→ x
│╱ ╲
│ ╲
│ ╲
なぜ楕円曲線を使うのか?
理由1: 鍵サイズが小さい
RSA 2048bit = ECDSA 224bit(同等の安全性)
RSA 3072bit = ECDSA 256bit
RSA 15360bit = ECDSA 521bit
理由2: 高速
署名生成・検証が RSA より速い
理由3: メモリ効率
組み込み機器やモバイルに最適
🎯 P-256 カーブとは?
正式名称
NIST P-256 (prime256v1, secp256r1)
パラメータ
名前: P-256
鍵サイズ: 256 bit
セキュリティレベル: 128 bit(AES-128 相当)
標準化: NIST, SECG
曲線方程式:
y² = x³ - 3x + b (mod p)
p = 2²⁵⁶ - 2²²⁴ + 2¹⁹² + 2⁹⁶ - 1
なぜ P-256 を選ぶのか?
✅ 広くサポートされている
- Node.js
- OpenSSL
- Web Crypto API
- ほぼすべてのプラットフォーム
✅ 高速
- モバイルデバイスでも快適
✅ 十分な安全性
- 128bit セキュリティレベル
- 2030年代まで安全とされる
✅ 鍵サイズが小さい
- 32バイト(256bit)
- ネットワーク転送に最適
🔑 鍵ペアの生成
Node.js での実装
const crypto = require('crypto');
// ECDSA P-256 鍵ペアの生成
const { publicKey, privateKey } = crypto.generateKeyPairSync('ec', {
namedCurve: 'prime256v1', // P-256
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem'
}
});
console.log('公開鍵:\n', publicKey);
console.log('秘密鍵:\n', privateKey);
出力例
公開鍵:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE3p8xHZx9...
-----END PUBLIC KEY-----
秘密鍵:
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawI...
-----END PRIVATE KEY-----
✍️ デジタル署名の仕組み
署名生成(サーバー側)
function signData(data, privateKey) {
const sign = crypto.createSign('SHA256');
sign.update(JSON.stringify(data));
sign.end();
const signature = sign.sign(privateKey, 'base64');
return signature;
}
// 使用例
const responseData = {
status: 'activated',
license_id: '12345',
plan: 'premium'
};
const signature = signData(responseData, privateKey);
console.log('署名:', signature);
// → MEUCIQDz7vkKYYj...
署名検証(クライアント側)
function verifySignature(data, signature, publicKey) {
const verify = crypto.createVerify('SHA256');
verify.update(JSON.stringify(data));
verify.end();
const isValid = verify.verify(publicKey, signature, 'base64');
return isValid;
}
// 使用例
const isValid = verifySignature(responseData, signature, publicKey);
if (isValid) {
console.log('✅ 署名が正しい!データは改ざんされていません');
} else {
console.log('❌ 署名が不正!データが改ざんされています');
}
🔍 署名の中身を見てみよう
DER エンコーディング
ECDSA 署名のフォーマット(ASN.1 DER):
SEQUENCE {
r: INTEGER (256 bit)
s: INTEGER (256 bit)
}
例:
30 45 SEQUENCE, 69 bytes
02 21 INTEGER, 33 bytes (r)
00 d3f7be42a...
02 20 INTEGER, 32 bytes (s)
7a8e2c15b9...
合計: 約70バイト(可変長)
Base64 エンコード
バイナリ → Base64 で文字列化
例:
MEUCIQDz7vkKYYjPxQw3vR2S8...
利点:
- JSONで送信可能
- URLセーフ
- 人間が読める(一応)
⚡ RSA vs ECDSA 徹底比較
パフォーマンス
署名生成(1000回):
RSA 2048: 1.2秒
ECDSA P-256: 0.3秒 ← 4倍速い!
署名検証(1000回):
RSA 2048: 0.05秒
ECDSA P-256: 0.6秒 ← RSAの方が速い
実際のユースケース:
サーバー: 署名生成が頻繁 → ECDSA が有利
クライアント: 検証のみ → どちらでもOK
鍵サイズ
同等の安全性:
RSA 2048 bit ≈ ECDSA 224 bit
RSA 3072 bit ≈ ECDSA 256 bit
RSA 15360 bit ≈ ECDSA 521 bit
実際のファイルサイズ:
RSA 公開鍵: ~450 bytes
ECDSA 公開鍵: ~120 bytes ← 1/4サイズ!
署名サイズ
RSA 2048: 256 bytes
ECDSA P-256: 64-72 bytes ← 1/4サイズ!
API レスポンス例:
データ: 1 KB
RSA署名: 256 bytes (25%増加)
ECDSA署名: 70 bytes (7%増加) ← 効率的!
🛡️ セキュリティレベル
ビット強度
ECDSA P-256:
鍵サイズ: 256 bit
セキュリティレベル: 128 bit
これは何を意味するか?
128 bit = 2^128 通りの組み合わせ
= 340,282,366,920,938,463,463,374,607,431,768,211,456 通り
現在のスーパーコンピューターでも
数十億年かかる計算量
攻撃耐性
✅ 総当たり攻撃: 不可能(2^128 通り)
✅ 量子コンピューター: 2030年代まで安全
✅ 数学的攻撃: 現時点で有効な攻撃なし
注意:
量子コンピューターの発展により
将来的には P-384, P-521 への移行が推奨される
💡 実装時のベストプラクティス
1. ハッシュ関数の選択
// ❌ 弱いハッシュ
crypto.createSign('SHA1'); // 脆弱!
// ✅ 推奨
crypto.createSign('SHA256'); // 安全
crypto.createSign('SHA384'); // より安全
crypto.createSign('SHA512'); // 最も安全
2. 鍵の安全な保管
// ❌ 危険
const privateKey = 'ハードコード';
// ✅ 推奨
const privateKey = fs.readFileSync(
process.env.PRIVATE_KEY_PATH,
'utf8'
);
// さらに良い
const privateKey = await secretManager.getSecret('private-key');
3. エラーハンドリング
function verifySignature(data, signature, publicKey) {
try {
const verify = crypto.createVerify('SHA256');
verify.update(JSON.stringify(data));
verify.end();
return verify.verify(publicKey, signature, 'base64');
} catch (error) {
console.error('署名検証エラー:', error.message);
return false; // 失敗として扱う
}
}
🌟 まとめ
今日学んだこと:
-
ECDSA の基本
- 楕円曲線を使った署名アルゴリズム
- 高速・コンパクト・安全
-
P-256 カーブ
- 256bit 鍵サイズ
- 128bit セキュリティレベル
- 広くサポートされている
-
RSA との比較
- 署名生成: ECDSA が4倍速い
- 鍵サイズ: ECDSA が1/4
- 署名サイズ: ECDSA が1/4
-
実装のポイント
- SHA256 以上のハッシュ
- 鍵の安全な保管
- 適切なエラーハンドリング
🎓 理解度チェック
- ECDSA の利点は?
- P-256 のセキュリティレベルは?
- なぜ RSA ではなく ECDSA を選ぶ?
- 署名検証の流れは?
💡 次回予告
Day 7: JWT(JSON Web Token)入門
- JWT の構造
- Header, Payload, Signature
- なぜ JWT を使うのか
- 実際の実装例
お楽しみに!
前回: Day 5: 暗号化の基礎 - 対称鍵と非対称鍵
次回: Day 7: JWT(JSON Web Token)入門
Happy Learning! 🎉