こんにちは!日々の開発やインフラの管理、本当にお疲れ様です。
セキュリティの世界に足を踏み入れると、聞き慣れない英語や難解な数学の用語がたくさん出てきて、思わず「うっ…」と頭を抱えたくなりますよね。特に「暗号」や「電子署名」の話になると、数式が並んでいて一歩引いてしまう方も多いのではないでしょうか。
でも、安心してください!一歩ずつ、身近な例えから紐解いていけば、決して越えられない壁ではありません。今回は、システムの信頼性を裏で支えるとても大切なテーマである「RSA-PSS署名とハッシュ関数の仕組み」について、一緒に優しく学んでいきましょう!
—
1. 家の鍵で例える「電子署名」と「パディング」の基本
まずは、そもそも「電子署名」って何をしているものなのか、私たちの身近な「家の鍵とハンコ」に例えて考えてみますね。
あなたが重要な契約書に判子を押すとき、「これは本物の私ですよ」と証明するために印鑑を押しますよね。デジタル世界でこれをやるのが「電子署名」です。秘密の鍵(自分だけが持っているハンコ)を使ってデータに署名をし、誰もが持っている公開鍵(ハンコが本物か確かめる型紙)でそれが正しいか検証します。
「決まったパターン」は泥棒に狙われやすい?
昔ながらの古い署名方式では、同じデータに対してはいつも全く同じ署名が作られる仕組みになっていました。
これって、防犯に例えると「いつも同じ合い言葉でドアを開けているようなもの」なんです。もし泥棒(悪意ある攻撃者)がその合い言葉をこっそり聞いてしまったら、簡単に合鍵を作って侵入できてしまいますよね。
そこで登場するのが、今回の主役である「PSS(Probabilistic Signature Scheme:確率的署名スキーム)」です。
PSSは、同じデータに署名をするたびに、毎回ランダムなノイズ(これを「ソルト」や「パディング」と呼びます)を混ぜ込みます。つまり、「毎回ドアを開けるときの合い言葉が変わるシステム」に進化させたわけです。これなら、仮に過去の通信を盗み見られても、次に使われる合い言葉は全く予想がつかないので、安全性がグッと跳ね上がりますよね。
—
2. RSA-PSS署名を支える「ハッシュ関数」の役割
このRSA-PSS署名の中で、めちゃくちゃ重要な役割を果たしているのが「ハッシュ関数(SHA-256など)」です。
ハッシュ関数というのは、どんなに長い文章でも、一瞬で「固定的で短いランダムな文字列(指紋のようなもの)」に変換してくれる魔法の箱だと思ってください。
例えば、「こんにちは」という言葉をハッシュ化すると、一瞬で 2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae のような文字列に変わります。
ここで大切なポイントがあります。
ハッシュ関数は、元の文章がほんの1文字(「こんにちは」が「こんにちわ」に)変わるだけで、出てくる指紋(ハッシュ値)が全く別のものにガラリと変わります。これを「衝突耐性(同じ指紋を持つ別の文章を簡単には作れない性質)」と呼びます。
ハッシュ関数の依存関係が崩れるとどうなる?
RSA-PSS署名では、このハッシュ関数を使ってデータの指紋を作り、そこにランダムなパディングを組み合わせて暗号化します。
もし、ここで使っているハッシュ関数に古くて脆弱なもの(例えば、昔に使われていたMD5やSHA-1など)を選んでしまうとどうなるでしょうか?
攻撃者がスーパーコンピューターなどを使って「偶然、同じ指紋(ハッシュ値)になる別の偽のデータ」を作り出すこと(ハッシュ衝突)が、現実的な時間で可能になってしまいます。
結果として、「中身はすり替わっているのに、署名の検証はなぜか『合格』してしまう」という、セキュリティ事故としては最悪の事態を引き起こしてしまうのです。これが、「署名の安全性は、使っているハッシュ関数の強さに強く依存している」と言われる所以なんですね。
—
3. 実務でどう設定する?安全なコード例を見てみよう
「理屈は分かったけれど、実際の開発ではどう書けばいいの?」という声にお応えして、モダンな言語(今回はNode.jsを例にします)で、安全なRSA-PSS署名を行うコードを見てみましょう。
実務でコードを書くときは、アルゴリズムのパラメータやハッシュ関数の指定を正しく行うことが何よりも大切です。以下のサンプルコードのコメントを参考にしながら、ポイントを確認してみてください。
const crypto = require('crypto');
// 1. 署名に使用するメッセージを定義
const message = '大切なお知らせ:システムのセキュリティ設定を更新しました。';
// 2. RSAキーペア(秘密鍵と公開鍵)を生成する
// 実務ではファイルや環境変数、KMS(鍵管理サービス)から安全に読み込みます
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048, // 鍵の長さは2048ビット以上(できれば4096ビット)が現在の標準です
});
// 3. 【重要】RSA-PSS署名を作成する
// ここでハッシュ関数に 'sha256' を指定し、パディングのsaltLength(ソルト長)を適切に設定します。
const signature = crypto.sign('sha256', Buffer.from(message), {
key: privateKey,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING, // 確率的パディング(PSS)を指定
saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST, // ソルトの長さをハッシュ長に合わせるのが安全
});
console.log('生成された署名 (Hex):', signature.toString('hex'));
// 4. 署名を検証する(受け取り側の処理)
const isVerified = crypto.verify(
'sha256',
Buffer.from(message),
{
key: publicKey,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING, // 送信側と同じPSSパディングを指定
saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST, // 検証側も同じソルト長にする必要あり
},
signature
);
if (isVerified) {
console.log('【成功】署名は本物です。データの改ざんはありません!');
} else {
console.log('【警告】署名が無効です。データが書き換えられているか、鍵が違います!');
}
コードのポイント
padding: crypto.constants.RSA_PKCS1_PSS_PADDING: ここで古いPKCS#1 v1.5ではなく、しっかりとPSSパディングを指定している点が、今回のテーマの肝です。sha256の指定: 脆弱性が指摘されている古いハッシュ関数(sha1やmd5)は絶対に避け、最低でもsha256(できればより強力なsha384やsha512)を指定するようにしましょう。
—
まとめ:一歩ずつ、セキュアな実装へ
今回は、RSA-PSS署名における確率的パディングの仕組みと、ハッシュ関数が果たす役割について、家の鍵や防犯の例えを交えてお話ししました。
- PSSパディングを使うことで、毎回異なる署名を作り出し、攻撃者による解析を防ぐことができる。
- その安全性の土台は「強力なハッシュ関数(SHA-256など)」の衝突耐性に依存しているため、古いハッシュ関数を使い続けるのは絶対にNG。
- 実際の開発では、ライブラリのデフォルト設定を鵜呑みにせず、パディング方式やハッシュアルゴリズム(
crypto.constants.RSA_PKCS1_PSS_PADDINGやsha256など)を明示的に正しく設定する。
セキュリティの知識は一度にすべてを完璧に理解するのは難しいですが、こうして一つひとつの技術背景や「なぜそれが必要なのか」を紐解いていくことで、確実に対応できる幅が広がっていきます。
ぜひ、皆さんのプロジェクトコードを見直す際の参考にしてみてくださいね。一歩ずつの安全な積み重ねが、強固なシステムを作り上げます。それでは、次のセキュリティ解説もお楽しみに!
コメント