公開鍵暗号とデジタル署名 入門 - なぜ鍵を公開しても安全なのか

公開鍵暗号とデジタル署名 入門 - なぜ鍵を公開しても安全なのか

作成日:
読了:19
更新日:

「秘密の鍵は隠すもの」というのが常識のはずなのに、公開鍵暗号では鍵の片方を堂々と世界中に配ります。それでいて安全が保たれるのはなぜでしょうか。HTTPS でサイトに繋いだとき、パスキーでログインしたとき、git commit -S で署名したとき——私たちは毎日この不思議な仕組みの恩恵を受けています。

この記事では、公開鍵暗号デジタル署名のエッセンスを、暗号の専門家でない Web 開発者向けに整理します。数式は最小限にして、たとえ話と擬似的な例で「なぜ鍵を公開しても大丈夫なのか」「署名は何をどう保証するのか」を直感的につかめるようにします。事実は RFC・NIST を一次ソースに確認しています。

素朴な疑問 - 鍵を公開して大丈夫なの

暗号と聞いて多くの人が思い浮かべるのは、南京錠のような「1つの鍵で閉めて、同じ鍵で開ける」イメージでしょう。これは対称鍵暗号(共通鍵暗号)の発想で、実際に世の中の大量のデータはこの方式で守られています。

ところが公開鍵暗号では、鍵が2つで1組(鍵ペア)になっていて、片方を公開鍵として誰にでも配り、もう片方を秘密鍵として自分だけが握ります。公開鍵から秘密鍵を割り出すことが現実的な時間ではできない、という数学的な性質があるおかげで、鍵の片方を公開しても破られません。まずは「なぜそんな都合のよい仕組みが必要になったのか」から見ていきます。

対称鍵の限界 - 鍵配送問題

対称鍵暗号(代表格は AES)は高速で、大きなデータの暗号化に向いています。ところが1つ、原理的な弱点があります。それが鍵配送問題です。

送信者と受信者が同じ秘密鍵を共有していなければ、暗号文をやり取りできません。では、その秘密鍵をどうやって相手に渡すのでしょうか。鍵そのものを盗聴されうる回線で送ってしまえば、暗号化した意味がありません。

  • 相手が10人いれば、それぞれと別の鍵を安全に共有する必要がある
  • 初対面の相手(たとえば初めて訪れる EC サイト)とは、共有する鍵がそもそも存在しない
  • 参加者が増えると、必要な鍵の数が急増して管理しきれなくなる

「安全な通信をしたいのに、そのための鍵を安全に配る手段がない」——このニワトリと卵の問題を解いたのが公開鍵暗号の発想です。

公開鍵暗号の考え方 - 鍵ペアと一方向性

公開鍵暗号のカギは、計算するのは簡単だが、逆算するのは極端に難しいという「一方向性」を持つ数学的な操作を使うことです。

イメージしやすいたとえは郵便ポストです。ポストの投入口(公開鍵)は誰でも使えて、手紙を投函するのは簡単です。しかし一度入れた手紙を取り出すには、ポストの扉を開ける鍵(秘密鍵)が要ります。投入口を世界中に公開しても、扉の鍵を持つ人だけが中身を読める、という非対称性が成り立ちます。

これを使うと、鍵配送問題が解けます。

  1. 受信者はあらかじめ鍵ペアを作り、公開鍵を誰にでも配っておく
  2. 送信者は受信者の公開鍵で暗号化して送る
  3. 暗号文を復号できるのは、対応する秘密鍵を持つ受信者だけ

事前に秘密の鍵を共有していない相手とも、安全にやり取りを始められるわけです。この一方向性を支える具体的な数学が、次に見る RSA や楕円曲線暗号です。

NOTE

実際の HTTPS では、通信全体を公開鍵暗号で暗号化するわけではありません。公開鍵暗号は「対称鍵を安全に共有する」ためや「相手を認証する」ために使い、実データの暗号化は高速な対称鍵 (AES など) に任せる、という役割分担が一般的です。両者は競合ではなく組み合わせて使います。

RSA の直感 - 大きな数の素因数分解

RSA(Rivest-Shamir-Adleman の頭文字)は、最も広く知られた公開鍵暗号方式です。仕様は RFC 8017 (PKCS#1) にまとまっており、FIPS 186-5 でも署名方式として認められています。

RSA の安全性が寄って立つのは、大きな数の素因数分解の難しさです。素数を2つ掛け算するのは簡単ですが、その積だけを見て元の2つの素数を復元するのは、数が十分大きいと現実的な時間では解けません。

掛けるのは簡単、素因数分解は難しい(イメージ)
61 × 53 = 3233        ← 前向きの計算は一瞬
3233 = ? × ?          ← 逆算は総当たりに近く、桁が増えると爆発的に困難

もちろん 3233 程度なら一瞬で割れますが、実際の RSA では数百桁の巨大な素数を使います。公開鍵にはこの「積」に相当する情報が含まれ、秘密鍵は「元の素数」を知っている側だけが持ちます。だから公開鍵を配っても、そこから秘密鍵を逆算できないわけです。

鍵長については、NIST SP 800-131A / SP 800-57 系の指針で RSA は 2048 ビット以上が求められ、より高い安全性が要る用途では 3072 ビットが使われます。年々「安全とされる鍵長」は伸びる傾向にあるので、数値は都度一次ソースで確認するのが安全です。

楕円曲線 (ECC/Ed25519) がなぜ主流に

RSA は堅実ですが、高い安全性を得ようとすると鍵が大きくなりがちです。そこで近年広く使われるようになったのが楕円曲線暗号 (ECC) です。安全性の根拠は、楕円曲線上の点に関する「離散対数問題」の難しさで、素因数分解とは別種の一方向性を利用します。

最大の利点は鍵の短さです。ざっくり言うと、同じ強度を保つのに RSA よりずっと短い鍵で済みます。

方式代表的な鍵長おおよその安全性強度
RSA2048 ビット112 ビット相当
RSA3072 ビット128 ビット相当
楕円曲線 (P-256 など)256 ビット128 ビット相当

NIST の指針では、256 ビットの曲線 (P-256) はおおむね 128 ビット相当の強度を持つとされます。鍵や署名が短いほど、通信量・保存量・計算コストが小さくなるため、モバイルや大量接続をさばくサーバで有利です。

署名用途では、ECDSA(FIPS 186-5 が規定、元は ANS X9.62)に加えて、Ed25519(Edwards 曲線を使う EdDSA の一種、RFC 8032 が規定)がとくに人気です。Ed25519 は署名が高速で、実装時の落とし穴が少ない設計になっており、FIPS 186-5 でも承認されています。SSH や Git 署名、パスキーなど現代的な場面で標準的な選択肢になっています。

NOTE

「RSA より ECC が新しくて強い」という単純な話ではありません。どちらも適切な鍵長で使えば現時点では安全とされ、既存資産や相互運用性で RSA が選ばれる場面も多くあります。後述する量子計算の観点では、両者とも同じ弱点を抱えます。

デジタル署名 - 暗号化とは目的が違う

ここまでは「秘密を守る(機密性)」話でしたが、公開鍵暗号のもう一つの大きな用途がデジタル署名です。署名は暗号化とは目的が異なります。中身を隠すのではなく、「誰が作ったか」と「改ざんされていないか」を証明するための仕組みです。

鍵の使い方が暗号化とちょうど逆になります。

  • 暗号化: 公開鍵で暗号化し、秘密鍵で復号する(受信者の鍵ペアを使う)
  • 署名: 秘密鍵で署名し、公開鍵で検証する(送信者の鍵ペアを使う)

秘密鍵を持つ本人しか正しい署名を作れず、一方で公開鍵を持つ誰もがその署名を検証できます。これによって次の3つが保証されます。

  • 完全性 (integrity): 署名後にデータが1ビットでも変われば、検証は失敗する
  • 認証 (authentication): その秘密鍵の持ち主が署名したと確認できる
  • 否認防止 (non-repudiation): 署名者が後から「自分は署名していない」と言い逃れできない

3番目の否認防止は、対称鍵ベースの MAC(共有鍵で作る認証コード)にはない、公開鍵署名ならではの性質です。MAC は鍵を共有する双方が同じものを作れてしまうため、「どちらが作ったか」を第三者に証明できません。

ハッシュと署名の関係

実際のデジタル署名では、元データそのものに署名するのではなく、まずハッシュ関数(SHA-256 など)でデータを固定長の短い値(ダイジェスト)に潰し、そのハッシュ値に対して署名します。

なぜハッシュを挟むのでしょうか。

  • 速度: 巨大なファイルでも、署名するのは短いハッシュ値だけで済む
  • 固定長: 入力サイズに関わらず署名の対象が一定サイズになる
  • 改ざん検知: ハッシュ関数は入力が少しでも変わると値が大きく変わる(衝突耐性)ので、改ざんを取りこぼさない

検証側は「受け取ったデータを自分でハッシュした値」と「署名を公開鍵で検証して得られる値」を突き合わせ、一致すれば「改ざんなし・正しい署名者」と判断できます。ハッシュと署名はセットで初めて意味を持つ、と覚えておくとよいです。ハッシュそのものの話、とくにパスワード保存で使う専用ハッシュについては Argon2 とパスワードハッシュ も参考になります。

証明書と認証局 - HTTPS/TLS での使われ方

公開鍵暗号には、まだ穴が一つあります。「その公開鍵が本当に相手のものか」をどう確かめるのか、という問題です。攻撃者が偽の公開鍵をあなたに掴ませれば、なりすましが成立してしまいます。

これを解くのが証明書 (X.509)認証局 (CA: Certificate Authority) です。証明書は「この公開鍵は確かにこのドメインのものだ」という情報を、信頼された第三者である CA が自分の秘密鍵で署名したものです。形式は RFC 5280 が定める X.509 v3 が広く使われます。

  • ブラウザや OS には、信頼できる CA の公開鍵(ルート証明書)があらかじめ組み込まれている
  • サーバは自分の証明書(CA が署名済み)を提示する
  • ブラウザは組み込み済みの CA 公開鍵で証明書の署名を検証し、正当ならそのサーバの公開鍵を信用する

この「信頼の連鎖(certificate chain)」をたどることで、初対面のサーバでも公開鍵の正当性を確認できます。

TLS 1.3(RFC 8446、2018年策定)での流れをおおまかに示すと次のようになります。

TLS 1.3 ハンドシェイクの役割分担(概略)
1. サーバが証明書を提示(公開鍵 + CA の署名)
2. クライアントが CA 署名を検証してサーバを認証
3. 双方が一時的な鍵で鍵共有 (エフェメラルな Diffie-Hellman)
4. 以後は共有した対称鍵 (AES など) で高速に暗号化通信

ここで重要なのは、TLS 1.3 では鍵共有に一時的な (ephemeral) 鍵を使う点です。サーバの秘密鍵は主に「本人であることを署名で証明する(認証)」ために使われ、通信の暗号化鍵そのものは毎回使い捨ての鍵合意で作られます。これにより、仮にサーバの秘密鍵が後日漏れても過去の通信は復号されない、という前方秘匿性 (forward secrecy) が得られます。公開鍵暗号が「機密性」より「認証と鍵共有」に効いている好例です。

実際に触ってみる - openssl と Node.js

概念だけでは掴みづらいので、手元で署名と検証を試してみます。まずは openssl で Ed25519 の鍵ペアを作り、ファイルに署名して検証する例です。

openssl で鍵生成・署名・検証(Ed25519)
# 秘密鍵と公開鍵のペアを生成
openssl genpkey -algorithm ed25519 -out private.pem
openssl pkey -in private.pem -pubout -out public.pem
 
# メッセージに署名(署名ファイルを出力)
echo "hello signature" > message.txt
openssl pkeyutl -sign -inkey private.pem -rawin -in message.txt -out sig.bin
 
# 公開鍵で署名を検証
openssl pkeyutl -verify -pubin -inkey public.pem -rawin -in message.txt -sigfile sig.bin
# => Signature Verified Successfully (改ざんすると Verification failure)

message.txt を1文字でも書き換えてから検証すると、Verification failure になります。これが「完全性の保証」です。次に Node.js の crypto モジュールで同じことを行う短い例です。

Node.js crypto で署名・検証(Ed25519)
import { generateKeyPairSync, sign, verify } from "node:crypto";
 
// 鍵ペアを生成
const { privateKey, publicKey } = generateKeyPairSync("ed25519");
 
const message = Buffer.from("hello signature");
 
// 秘密鍵で署名(Ed25519 はダイジェストアルゴリズム指定不要のため null)
const signature = sign(null, message, privateKey);
 
// 公開鍵で検証
console.log(verify(null, message, publicKey, signature)); // true
 
// 1バイトでも改ざんすると検証は false
message[0] ^= 0x01;
console.log(verify(null, message, publicKey, signature)); // false

秘密鍵で作った署名を、公開鍵だけで検証できている点がポイントです。

WARNING

暗号は「自前で実装しない」が鉄則です。パディング (RSA の PKCS#1 v1.5 など) や乱数の扱いを独自に組むと、理論上は正しくても実装上の穴で簡単に破られます。必ず openssl や標準ライブラリなど、検証された実装を使ってください。

身近な使われどころ

公開鍵暗号とデジタル署名は、意識しないだけで日常の至るところで動いています。

  • HTTPS / TLS: サーバ証明書の検証と鍵共有。ブラウザの鍵マークの正体
  • パスキー / WebAuthn: 端末内の秘密鍵で署名し、サーバは公開鍵で検証する。パスワードレス認証の中核。詳しくは パスキー / WebAuthn 入門、端末間の移行は FIDO Credential Exchange
  • JWT の署名: RS256 / ES256 は公開鍵署名。発行者が秘密鍵で署名し、検証側は公開鍵で確かめる。詳しくは JWT の仕組みと正しい使い方
  • OAuth 2.0 / OpenID Connect: ID トークンの署名検証に公開鍵を使う。OAuth 2.0 / OIDC 認可フロー入門
  • Git のコミット署名: GPG や SSH 鍵でコミット・タグに署名し、作者を証明する
  • SSH ログイン: 公開鍵をサーバに登録し、秘密鍵で認証する定番の仕組み
  • ソフトウェア署名: OS やパッケージが「正規の配布元が作ったもの」であることを署名で保証する

「秘密鍵で署名して公開鍵で検証」「公開鍵で暗号化して秘密鍵で復号」という2つの型さえ押さえておけば、どの場面もこの応用として読み解けます。

耐量子暗号 (PQC) への展望

最後に、将来の話に軽く触れます。RSA も楕円曲線暗号も、その安全性は「素因数分解」や「離散対数問題」が現実的に解けないことに支えられています。ところが十分な規模の量子コンピュータが実現すると、これらの問題は効率的に解けてしまう可能性が指摘されています。つまり現行の公開鍵暗号は、将来的に破られうるということです(対称鍵の AES やハッシュへの影響は相対的に小さいとされます)。

そこで進んでいるのが耐量子暗号 (PQC: Post-Quantum Cryptography) の標準化です。NIST は 2024年8月に最初の標準を公表しました。名称と規格番号は次のとおりです(公表内容を確認済み)。

規格アルゴリズム用途
FIPS 203ML-KEM鍵カプセル化(鍵共有)。RSA/ECDH の置き換え
FIPS 204ML-DSAデジタル署名。ECDSA/RSA 署名の置き換え
FIPS 205SLH-DSAハッシュベースの署名(保守的な代替)

これらは格子問題やハッシュの困難性に基づき、量子計算でも解きにくいと考えられています。現実の移行は、既存方式と新方式を併用するハイブリッド構成から徐々に進むとみられます。今すぐ全面的に切り替える必要はありませんが、「いま暗号化した通信を保存しておき、将来の量子計算で復号する」という攻撃(harvest now, decrypt later)が懸念されているため、長期に秘匿したいデータを扱う分野では動きが始まっています。

まとめ

要点を整理します。

  • 対称鍵 vs 公開鍵: 対称鍵は高速だが鍵配送問題を抱える。公開鍵暗号は鍵ペア(公開鍵と秘密鍵)と一方向性で、その問題を解いた
  • RSA と楕円曲線: RSA は素因数分解、ECC は離散対数の困難性が根拠。ECC は短い鍵で同等の強度を得られ、Ed25519 (RFC 8032) が現代的な定番。鍵長は RSA 2048 ビット以上、P-256 は 128 ビット相当が目安
  • デジタル署名: 秘密鍵で署名し公開鍵で検証する。完全性・認証・否認防止を保証し、目的は暗号化とは別
  • ハッシュと署名: 実際はデータをハッシュ (SHA-256 等) してから署名する。セットで機能する
  • 証明書と CA: X.509 証明書 (RFC 5280) を CA が署名し、信頼の連鎖で公開鍵の正当性を担保。TLS 1.3 (RFC 8446) は認証に署名、鍵共有に一時鍵を使い前方秘匿性を得る
  • 身近な用途: HTTPS・パスキー・JWT 署名・OIDC・Git 署名・SSH など
  • 耐量子: 量子計算は現行方式を脅かす。NIST が 2024年に PQC 標準 (FIPS 203/204/205) を公表し、移行が始まりつつある

「鍵を公開しても安全」という一見の逆説は、一方向性という数学的な非対称性に支えられています。この2つの型——暗号化と署名——を押さえておけば、Web セキュリティの多くの仕組みが同じ原理の応用として見えてきます。

参考リンク

パスキーとパスワードの移行 - FIDO Credential Exchange (CXP/CXF) 入門

パスキーとパスワードの移行 - FIDO Credential Exchange (CXP/CXF) 入門

12

パスキーやパスワードを管理ツール間で安全に移行するための標準、FIDO Alliance の Credential Exchange Protocol (CXP) と Credential Exchange Format (CXF) を整理します。CXF=JSON ベースのデータ形式、CXP=HPKE による暗号化転送という役割分担、ドラフトという現在のステータス、そして 2026 年 6 月の Google Play services 26.21 による Android 対応や iOS/macOS 26 の対応まで、一次ソースを軸にまとめます。

DNS の仕組み入門 - 名前解決・レコード種別・TTL とキャッシュを理解する

DNS の仕組み入門 - 名前解決・レコード種別・TTL とキャッシュを理解する

23

DNS(ドメインネームシステム)の名前解決の仕組みを、一次ソースである RFC を軸に整理します。ルート・TLD・権威サーバーの階層、再帰リゾルバとスタブリゾルバ、A/AAAA/CNAME/MX/NS/SOA/TXT/CAA/SRV/PTR などのレコード種別、再帰と反復の解決の流れ、TTL とキャッシュ、ネガティブキャッシュ(RFC 2308)、ポート53と UDP/TCP・EDNS0(RFC 6891)、DNSSEC(RFC 4033)、DoH(RFC 8484)/DoT(RFC 7858)まで、Web 開発者・インフラ担当者向けにまとめます。