0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

暗号アジリティとは何か:暗号方式を差し替えられる設計を考える

0
Posted at

概要

スマホ決済、ネットショッピング、SNSログイン、クラウドサービス。
普段使っているサービスの裏側では、通信内容を盗み見られないようにしたり、途中で書き換えられていないか確認したり、本物の相手につながっているかを確かめたりするために、さまざまな暗号技術が使われています。

ただし、暗号技術は「一度選んだら、ずっとそのまま使えるもの」ではありません。

たとえば、スマホアプリやOSは、脆弱性の修正や新しい機能への対応のためにアップデートされます。
それと同じように、暗号方式や鍵長も、計算機の性能向上、暗号解析の進展、標準仕様の変更、量子コンピュータへの備えなどによって、将来的に見直しが必要になることがあります。

ここで問題になるのが、暗号方式を変えたいと思ったときに、システム側がその変更に耐えられるかどうかです。

もしアプリのあちこちに特定の暗号方式が直接書き込まれていたり、古い通信方式を前提にした機器が大量に残っていたり、どこで何の暗号を使っているか分からなかったりすると、暗号方式の変更は簡単ではありません。

このような問題に備える考え方が、暗号アジリティです。

暗号アジリティとは、ざっくり言うと、暗号方式・鍵長・ライブラリ・設定・運用方針を、必要になったときに安全に差し替えられるようにしておく考え方です。

少し言い換えると、「今どの暗号を使うか」だけでなく、「将来変える必要が出たときに、慌てず安全に変えられるか」まで考える姿勢です。

本記事では、暗号アジリティについて、次の流れで整理します。

  1. 身近なサービスの裏側で暗号が使われていること
  2. 暗号方式を固定してしまうと何が困るのか
  3. 「強い暗号を選べば安全」と言い切れない理由
  4. 暗号方式を差し替えにくいシステムで起きる問題
  5. 暗号アジリティを支える設計要素
  6. TLSや耐量子暗号への移行を例にした具体的な考え方
  7. 暗号アジリティだけでは安全にならない理由と注意点

本記事では、暗号アジリティをいきなり標準仕様の言葉から説明するのではなく、身近なサービスを入口に整理します。
そのうえで、TLS、暗号ライブラリ、暗号インベントリ、耐量子暗号への移行などを例にしながら、「暗号方式を差し替えられる設計」とは何かを考えていきます。

💡 豆知識
暗号アジリティは、暗号の世界における「引っ越ししやすい設計」と考えると分かりやすいです。
家具が壁に完全に埋め込まれている家では引っ越しが大変ですが、動かしやすい家具や分かりやすい配線になっていれば、必要なときに移動しやすくなります。
暗号方式も同じで、システムの奥深くに固定されているほど、後から変更するのが難しくなります。

この記事で分かること

この記事では、次の内容を整理します。

  • 暗号アジリティの基本的な意味
  • 暗号方式が「一度決めたら終わり」ではない理由
  • 暗号方式を差し替えにくいシステムで起こる問題
  • TLSを例にした暗号方式の選択・交渉の考え方
  • 暗号ライブラリ、設定分離、鍵管理、暗号インベントリの重要性
  • 耐量子暗号への移行と暗号アジリティの関係
  • 暗号アジリティを考えるときの注意点

対象読者

この記事は、次のような人を想定しています。

  • 情報セキュリティを学び始めた人
  • 暗号技術の全体像は少し分かってきたが、運用や移行の話はまだ曖昧な人
  • TLS、証明書、鍵管理、耐量子暗号などの話題をつなげて理解したい人
  • 「暗号方式を変更できる設計」がなぜ大事なのか知りたい人
  • セキュリティを意識したアプリケーション設計に関心がある人

本記事で扱わないこと

本記事は、暗号アジリティの考え方を整理することを目的にしています。
そのため、次の内容は深く扱いません。

  • AES、RSA、楕円曲線暗号、ML-KEMなどの内部アルゴリズム
  • 暗号アルゴリズムの数式や安全性証明
  • 暗号アルゴリズムそのものを実装するコード
  • 実システムにおける詳細な鍵管理ポリシー
  • 特定クラウドや特定製品での設定手順
  • 法的・契約上の暗号要件の詳細
  • 企業や組織ごとの移行計画の具体的な策定

ただし、暗号アジリティは、プロトコル、ライブラリ、設定、鍵管理、証明書、運用手順などにまたがる考え方です。
そのため、本文ではTLSや耐量子暗号移行などを例にしながら、「どこを固定しすぎると変更しにくくなるのか」を中心に説明します。

また、本記事に出てくるコードは、考え方を理解するための学習用サンプルです。
実際のサービスでは、利用しているフレームワーク、暗号ライブラリ、クラウドサービス、組織のセキュリティ基準、公式ドキュメントを確認し、独自判断で暗号設定を変更しないようにしてください。

コードを試す場合の前提

本記事では、「暗号方式をコードの中に固定しすぎない」「方式名や鍵IDをメタデータとして残す」といった考え方を説明するために、Pythonの短いコードをいくつか使います。
コードを手元で試す場合は、次の前提で読んでください。

項目 内容
目的 暗号アジリティの設計イメージを理解するための学習用
想定環境 Python 3.x系
主に使う標準ライブラリ hashlibhmacsslsocketjson など
外部ライブラリ 基本的には使用しない
注意点 本番環境の暗号設定や鍵管理へそのまま流用しない

特に、暗号アルゴリズムそのものを自作することは目的にしていません。
本番環境では、検証済みの暗号ライブラリ、標準仕様、ベンダーのサポート状況、組織のセキュリティ基準に従ってください。

全体の流れ

この記事では、次の順番で話を進めます。

1. 身近なサービスの裏側で暗号は変わり続けている

まずは、少し身近な場面から考えてみます。

ネットショッピングで住所や支払い情報を入力するとき、ブラウザには https:// から始まるURLや鍵マークが表示されます。
スマホ決済を使うときも、アプリとサーバーの間では、支払いに関する情報が安全にやり取りされています。
クラウドサービスにファイルを保存するときも、ログイン情報や通信内容がそのまま外から見える状態にならないように守られています。

普段はあまり意識しませんが、こうしたサービスの裏側では、暗号技術が「安全確認のための部品」として使われています。

ここで大切なのは、暗号技術は一度入れたら終わりの部品ではない、という点です。
スマホアプリやOSがアップデートされるように、暗号方式や設定も、時代に合わせて見直されることがあります。

1.1 HTTPSの裏側では、複数の暗号技術が組み合わされている

たとえば、Webサイトにアクセスするときによく使われるHTTPSでは、TLSという仕組みによって通信を保護します。
TLSでは、通信内容を暗号化するだけでなく、接続先サーバーが本物か確認したり、通信内容が途中で改ざんされていないか確認したりします。

ざっくり図にすると、次のようなイメージです。

ここでは、共通鍵暗号、公開鍵暗号、デジタル署名、鍵共有、証明書など、複数の暗号技術が役割分担しています。
つまり、HTTPSの安全性は「何か1つの暗号方式」だけで成り立っているわけではありません。

この点は、派生元の記事でも整理した通りです。
暗号技術は、単体の道具というより、実サービスの中で組み合わせて使われる道具箱のようなものです。

1.2 サービスが更新されるように、暗号方式も見直される

スマホアプリを長く使っていると、機能追加や不具合修正のためにアップデートが入ります。
OSやブラウザも、脆弱性への対応や新しい標準への対応のために更新されます。

暗号技術も、これに少し似ています。

ある時点で十分安全だと考えられていた暗号方式でも、計算機の性能向上、暗号解析技術の進展、標準仕様の変更によって、後から見直しが必要になることがあります。

IETFのRFC 7696では、暗号アルゴリズムは計算能力や暗号解析技術の進展によって、いつかは時代遅れになると想定してプロトコルを設計する必要がある、と説明されています。
参考: RFC 7696 - Guidelines for Cryptographic Algorithm Agility and Selecting Mandatory-to-Implement Algorithms

💡 豆知識
暗号の世界では、「今安全」と「将来もずっと安全」は同じ意味ではありません。
暗号方式は、その時点の攻撃手法や計算能力を前提に評価されます。
そのため、時間が経つと「そろそろ別の方式へ移行しよう」という判断が必要になることがあります。

1.3 「古い暗号方式を使い続けない」ことも大切

暗号技術の更新というと、「新しい方式を追加すること」をイメージしやすいかもしれません。
しかし実際には、古い方式を安全にやめることも同じくらい重要です。

分かりやすい例が、TLS 1.0 / TLS 1.1 の非推奨化です。

RFC 8996では、TLS 1.0とTLS 1.1が正式に非推奨化され、Historic statusに移されたことが説明されています。
理由として、現在推奨される暗号アルゴリズムや仕組みを十分にサポートしていないこと、古いバージョンのサポートを削除することで攻撃面・設定ミスの可能性・ライブラリや製品の保守負荷を減らせることが挙げられています。
参考: RFC 8996 - Deprecating TLS 1.0 and TLS 1.1

この例から分かるのは、暗号技術の安全性は「新しいものを入れる」だけでは保てないということです。
古い方式が互換性のために残り続けると、設定ミス、古い通信への誘導、保守の複雑化につながる可能性があります。

起きていること 暗号アジリティにつながる学び
TLS 1.0 / TLS 1.1 の非推奨化 古いTLSバージョンが推奨されなくなった 古い方式を安全にやめる仕組みが必要
暗号アルゴリズム・鍵長の移行 より強い鍵や堅牢な方式への移行が必要になる 将来の変更を前提にしておく必要がある
耐量子暗号への移行準備 量子コンピュータ時代を見据えた公開鍵暗号の見直しが進む システム全体で暗号方式を入れ替える準備が必要

NIST SP 800-131A Rev.2でも、暗号の利用について、アルゴリズムの破壊やより強力な計算技術の登場に備え、より強い鍵やより堅牢なアルゴリズムへ移行するためのガイダンスが示されています。
参考: NIST SP 800-131A Rev.2 - Transitioning the Use of Cryptographic Algorithms and Key Lengths

1.4 暗号方式の変更は、コードを1行変えれば終わりではない

ここで、少しだけ実務寄りの視点に移ります。

「古い暗号方式をやめて、新しい方式を使えばよい」と聞くと、アプリケーションの設定を1つ変えれば終わるように感じるかもしれません。
しかし、実際のシステムではそう簡単ではないことが多いです。

たとえば、暗号方式を変更しようとすると、次のような場所に影響が出る可能性があります。

影響する場所 具体例
アプリケーション 暗号ライブラリの呼び出し方、設定ファイル、エラーハンドリング
サーバー設定 TLSのバージョン、暗号スイート、証明書、秘密鍵
利用者環境 古いブラウザ、古いスマホ、古いOS
外部連携先 取引先API、決済サービス、認証基盤、クラウドサービス
運用 監視、ログ、証明書更新、障害時の切り戻し手順
ハードウェア HSM、組込み機器、ファームウェア、専用チップ

特に、企業システムや長く運用されているサービスでは、「古い端末もまだ使われている」「取引先が新しい方式に対応していない」「組込み機器の更新が難しい」といった事情があります。

そのため、暗号方式の変更は、単なる実装変更ではなく、システム全体の設計・運用・移行計画に関わる作業になります。

NIST CSWP 39upd1では、暗号アジリティを、プロトコル、アプリケーション、ソフトウェア、ハードウェア、ファームウェア、インフラにおいて、セキュリティと継続運用を保ちながら暗号アルゴリズムを置き換え・適応できる能力として説明しています。
参考: NIST CSWP 39upd1 - Considerations for Achieving Crypto Agility

この説明からも、暗号アジリティは「暗号アルゴリズムを知っているか」だけの話ではないことが分かります。
アプリケーション、通信プロトコル、ライブラリ、サーバー、ハードウェア、運用体制まで含めて、変更に対応できる状態を作ることが重要になります。

1.5 暗号の更新は「引っ越し」に近い

暗号方式の変更は、家の引っ越しに少し似ています。

荷物が少なく、どこに何があるか分かっていて、家具も動かしやすければ、引っ越しは比較的スムーズです。
一方で、荷物の場所が分からず、家具が壁に埋め込まれていて、配線も複雑に絡まっていると、引っ越しは大変になります。

暗号方式も同じです。

どこでどの暗号方式を使っているか分からない。
特定の方式がアプリの中に直接書き込まれている。
古い端末や外部サービスとの互換性のために、弱い方式を残し続けている。

このような状態では、いざ暗号方式を変更しようとしても、影響範囲が分からず、作業が大きくなってしまいます。

この「引っ越ししやすい状態」を、暗号の世界で考えるのが暗号アジリティです。

1.6 この章のまとめ

この章では、スマホ決済、HTTPS、クラウドサービスのような身近な例から、暗号技術がサービスの裏側で使われていることを見ました。

ポイントは、暗号方式は一度選んだら終わりではないということです。

計算機の性能向上、暗号解析技術の進展、標準仕様の変更、古いプロトコルの非推奨化によって、暗号方式や鍵長は見直しが必要になることがあります。
そして、その変更はアプリケーションだけでなく、サーバー設定、証明書、ライブラリ、外部連携先、古い端末、ハードウェア、運用手順にまで影響する場合があります。

だからこそ、暗号方式を「後から安全に差し替えられる状態」にしておくことが重要になります。

次の章では、もう少し踏み込んで、なぜ暗号方式は「一度決めたら終わり」ではないのかを、計算機の性能向上、暗号解析、標準化、量子コンピュータへの備えという観点から整理していきます。


2. 暗号方式を固定してはいけない理由

前章では、HTTPSやスマホ決済のような身近なサービスの裏側で、暗号技術が使われていることを見ました。
ここからは、もう少し踏み込んで「なぜ暗号方式を固定しすぎると危ないのか」を整理していきます。

結論からいうと、暗号方式は次のような理由で見直しが必要になります。

理由 何が起きるか 具体例
計算機の性能が上がる 以前は現実的でなかった総当たり攻撃や解析が、少しずつ現実に近づく 鍵長の見直し
暗号解析が進む アルゴリズムの弱点が新しく見つかる SHA-1の衝突耐性に関する懸念
標準仕様が更新される 推奨される方式や設定が変わる TLS 1.0 / 1.1 の非推奨化
利用環境が変わる ブラウザ、OS、ライブラリ、クラウド、取引先APIの対応状況が変わる 古い端末や外部連携先との互換性
新しい脅威に備える必要がある まだ実用化前でも、長期的なリスクを見込んで準備が必要になる 耐量子暗号への移行準備

IETFのRFC 7696では、暗号アルゴリズムは計算能力や暗号解析技術の進展によって、いずれ時代遅れになると考えて設計する必要があると説明されています。
参考: RFC 7696 - Guidelines for Cryptographic Algorithm Agility and Selecting Mandatory-to-Implement Algorithms

また、NIST SP 800-131A Rev.2でも、アルゴリズムの破壊やより強力な計算技術の登場に備え、より強い鍵やより堅牢なアルゴリズムへ移行するためのガイダンスが示されています。
参考: NIST SP 800-131A Rev.2 - Transitioning the Use of Cryptographic Algorithms and Key Lengths

2.1 暗号の安全性は「時間」とセットで考える

暗号方式の安全性は、単に「この方式は安全」「この方式は危険」とだけ決まるものではありません。
多くの場合、次のような条件とセットで評価されます。

  • 現在知られている攻撃手法では破るのが難しいか
  • 現在の計算能力では現実的な時間で攻撃できないか
  • 想定する利用期間中、守りたい情報を保護できるか
  • 標準化団体や利用している業界で推奨されているか

たとえば、ある情報を「今日だけ守れればよい」のか、「10年後も守る必要がある」のかで、求められる安全性は変わります。

医療情報、金融情報、企業の機密情報、研究データのように長期間守る必要がある情報では、今すぐ解読されないだけでは不十分な場合があります。
将来の計算能力や暗号解析の進展も見込んで、余裕のある方式を選ぶ必要があります。

💡 豆知識
暗号の安全性は、「絶対に破れない」ではなく、「現実的な時間やコストでは破るのが難しい」という考え方で語られることが多いです。
そのため、計算機が速くなったり、より良い攻撃方法が見つかったりすると、以前の安全性評価を見直す必要が出てきます。

2.2 暗号解析が進むと、評価が変わることがある

暗号方式は、公開されたあとも研究者や技術者によって継続的に分析されます。
これは「公開されているから危ない」という意味ではなく、むしろ多くの人が検証することで安全性への理解が深まる、という側面があります。

ただし、その過程で弱点が見つかることもあります。

分かりやすい例が、ハッシュ関数のSHA-1です。
NISTは2022年に、SHA-1の現在の限定的な利用から移行する計画を示し、暗号的保護を目的とする全アプリケーションでのSHA-1利用から2030年12月31日までに移行すると説明しています。
背景として、SHA-1の衝突耐性に対する深刻な暗号解析上の攻撃が2005年に発表され、その後も攻撃がより深刻になってきたことが挙げられています。
参考: NIST - Transitioning Away from SHA-1 for All Applications

ここで出てきた「衝突耐性」とは、異なる2つの入力から同じハッシュ値を作ることが難しい、という性質です。

たとえば、ファイルAとファイルBがまったく違う内容なのに、同じハッシュ値になる組み合わせを攻撃者が作れると、デジタル署名や改ざん検知の前提が崩れる可能性があります。

もちろん、ハッシュ関数の用途によって必要な性質は変わります。
そのため、「SHA-1という文字を見たら即すべて危険」と単純に言い切るのではなく、どの用途で、何を守るために、どのように使っているかを確認する必要があります。

ただ、重要なのは、かつて広く使われていた方式でも、研究の進展によって推奨が変わることがある、という点です。

2.3 標準仕様や推奨設定も変わる

暗号方式そのものだけでなく、暗号を使うためのプロトコルや設定も更新されます。

たとえばTLSでは、TLS 1.0 / TLS 1.1 がRFC 8996によって非推奨化されています。
また、TLS 1.3の仕様であるRFC 8446では、古い署名アルゴリズムやSHA-1の扱いについて制限が明記されています。
参考: RFC 8996 - Deprecating TLS 1.0 and TLS 1.1
参考: RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3

ここで大事なのは、暗号技術の安全性が「アルゴリズム単体」だけで決まるわけではないことです。

同じアルゴリズム名が出てきても、次のような違いによって意味が変わります。

見るべき観点
どのプロトコルで使うのか TLS、SSH、IPsec、JWT、証明書など
どの用途で使うのか 署名、鍵共有、暗号化、ハッシュ、MACなど
どの鍵長・パラメータで使うのか RSAの鍵長、楕円曲線の種類、ハッシュ長など
どの実装で使うのか 暗号ライブラリ、OS、ブラウザ、HSM、組込み機器など
いつまで守る必要があるのか 一時的な通信か、長期保存データか

そのため、暗号方式を考えるときは、「アルゴリズム名だけを見て判断しない」ことが大切です。

💡 豆知識
TLS 1.3では、以前のTLSとは暗号スイートの意味が少し変わっています。
RFC 8446では、TLS 1.3の暗号スイートは主に対称暗号とHKDFで使うハッシュを指定し、TLS 1.2以前の暗号スイートとは互換ではないと説明されています。
つまり、同じ「TLSの暗号スイート」という言葉でも、バージョンによって中身の考え方が変わることがあります。

2.4 古い方式は「互換性」のために残りやすい

暗号方式の移行で難しいのは、技術的に新しい方式があるとしても、すぐに全員が移行できるとは限らないことです。

たとえば、あるWebサービスが新しいTLS設定に移行したいとします。
しかし、利用者の中に古いOSや古いブラウザを使っている人がいるかもしれません。
外部APIの連携先が、まだ新しい方式に対応していないかもしれません。
組込み機器や社内システムのように、簡単にアップデートできない環境が残っているかもしれません。

その結果、「本当は古い方式をやめたいが、互換性のために残している」という状態が起こります。

RFC 7696でも、アルゴリズム識別子を用意するだけでは移行は十分ではなく、実装を保守する人やサービスを運用する人が、設定変更、展開、古い方式の非推奨化・無効化を進める必要があると説明されています。
参考: RFC 7696 - Guidelines for Cryptographic Algorithm Agility and Selecting Mandatory-to-Implement Algorithms

この点から、暗号アジリティは「新しい方式に対応できること」だけではありません。
古い方式を安全にやめられること も、同じくらい重要です。

2.5 コード例:暗号方式を直書きすると、あとから変えにくい

ここで、少しだけコードで考えてみます。

以下は、ファイルの内容からハッシュ値を計算する、とても単純な例です。
説明用の例であり、パスワード保存には使わないでください。
パスワード保存では、通常のハッシュ関数ではなく、Argon2、bcrypt、scrypt、PBKDF2など、パスワード保存向けの方式を検討する必要があります。

まずは、移行しにくい例です。

import hashlib


def calc_file_digest_v1(data: bytes) -> str:
    """ファイル内容のハッシュ値を計算する例。"""
    # 移行しにくい例:
    # 使うハッシュ関数がコード内で SHA-1 に固定されている。
    # 後から別方式へ変える場合、この関数を呼び出す前提や保存済みデータの扱いを確認する必要がある。
    return hashlib.sha1(data).hexdigest()

この例では、hashlib.sha1() が関数の中に直接書かれています。
小さなプログラムなら変更は簡単ですが、大きなシステムのあちこちに同じようなコードが散らばっていると、移行時に次の問題が起きやすくなります。

  • どこでSHA-1を使っているか探す必要がある
  • 変更漏れが起きる可能性がある
  • 保存済みのハッシュ値との互換性をどう扱うか考える必要がある
  • テスト範囲が広がる

次に、少しだけ移行しやすくした例です。

import hashlib
from typing import Final

# 利用を許可するハッシュ関数を1か所に集める。
# 実務では、組織のセキュリティポリシー、標準仕様、ライブラリのサポート状況に合わせて管理する。
ALLOWED_HASH_ALGORITHMS: Final[set[str]] = {"sha256", "sha384", "sha512"}


def calc_file_digest(data: bytes, algorithm: str = "sha256") -> str:
    """指定されたハッシュ関数でファイル内容のハッシュ値を計算する例。"""
    # 入力されたアルゴリズム名を小文字にそろえる。
    # 例: "SHA256" と "sha256" を同じ指定として扱いやすくするため。
    normalized_algorithm = algorithm.lower()

    # 許可していないアルゴリズムが指定された場合は、明示的にエラーにする。
    # これにより、古い方式や想定外の方式が何となく使われることを避ける。
    if normalized_algorithm not in ALLOWED_HASH_ALGORITHMS:
        raise ValueError(f"許可されていないハッシュ関数です: {algorithm}")

    # hashlib.new() を使うと、アルゴリズム名を変数として扱える。
    # ただし、何でも受け入れるのではなく、上の許可リストで制御することが重要。
    digest = hashlib.new(normalized_algorithm)

    # ハッシュ値を計算したいデータを入力する。
    digest.update(data)

    # 16進文字列としてハッシュ値を返す。
    return digest.hexdigest()

この例では、利用できるハッシュ関数を ALLOWED_HASH_ALGORITHMS に集めています。
そのため、将来ポリシーを変更したい場合、少なくとも「許可する方式をどこで管理しているか」が分かりやすくなります。

もちろん、これだけで暗号アジリティが完成するわけではありません。
実際には、保存済みデータの移行、既存データの再計算、外部システムとの互換性、監査ログ、テストなども必要です。

それでも、暗号方式をコードのあちこちに直接書くよりは、変更箇所を集約できるため、移行時の見通しが良くなります。

補足
Pythonのhashlibは、ハッシュ関数を扱う標準ライブラリです。
ここでは「暗号方式を直書きする場合」と「設定やポリシーで切り替えやすくする場合」の違いを説明するために使っています。
参考: Python Documentation - hashlib

2.6 コード例:TLSの最低バージョンを設定として扱う

もう1つ、TLS設定の例も見てみます。

Pythonのsslモジュールでは、SSLContextを使ってTLS接続に関する設定をまとめて扱えます。
これは、アプリケーションのあちこちでTLS設定を個別に書くよりも、共通の設定を作って使い回しやすい形です。
参考: Python Documentation - ssl

import ssl


def create_tls_client_context() -> ssl.SSLContext:
    """HTTPSクライアント用のTLS設定を作成する例。"""
    # Pythonが用意するデフォルト設定を使って、証明書検証などを有効にしたコンテキストを作る。
    # 実務では、利用するPython/OpenSSLのバージョンや組織のセキュリティ基準も確認する。
    context = ssl.create_default_context()

    # TLS 1.0 / TLS 1.1 はRFC 8996で非推奨化されている。
    # ここでは例として、最低バージョンをTLS 1.2に設定する。
    # 可能であればTLS 1.3も利用されるが、相手側の対応状況に依存する。
    context.minimum_version = ssl.TLSVersion.TLSv1_2

    # create_default_context()では、通常、証明書検証とホスト名検証が有効になる。
    # 検証を無効化すると中間者攻撃のリスクが高まるため、安易に無効化しない。
    return context

このコードのポイントは、「TLSの最低バージョン」というセキュリティ上重要な設定を、共通の関数に集めていることです。

もちろん、実システムではこれだけでは不十分です。
サーバー側の設定、ロードバランサー、リバースプロキシ、証明書、監視、外部連携先の対応状況なども確認する必要があります。

ただ、考え方としては、次のように整理できます。

移行しにくい状態 移行しやすい方向性
TLS設定がアプリごとにばらばら 共通の設定関数や設定ファイルに集める
古いバージョンを許可する理由が分からない 例外的に許可する場合は理由と期限を記録する
設定変更時の影響範囲が分からない どのサービスがどの設定を使うか把握する
証明書検証を一時対応で無効化したまま残る 検証無効化を禁止し、レビュー対象にする

2.7 「今の正解」を固定しすぎない

ここまで見ると、「では、今いちばん強い方式を選べばよいのでは?」と思うかもしれません。

もちろん、現時点で推奨される方式を選ぶことは大切です。
しかし、暗号アジリティの考え方では、それだけでは不十分です。

なぜなら、今日の推奨設定も、将来は変わる可能性があるからです。

暗号技術では、「今の正解を選ぶこと」と「将来の正解に移れること」の両方が重要です。

たとえば、次のような設計は将来の変更に弱くなりがちです。

  • アルゴリズム名をアプリケーションコードに直接埋め込む
  • 鍵長や証明書の前提を仕様書に書かず、暗黙の前提にする
  • 古い方式を残す理由や期限を記録しない
  • どのサービスがどの暗号設定を使っているか管理しない
  • 外部連携先や利用者環境の対応状況を確認しない

反対に、変更に強い設計では、次のような考え方を持ちます。

  • 暗号方式や鍵長を設定・ポリシーとして管理する
  • 暗号ライブラリやAPIを通して、実装を直接抱え込まない
  • 古い方式を無効化する手順を用意する
  • 利用状況を測定し、移行状況を確認できるようにする
  • 新しい方式を追加するだけでなく、古い方式を安全に削除する

RFC 7696でも、実装は新しいアルゴリズムやアルゴリズムスイートを挿入しやすいようにモジュール化されていることが望ましいと説明されています。
つまり、暗号アジリティは、暗号そのものの知識だけでなく、ソフトウェア設計や運用設計とも強く関係します。

2.8 この章のまとめ

この章では、暗号方式が「一度決めたら終わり」ではない理由を整理しました。

暗号方式は、計算機の性能向上、暗号解析の進展、標準仕様の更新、利用環境の変化、新しい脅威への備えによって、将来的に見直しが必要になります。

また、SHA-1やTLS 1.0 / TLS 1.1のように、かつて広く使われていたものでも、時間が経つにつれて推奨されなくなる例があります。

そして、実装面では、暗号方式をコードに直接書き込むほど、後から変更しにくくなります。
逆に、設定やポリシー、共通関数、ライブラリに責務を集めておくと、将来の移行を考えやすくなります。

次の章では、「強い暗号方式を選ぶだけで十分なのか」という視点から、今回のテーマである 暗号アジリティ そのものを整理していきます。

3. 「強い暗号方式を選べば安全」は半分正しい

前章では、暗号方式を固定しすぎると、将来の見直しや移行が難しくなることを見てきました。
ここまで読むと、次のように考えたくなるかもしれません。

それなら、最初から強い暗号方式を選んでおけばよいのでは?

この考え方は、半分正しいです。
現時点で推奨される方式を選ぶことはもちろん大切です。

しかし、暗号技術では「今の正解」が将来もずっと正解とは限りません。
そのため、強い方式を選ぶことに加えて、必要になったときに安全に差し替えられる状態を作っておく必要があります。

ここから、今回の記事の中心である 暗号アジリティ に入ります.

いきなり定義だけを見ると少し固く感じるかもしれません。
まずは、身近な言い方にすると、暗号アジリティとは次のような考え方です。

暗号アジリティとは、暗号方式を「今これに決める」だけでなく、
将来必要になったときに、安全に差し替えられるようにしておく設計・運用の考え方です。

もう少しくだけて言うと、暗号技術をシステムの奥深くに固定してしまうのではなく、必要に応じて交換できる部品として扱えるようにしておく、というイメージです。

3.1 NISTの定義をかみ砕く

NIST CSWP 39upd1では、暗号アジリティを、プロトコル、アプリケーション、ソフトウェア、ハードウェア、ファームウェア、インフラにおいて、セキュリティと継続運用を保ちながら暗号アルゴリズムを置き換え・適応できる能力として説明しています。
参考: NIST CSWP 39upd1 - Considerations for Achieving Crypto Agility

この説明を、初学者向けに分解すると次のようになります。

NISTの説明に出てくる要素 かみ砕いた意味
プロトコル TLSやSSHのように、通信の手順を決める仕組み
アプリケーション Webアプリ、スマホアプリ、社内システムなど
ソフトウェア 暗号ライブラリ、ミドルウェア、OSなど
ハードウェア・ファームウェア HSM、TPM、組込み機器、専用チップなど
インフラ サーバー、ロードバランサー、クラウド、ネットワーク機器など
セキュリティと継続運用を保つ 安全性を落とさず、サービス停止や大きな混乱を避けながら移行すること

ここで重要なのは、暗号アジリティが 暗号アルゴリズム単体の話ではない という点です。

たとえば、「SHA-1をSHA-256に変えたい」「RSAから別の方式へ移行したい」と思ったとします。
このとき、変更する対象はコードだけとは限りません。

証明書、鍵、設定ファイル、データベースに保存された値、API仕様、外部連携先、監視ルール、障害時の切り戻し手順など、さまざまな場所に影響が出る可能性があります。

暗号アジリティは、そのような変更をできるだけ安全に進められるように、あらかじめ設計・実装・運用を整えておく考え方です。

💡 豆知識
「アジリティ」は英語の agility で、素早さ、身軽さ、機敏さといった意味があります。
ソフトウェア開発の「アジャイル」と語源が近く、変化に対応しやすい状態を表す言葉です。
暗号アジリティも、「暗号を頻繁に変える」という意味ではなく、「変えなければならないときに動ける状態にしておく」という意味で理解すると自然です。

3.2 何を差し替えられるようにするのか

暗号アジリティという言葉を聞くと、「暗号アルゴリズムだけを切り替えられればよい」と思うかもしれません。
しかし、実際に差し替え対象になるものはもう少し広いです。

差し替え対象 なぜ変更が必要になるか
暗号アルゴリズム SHA-1からSHA-256、RSAから別方式など 暗号解析や標準変更により推奨が変わるため
鍵長・パラメータ RSA 2048 bit、楕円曲線の種類、ハッシュ長など 計算能力や安全性要件に合わせるため
プロトコルバージョン TLS 1.0 / 1.1からTLS 1.2 / 1.3へ 古いプロトコルが非推奨化されるため
暗号ライブラリ OpenSSL、BoringSSL、libsodium、各言語の標準ライブラリなど 脆弱性修正や新方式対応のため
証明書・鍵管理 サーバー証明書、秘密鍵、HSM、KMSなど 有効期限、鍵更新、方式変更に対応するため
設定・ポリシー 許可する暗号スイート、禁止する方式、例外設定など 古い方式を段階的に止めるため
監視・棚卸し どのサービスが何を使っているかの一覧 移行対象と進捗を把握するため

たとえば、WebサーバーでTLSの設定を変えるだけなら簡単そうに見えます。
しかし、実際にはロードバランサー、APIゲートウェイ、証明書発行基盤、古いクライアント、外部サービスとの接続まで確認が必要になることがあります。

つまり暗号アジリティでは、次の2つをセットで考える必要があります。

  1. 技術的に切り替えられること
    新しい方式に対応できる実装・設定・プロトコルになっていること。

  2. 運用上も切り替えられること
    影響範囲を把握し、段階的に展開し、問題が起きたときに検知・対応できること。

この2つのどちらかが欠けると、「仕様上はできるが、実際には移行できない」という状態になりやすくなります。

3.3 プロトコル・実装・運用の3つの視点で考える

暗号アジリティは、1つの層だけを見ても十分に理解できません。
この記事では、分かりやすくするために、次の3つの視点で整理します。

プロトコルの視点

プロトコルの視点では、「通信する相手と、どの暗号方式を使うか合意できるか」が重要になります。

IETF RFC 7696では、暗号を使うプロトコルは、使用しているアルゴリズムやアルゴリズムスイートを識別する仕組みを持つ必要があると説明されています。
参考: RFC 7696 - Guidelines for Cryptographic Algorithm Agility and Selecting Mandatory-to-Implement Algorithms

たとえばTLSでは、クライアントとサーバーが対応している方式をやり取りし、共通して使える方式を選びます。
TLS 1.3の仕様であるRFC 8446では、対応バージョン、署名アルゴリズム、鍵共有グループなどをやり取りする拡張が定義されています。
参考: RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3

ただし、ここで注意が必要です。
「たくさんの方式に対応しているから安心」とは限りません。

古い方式を残し続けると、設定ミスやダウングレード攻撃の原因になる可能性があります。
そのため、プロトコルの視点では、新しい方式を追加できることだけでなく、古い方式を安全に無効化できることも大切です。

実装の視点

実装の視点では、「暗号処理がアプリケーションの中に固定されすぎていないか」が重要になります。

たとえば、アプリケーションのあちこちに sha1TLSv1 のような文字列が直接書かれていると、後から変更するときに影響範囲を探すだけでも大変です。
一方で、暗号処理を共通の関数、設定ファイル、ライブラリ、APIに集めておけば、変更箇所を絞りやすくなります。

もちろん、暗号処理を共通化すればそれだけで安全になるわけではありません。
共通化した部分が誤っていると、逆に広い範囲へ影響します。
そのため、信頼できる暗号ライブラリを使い、独自実装を避け、レビューやテストを行うことが前提になります。

運用の視点

運用の視点では、「どこで何の暗号を使っているか把握できるか」が重要になります。

どれだけ設計がきれいでも、実際の運用で次のような状態になっていると、移行は難しくなります。

  • 古いサーバーが残っているが、誰も把握していない
  • 外部APIとの接続で、どのTLS設定を使っているか分からない
  • 証明書や秘密鍵の管理場所が整理されていない
  • 例外的に許可した古い方式が、そのまま残り続けている
  • ベンダー製品やクラウドサービス側の対応状況が分からない

暗号アジリティでは、暗号方式を変更できる実装だけでなく、変更対象を見つけ、影響を評価し、段階的に移行する運用体制も必要になります。

NIST CSWP 39upd1でも、暗号アジリティには暗号リスク管理、暗号資産の把握、自動化ツール、ガバナンスなどの観点が含まれると整理されています。
参考: NIST CSWP 39upd1 - Considerations for Achieving Crypto Agility

3.4 「どの方式で作ったものか」を記録する

暗号方式を差し替えられるようにするには、「今どの方式を使っているか」を識別できることが重要です。

たとえば、ファイルのハッシュ値を保存する場面を考えます。
ハッシュ値だけを保存してしまうと、後から見たときに、その値がSHA-256で作られたのか、SHA-384で作られたのか、別の方式で作られたのか分かりにくくなります。

# 後から見たときに困りやすい例
9f86d081884c7d659a2feaa0c55ad015...

このような場合、ハッシュ値だけでなく、使用したアルゴリズムやバージョンを一緒に記録しておくと、将来の移行や検証がしやすくなります。

{
  "algorithm": "sha256",
  "purpose": "file_integrity_check",
  "digest": "9f86d081884c7d659a2feaa0c55ad015...",
  "created_at": "2026-07-01T00:00:00Z"
}

この考え方を、簡単なPythonコードで表すと次のようになります。
ここでは説明を分かりやすくするため、ファイルの改ざん検知用ハッシュ値を例にしています。
パスワード保存にはこの例を使わないでください。

import hashlib
from dataclasses import dataclass


@dataclass(frozen=True)
class DigestRecord:
    """ハッシュ値と、その作成条件を一緒に保存するためのデータ構造。"""
    # どのハッシュ関数で作成したかを記録する。
    # これがないと、後から検証・移行するときに方式を推測する必要が出てしまう。
    algorithm: str

    # 何のために使うハッシュ値なのかを記録する。
    # 用途によって必要な安全性や移行判断が変わるため。
    purpose: str

    # 実際のハッシュ値を16進文字列で保存する。
    digest: str


def create_digest_record(data: bytes, algorithm: str = "sha256") -> DigestRecord:
    """データのハッシュ値とメタ情報をまとめて作成する例。"""
    # アルゴリズム名の表記ゆれを減らすため、小文字にそろえる。
    normalized_algorithm = algorithm.lower()

    # 説明用の簡易的な許可リスト。
    # 実務では、組織のセキュリティ基準や標準仕様に合わせて管理する。
    allowed_algorithms = {"sha256", "sha384", "sha512"}

    # 想定外の方式が指定された場合は、明示的にエラーにする。
    # 「指定できるから何でも使える」状態にしないことが重要。
    if normalized_algorithm not in allowed_algorithms:
        raise ValueError(f"許可されていないハッシュ関数です: {algorithm}")

    # 指定されたアルゴリズムでハッシュ値を計算する。
    hasher = hashlib.new(normalized_algorithm)
    hasher.update(data)

    # ハッシュ値だけでなく、アルゴリズム名と用途も一緒に返す。
    # これにより、将来の検証や移行時に判断材料を残せる。
    return DigestRecord(
        algorithm=normalized_algorithm,
        purpose="file_integrity_check",
        digest=hasher.hexdigest(),
    )

このコードで伝えたいのは、「ハッシュ値の計算方法」そのものではありません。
ポイントは、暗号処理の結果だけでなく、どの方式・どの用途で作られたものかを記録する という考え方です。

これは、暗号アジリティでとても重要です。
なぜなら、将来方式を変更するときに、「どこに古い方式のデータが残っているか」「どれを再計算すべきか」「どれは互換性のために残す必要があるか」を判断しやすくなるからです。

3.5 「選択肢を増やすこと」と「安全に移行できること」は違う

暗号アジリティという言葉から、「対応アルゴリズムをたくさん増やせばよい」と考えてしまうことがあります。
しかし、これは少し危険です。

RFC 7696では、選択肢が多すぎることは有害になり得る、また、唯一の暗号スイートだけに固定することも有害になり得る、といった観点が整理されています。
参考: RFC 7696 - Guidelines for Cryptographic Algorithm Agility and Selecting Mandatory-to-Implement Algorithms

つまり、暗号アジリティではバランスが大切です。

状態 問題点
1つの方式に完全固定する その方式が弱くなったときに移行しにくい
対応方式をむやみに増やす 実装・設定・テスト・監視が複雑になり、古い方式が残りやすい
古い方式を互換性のため無期限に残す 弱い方式が攻撃面として残る可能性がある
許可・禁止の方針が曖昧 何を使ってよいか、いつ止めるべきか判断しにくい

そのため、目指すべきなのは「何でも選べる状態」ではありません。

目指すべきなのは、次のような状態です。

  • 現在許可する方式が明確である
  • 古い方式をいつ、どの条件で無効化するか決められる
  • 新しい方式を追加するときに、影響範囲を確認できる
  • 実際にどの方式が使われているか観測できる
  • 例外的に古い方式を許可する場合は、理由と期限を記録できる

暗号アジリティは、「選択肢の多さ」ではなく、安全に選び、切り替え、不要になったものを止められる管理能力 と考えると分かりやすいです。

3.6 暗号アジリティではないもの

最後に、混同しやすい点を整理します。

暗号アジリティは、次のような意味ではありません。

誤解 実際の考え方
最新の暗号方式を使えば、それだけで暗号アジリティがある 最新方式を選ぶことと、将来差し替えられることは別の話
たくさんの暗号方式に対応すればよい 不要な方式を増やすと、設定ミスや保守負荷が増える可能性がある
古い方式も残しておけば互換性が高くて安心 古い方式を残し続けると、弱い設定が残るリスクがある
独自暗号を作れば自由に変更できる 暗号の独自実装は危険で、通常は標準化・検証されたライブラリを使うべき
設定で切り替えられれば十分 影響範囲の把握、テスト、監視、移行計画、ロールバックも必要

特に、独自暗号を作ることは暗号アジリティではありません。
暗号アジリティは、標準化され、検証され、広く使われている方式やライブラリを前提に、それらを安全に選び直せるようにする考え方です。

💡 豆知識
暗号分野では「自分で暗号を作らない」という教訓がよく語られます。
暗号アジリティも、「自作して自由に変える」ことではなく、「信頼できる方式やライブラリを、将来の変更に備えて扱いやすくしておく」ことに近いです。

3.7 この章のまとめ

この章では、暗号アジリティそのものの意味を整理しました。

暗号アジリティとは、暗号方式を将来必要になったときに、安全に差し替えられるようにしておく設計・運用の考え方です。
対象は暗号アルゴリズムだけではなく、プロトコル、アプリケーション、暗号ライブラリ、設定、鍵、証明書、ハードウェア、運用体制にまで広がります。

特に重要なのは、次の3つです。

  • プロトコルの視点: 通信相手と暗号方式を合意し、古い方式を安全に無効化できること
  • 実装の視点: 暗号処理をコードのあちこちに固定せず、設定やライブラリに整理できること
  • 運用の視点: どこで何の暗号を使っているか把握し、段階的に移行できること

暗号アジリティは、「何でも選べるようにすること」ではありません。
安全な選択肢を管理し、古い方式を止め、新しい方式へ移れる状態を作ることです。

次の章では、暗号方式を差し替えにくいシステムでは具体的に何が困るのかを、アプリケーション、外部連携、証明書、ハードウェアなどの観点から整理していきます。


4. 暗号方式を差し替えにくいと何が困るのか

前章では、暗号アジリティを「暗号方式を安全に差し替えられる設計・運用の考え方」として整理しました。

では、もし暗号方式を差し替えにくいシステムだった場合、具体的に何が困るのでしょうか。
ここでは、アプリケーション、外部連携、証明書、鍵、ハードウェア、運用管理の観点から見ていきます。

最初に結論をまとめると、暗号方式を差し替えにくいシステムでは、古い方式をやめたくても、影響範囲が分からず、簡単には止められない という問題が起こります。

これは、家の電気配線に少し似ています。
どの部屋にどの配線がつながっているか分かっていれば、古くなった配線を交換しやすいです。
しかし、配線図がなく、壁の中で複雑につながっていると、どこを直せばよいのか分からなくなります。

暗号方式も同じです。
どこで、何の暗号方式を、どの目的で使っているのか分からないと、いざ変更が必要になったときに大きな負担になります。

4.1 どこで暗号を使っているか分からない

暗号方式を差し替えるには、まず「どこで暗号を使っているか」を知る必要があります。

しかし、実際のシステムでは、暗号技術は目立つ場所だけで使われているわけではありません。
ログイン、API通信、パスワード保存、ファイル暗号化、電子署名、証明書、バックアップ、外部サービス連携など、いろいろな場所に少しずつ入り込んでいます。

たとえば、あるWebサービスで「古いハッシュ関数を使うのをやめたい」と考えたとします。
このとき、対象はパスワード保存だけとは限りません。

暗号が使われる場所 使われ方の例 差し替え時に困ること
パスワード保存 パスワードハッシュ 既存ユーザーのデータをどう移行するか考える必要がある
API通信 TLSのバージョンや暗号スイート 古いクライアントが接続できなくなる可能性がある
JWTやトークン 署名アルゴリズム 発行済みトークンの検証期間をどう扱うか考える必要がある
証明書 署名アルゴリズムや鍵長 証明書の再発行、配布、検証環境の確認が必要になる
バックアップ ファイル暗号化 古い鍵や方式で暗号化されたデータを復号できるようにしておく必要がある
組込み機器 ファームウェア署名、通信保護 更新できない機器や長期間稼働する機器が残る可能性がある

NISTの暗号アジリティに関する文書でも、暗号アジリティはプロトコルやアプリケーションだけでなく、ソフトウェア、ハードウェア、ファームウェア、インフラまで含めて考える必要があると説明されています。
参考: NIST CSWP 39upd1 - Considerations for Achieving Crypto Agility

また、CISA、NSA、NISTによる耐量子暗号への移行資料でも、量子計算機に対して脆弱な暗号を使っているシステムを把握するために、暗号資産のインベントリ作成が重要だとされています。
参考: CISA / NSA / NIST - Quantum-Readiness: Migration to Post-Quantum Cryptography

💡 豆知識
「暗号インベントリ」という言葉があります。
これは、組織やシステムの中で、どこに、どの暗号方式・鍵長・証明書・ライブラリが使われているかを一覧化する考え方です。
ざっくり言えば、暗号の部品表です。
部品表がないと、古くなった部品を交換したくても、どこに使われているのか探すところから始める必要があります。

4.2 アプリケーションの中に暗号方式が埋め込まれている

差し替えにくさの分かりやすい例が、アプリケーションコードへの直書きです。

たとえば、次のように複数の処理で暗号方式名を直接書いているとします。

import hashlib


def hash_for_user_id(user_id: str) -> str:
    # ユーザーIDから確認用のハッシュ値を作る例。
    # 説明用のコードであり、実務上の設計例ではない。
    return hashlib.sha256(user_id.encode("utf-8")).hexdigest()


def hash_for_file(data: bytes) -> str:
    # ファイルの内容確認用にハッシュ値を作る例。
    # ここでも sha256 を直接指定している。
    return hashlib.sha256(data).hexdigest()


def hash_for_cache_key(value: str) -> str:
    # キャッシュキー用のハッシュ値を作る例。
    # 用途が違っても、同じ方式名がコード内に散らばっている。
    return hashlib.sha256(value.encode("utf-8")).hexdigest()

このコードでは、sha256 という方式名が複数の場所に直接書かれています。

もちろん、SHA-256自体がこの例で問題だと言いたいわけではありません。
ここで見たいのは、暗号方式やハッシュ関数がコードのあちこちに散らばっている状態 です。

将来、用途ごとに方式を変えたい、許可する方式をポリシーで管理したい、ログに利用方式を残したい、という場面になったとき、コードのあちこちを確認する必要があります。
さらに、似たような処理が別のサービスや別のリポジトリにもあると、修正漏れやテスト漏れが起こりやすくなります。

少し改善するなら、次のように用途ごとのポリシーを1か所に寄せる方法があります。

import hashlib
from dataclasses import dataclass


@dataclass(frozen=True)
class HashPolicy:
    # どの用途で、どのハッシュ関数を使うかを表す設定。
    # 実務では、設定ファイルや組織のセキュリティ基準と連携させることが多い。
    purpose: str
    algorithm: str


# 用途ごとのハッシュ方針を1か所にまとめる。
# 方式名をコード中に散らばらせないことで、確認・変更しやすくする。
HASH_POLICIES = {
    "file_integrity": HashPolicy(
        purpose="file_integrity",
        algorithm="sha256",
    ),
    "cache_key": HashPolicy(
        purpose="cache_key",
        algorithm="sha256",
    ),
}


def calculate_hash(data: bytes, purpose: str) -> str:
    # 指定された用途に対応するポリシーを取り出す。
    # 未定義の用途を使った場合は、意図しない方式で処理しないようにエラーにする。
    if purpose not in HASH_POLICIES:
        raise ValueError(f"未定義の用途です: {purpose}")

    policy = HASH_POLICIES[purpose]

    # hashlib.new() を使うことで、ポリシーに書かれた方式名からハッシュ処理を作る。
    # ただし、利用できる方式を無制限にすると危険なので、実務では許可リストで管理する。
    hasher = hashlib.new(policy.algorithm)
    hasher.update(data)

    return hasher.hexdigest()

この例で大切なのは、「設定にすれば何でも安全」ということではありません。
重要なのは、暗号方式を使う場所を把握しやすくし、用途と方式の関係を整理することです。

暗号アジリティでは、単に方式名を変えられるだけでなく、次のような点も考えます。

  • どの用途で使っている方式なのか
  • その方式は現在も許可されているのか
  • 古い方式をいつ無効化するのか
  • 方式変更時に既存データをどう扱うのか
  • 変更後に正しく動いているか確認できるのか

コード上の工夫は、暗号アジリティの一部にすぎません。
それでも、暗号処理があちこちに散らばっている状態を避けることは、将来の移行をかなり楽にしてくれます。

4.3 外部サービスや古い端末との互換性が壁になる

暗号方式を変更するとき、自分たちのシステムだけを見ればよいとは限りません。

Webサービスであれば、利用者のブラウザやスマホアプリがあります。
業務システムであれば、取引先のシステム、社内の古い端末、外部API、決済サービス、監視ツールなどが関係することもあります。

たとえば、サーバー側で古いTLSバージョンを無効化したい場合を考えます。
セキュリティ上は古い方式を止めたいとしても、利用者の一部が古い端末や古いアプリを使っていると、突然接続できなくなる可能性があります。

一方で、互換性を理由に古い方式をずっと残してしまうと、弱い設定が攻撃面として残ります。

IETF RFC 7696では、通信する相手同士が共通の暗号アルゴリズムをサポートしていなければ、機密性、完全性、認証、デジタル署名などの仕組みが正しく働かないと説明されています。
また、アルゴリズム選択が完全性で保護されていない場合、弱い方式へ誘導されるダウングレード攻撃の原因になるとも説明されています。
参考: RFC 7696 - Guidelines for Cryptographic Algorithm Agility and Selecting Mandatory-to-Implement Algorithms

TLSの世界でも、古いバージョンを無期限に残すことは推奨されません。
RFC 8996では、TLS 1.0とTLS 1.1が非推奨化されています。
参考: RFC 8996 - Deprecating TLS 1.0 and TLS 1.1

このように、暗号方式の移行では、次の2つのバランスが必要になります。

観点 考えること
セキュリティ 弱くなった方式や古いプロトコルを残し続けない
互換性 利用者、取引先、端末、アプリが新しい方式に対応できるか確認する
移行計画 いきなり切り替えるのではなく、段階的に無効化する
観測 実際に古い方式で接続している相手がどれくらいいるか確認する
例外管理 一時的に古い方式を許可する場合、理由と期限を決める

暗号アジリティは、「古いものをすぐ全部切る」という意味ではありません。
安全性と業務継続の両方を見ながら、古い方式を減らし、新しい方式へ移れるようにする考え方です。

4.4 証明書や鍵は、ただの設定値ではない

暗号方式を差し替えるとき、証明書や鍵も大きな論点になります。

たとえば、Webサイトで使うサーバー証明書、API認証で使うクライアント証明書、ソフトウェア更新に使う署名鍵、JWTに使う署名鍵などは、単なる設定ファイルではありません。
それらは、発行、配布、保管、更新、失効、ローテーションといった運用と強く結びついています。

たとえば、署名アルゴリズムや鍵長を変更する場合、次のような作業が発生する可能性があります。

対象 起こり得る作業
サーバー証明書 新しい鍵ペアの生成、CSR作成、証明書再発行、サーバー設定変更
クライアント証明書 利用者や端末への再配布、失効管理、接続確認
署名鍵 鍵の保管場所、アクセス権限、ローテーション手順の見直し
HSMやKMS 対応アルゴリズム、鍵長、API、運用手順の確認
古いデータ 旧方式で署名・暗号化されたデータをどう検証・復号するかの判断

NIST SP 800-131A Rev.2では、より強い暗号鍵や、より堅牢なアルゴリズムへ移行するための具体的なガイダンスが示されています。
参考: NIST SP 800-131A Rev.2 - Transitioning the Use of Cryptographic Algorithms and Key Lengths

ここで気をつけたいのは、「新しい方式に変えたら、古い鍵や証明書をすぐ消せばよい」と単純には言えないことです。

たとえば、過去に署名されたデータを後から検証する必要がある場合、旧方式の検証環境や証明書チェーンを一定期間維持する必要があるかもしれません。
バックアップを復元する可能性があるなら、古い方式で暗号化されたデータを復号できる手段も考えておく必要があります。

暗号の移行では、新しく作ること と同じくらい、過去に作ったものをどう扱うか が重要になります。

4.5 ハードウェアや組込み機器では、後から変更しにくいことがある

ソフトウェアであれば、ライブラリ更新や設定変更で対応できる場面があります。
しかし、ハードウェアや組込み機器では、暗号方式の変更がさらに難しくなることがあります。

たとえば、次のようなケースです。

対象 差し替えが難しい理由
古いIoT機器 ファームウェア更新機能がない、または更新が現場作業になる
組込み機器 メモリやCPU性能の制約で、新しい暗号方式を載せにくい
HSM 対応アルゴリズムが製品やファームウェアに依存する
TPMやセキュアエレメント ハードウェアの機能として方式が固定されている場合がある
長期稼働システム 医療、産業、インフラなどで停止や交換が簡単ではない

NIST CSWP 39upd1でも、暗号アジリティを考える対象として、ハードウェア、ファームウェア、インフラが含まれています。
特に、組込み機器やハードウェアベースの暗号機能では、ソフトウェアのように簡単に差し替えられない場合があるため、導入時点から更新可能性を考える必要があります。
参考: NIST CSWP 39upd1 - Considerations for Achieving Crypto Agility

💡 豆知識
暗号の世界では、「ソフトウェアなら直せるが、ハードウェアは簡単には直せない」という問題がよくあります。
たとえば、サーバーの設定は遠隔から変更できても、現場に設置された機器のファームウェア更新や交換には、作業員の派遣、停止時間、検証作業が必要になることがあります。
そのため、長く使う機器ほど、将来の暗号方式変更を見越した設計が大切になります。

4.6 古い方式を残し続けると、攻撃面が増える

差し替えにくいシステムでは、「とりあえず古い方式も残しておく」という判断になりがちです。

確かに、古い方式を残しておけば、古い端末や古い取引先システムとの互換性は保ちやすくなります。
しかし、古い方式を残し続けることは、攻撃者に選択肢を与えることにもつながります。

RFC 7696では、多くのアルゴリズム候補をサポートすることは有害になり得ると説明されています。
対応方式が増えるほど、実装、設定、テスト、監視が複雑になります。
さらに、使われていない古い方式が残ると、普段あまり確認されないコードや設定が攻撃面になる可能性もあります。
参考: RFC 7696 - Guidelines for Cryptographic Algorithm Agility and Selecting Mandatory-to-Implement Algorithms

ここで重要なのは、暗号アジリティは「古い方式を永遠に残すための仕組み」ではないということです。

むしろ、暗号アジリティでは次のような管理が必要になります。

  • どの方式を許可するか決める
  • どの方式を非推奨にするか決める
  • 古い方式をいつ無効化するか決める
  • 実際に古い方式がどれくらい使われているか観測する
  • 例外的に残す場合は、理由、対象、期限を明確にする

「対応できること」と「使い続けてよいこと」は別です。
安全に差し替えられる設計には、古い方式を安全にやめる仕組みも含まれます。

4.7 移行作業が大きくなり、後回しになりやすい

暗号方式の差し替えが難しいシステムでは、移行作業そのものが大きくなります。

すると、セキュリティ上は必要だと分かっていても、次のような理由で後回しになりやすくなります。

後回しになりやすい理由 具体例
影響範囲が分からない どのシステム、どのAPI、どの端末に影響するか分からない
テストが難しい 外部連携先や古い端末を含めた確認が必要になる
業務停止が怖い 切り替え後にログインできない、決済できない、接続できない可能性がある
責任範囲が広い 開発、インフラ、運用、セキュリティ、ベンダー、取引先が関係する
優先順位が上がりにくい すぐに機能追加として見えないため、後回しにされやすい

その結果、古い方式が長く残り、さらに移行しにくくなる、という悪循環が起こります。

暗号アジリティは、この悪循環を少しでも小さくするための考え方です。
最初から完璧にする必要はありません。
まずは、暗号方式を使っている場所を見えるようにすること、暗号処理を1か所に寄せること、古い方式を止める計画を持つことから始められます。

4.8 この章のまとめ

この章では、暗号方式を差し替えにくいと何が困るのかを整理しました。

暗号方式を変更するときに困るのは、単に「コードを書き換えるのが大変」という話だけではありません。
実際には、次のような問題が重なります。

  • どこで何の暗号を使っているか分からない
  • アプリケーションコードのあちこちに方式名が埋め込まれている
  • 古い端末や外部サービスとの互換性を考える必要がある
  • 証明書、鍵、署名、バックアップなど過去のデータも関係する
  • ハードウェアや組込み機器では更新が難しい
  • 古い方式を残し続けると攻撃面が増える
  • 影響範囲が大きくなり、移行作業が後回しになりやすい

暗号アジリティは、こうした問題をゼロにする魔法ではありません。
しかし、将来の変更に備えて、暗号方式を見つけやすくし、切り替えやすくし、古い方式を安全にやめられる状態に近づけることができます。

次の章では、暗号アジリティを支える具体的な設計要素として、暗号処理の分離、設定管理、アルゴリズム識別子、暗号インベントリ、移行計画などを整理していきます。


5. 暗号アジリティを支える設計要素

前章では、暗号方式を差し替えにくいシステムでは、影響範囲が分からない、古い方式を止めにくい、外部連携やハードウェアが壁になる、といった問題が起こることを見ました。

ここからは、少し前向きに考えていきます。
では、暗号方式を将来差し替えやすくするには、どのような設計を意識すればよいのでしょうか。

最初に全体像をまとめると、暗号アジリティを支える設計要素は次のように整理できます。

設計要素 何をするか なぜ大事か
暗号利用の見える化 どこで何の暗号を使っているか記録する 差し替え対象を把握できないと移行できないため
アルゴリズム識別子 方式名、鍵ID、バージョン、用途をデータに持たせる 後から「これは何で作られたものか」を判断するため
暗号処理の分離 アプリ本体から暗号処理を切り離す コード全体を大改修せずに変更しやすくするため
ポリシー管理 許可する方式・禁止する方式を一元管理する 古い方式が勝手に残ることを防ぐため
段階的な移行 追加、併用、観測、無効化の流れで移行する いきなり切り替えて障害を起こすのを避けるため
テスト・監視 実際にどの方式が使われているか確認する 机上の設計だけでなく、運用状態を確認するため

NIST CSWP 39upd1では、暗号アジリティを、プロトコル、アプリケーション、ソフトウェア、ハードウェア、ファームウェア、インフラにおいて、セキュリティと継続運用を保ちながら暗号アルゴリズムを置き換え・適応できる能力として説明しています。
参考: NIST CSWP 39upd1 - Considerations for Achieving Crypto Agility

この章では、この考え方をもう少し実装や設計に近い形へ落とし込んでいきます。

5.1 まずは「どこで何を使っているか」を見えるようにする

暗号方式を差し替えるには、まず差し替え対象を知る必要があります。

これは当たり前のようで、実際にはかなり難しい部分です。
暗号は、ログイン処理、通信、ファイル保存、バックアップ、トークン、証明書、外部API連携、署名検証など、いろいろな場所で使われます。

たとえば、次のような情報を一覧化しておくと、移行時の見通しがかなり良くなります。

管理したい情報
利用箇所 payment-api, login-service, backup-job など
用途 通信保護、署名、ハッシュ、パスワード保存、バックアップ暗号化など
アルゴリズム AES-GCM、SHA-256、HMAC-SHA-256、RSA、ECDSAなど
鍵長・パラメータ RSA 2048 bit、AES 256 bit、楕円曲線の種類など
ライブラリ・実装 OpenSSL、OS標準機能、クラウドKMS、HSMなど
設定場所 アプリ設定、環境変数、リバースプロキシ設定、クラウド設定など
移行優先度 すぐ対応、次回更改時に対応、外部連携先待ちなど

このような一覧は、よく 暗号インベントリ と呼ばれます。
インベントリという言葉は少し固いですが、要するに「暗号の部品表」です。

💡 豆知識
暗号インベントリは、引っ越し前の荷物リストに近いです。
何を持っているか分からないと、何を捨てるべきか、何を新居へ持っていくべきか、どれから運ぶべきかを判断できません。
暗号方式の移行でも、まずは「どこに何があるか」を知ることが出発点になります。

特に耐量子暗号への移行では、この考え方が重要になります。
NISTのPQCプロジェクトでも、組織は量子耐性暗号へ移行するために、脆弱なアルゴリズムがどこで使われているかを特定し、置き換えや更新を計画する必要があると説明されています。
参考: NIST - Post-Quantum Cryptography Project

5.2 アルゴリズム名やバージョンをデータに残す

暗号方式を差し替えるときに困りやすいのが、過去に作られたデータです。

たとえば、ある値のハッシュ、署名、MAC、暗号文が保存されているとします。
その値だけを見て、後から次のことが分かるでしょうか。

  • どのアルゴリズムで作ったのか
  • どの鍵で作ったのか
  • どのバージョンのルールで作ったのか
  • 何の用途のために作ったのか
  • いつ作ったのか

これらが分からないと、移行時に「このデータは新方式で検証すべきか、旧方式で検証すべきか」が判断しにくくなります。

そのため、暗号処理の結果を保存する場合は、必要に応じて次のようなメタデータを一緒に持たせる設計が有効です。

{
  "version": 2,
  "purpose": "webhook_signature",
  "algorithm": "HMAC-SHA-256",
  "key_id": "webhook-key-2026-07",
  "created_at": "2026-07-01T12:00:00+09:00",
  "value": "...署名値やMAC値..."
}

ここで大切なのは、暗号値そのものだけでなく、それをどう解釈すればよいか も保存しておくことです。

メタデータ 役割
version データ形式やポリシーのバージョンを表す
purpose 何のための暗号処理かを表す
algorithm 利用した方式を表す
key_id どの鍵で作られたかを表す。秘密鍵そのものではない
created_at 作成時刻を表す。移行期限や検証期間の判断に使える
value 実際の署名値、MAC値、ハッシュ値など

IETF RFC 7696でも、プロトコルが時間とともに別のアルゴリズムスイートへ移行できるように、アルゴリズムやスイートを識別する仕組みが必要だと説明されています。
ただし、RFC 7696は、識別子があるだけでは十分ではなく、実装や運用側で設定変更、展開、古い方式の無効化を進める必要があるとも述べています。
参考: RFC 7696 - Guidelines for Cryptographic Algorithm Agility and Selecting Mandatory-to-Implement Algorithms

つまり、アルゴリズム名を保存することはスタート地点です。
そこから、実際に安全な方式へ移行できる運用までつなげる必要があります。

5.3 暗号処理をアプリケーション本体から分離する

暗号処理がアプリケーションのあちこちに直接書かれていると、将来の変更が難しくなります。

たとえば、署名処理、ハッシュ処理、暗号化処理が各ファイルにばらばらに書かれているとします。
その状態でアルゴリズムや鍵の扱いを変えようとすると、修正箇所の調査、テスト、レビューが広範囲に広がります。

そこで、暗号処理はできるだけ 共通のモジュール、ライブラリ、API、KMS、HSMなどに寄せる ことが重要になります。

この形にしておくと、アプリケーション側は「何をしたいか」を暗号処理モジュールへ依頼し、具体的な方式や鍵の選択はポリシー側で管理しやすくなります。

たとえば、アプリケーション側は次のように考えます。

  • Webhookの内容が本物か確認したい
  • ファイルの改ざんを検知したい
  • トークンに署名したい
  • 保存データを暗号化したい

一方で、暗号処理モジュール側は次を管理します。

  • どの用途に、どの方式を使うか
  • どの鍵を使うか
  • 古い方式をまだ検証用に許可するか
  • いつ新方式へ切り替えるか
  • どのログを残すか

このように責務を分けると、アプリケーション本体を大きく壊さずに、暗号処理の変更を進めやすくなります。

5.4 設定で切り替えられる範囲と、コードで固定する範囲を分ける

暗号アジリティというと、「全部設定で変えられるようにすればよい」と思うかもしれません。
しかし、何でも設定で自由に変えられるようにすると、逆に危険になることがあります。

たとえば、設定ファイルで md5sha1 のような古い方式を自由に指定できてしまうと、誤設定によって弱い方式が使われる可能性があります。
また、利用してはいけない方式を一時対応で有効化し、そのまま残ってしまうこともあります。

そのため、次のように分けて考えると安全です。

管理するもの 考え方
コードで固定しすぎないもの 用途ごとの既定アルゴリズム、鍵ID、移行期間 将来の変更に備えて設定・ポリシー化する
自由に変えさせないもの 許可されていない古い方式、独自暗号、検証無効化 許可リストやレビューで制御する
記録すべきもの 例外的な旧方式利用、移行期限、理由 後から追跡できるようにする

つまり、暗号アジリティでは、自由に何でも選べること ではなく、安全な範囲で切り替えられること が重要です。

RFC 9325では、TLS/DTLSの安全な利用に関する推奨が示されており、暗号方式や設定はその時点の攻撃状況や実装環境に応じて見直されることが分かります。
また、同文書では、TLS 1.3への移行を促しつつ、実際の配備では相互運用性や環境ごとの事情も考慮されることが説明されています。
参考: RFC 9325 - Recommendations for Secure Use of TLS and DTLS

5.5 コード例:HMACを題材に「方式・鍵ID・バージョン」を一緒に扱う

ここでは、HMACを例にして、暗号アジリティを意識したデータ形式を考えてみます。

HMACは、共通の秘密鍵とハッシュ関数を使って、メッセージが改ざんされていないか、正しい相手が作ったものかを確認する仕組みです。
Pythonの標準ライブラリhmacは、RFC 2104で説明されるHMACアルゴリズムを実装しており、digestmodで利用するハッシュ関数を指定できます。
参考: Python Documentation - hmac

以下のコードは、実務用の完成形ではなく、暗号方式や鍵IDをメタデータとして一緒に扱う考え方 を示すための簡略化した例です。

from __future__ import annotations

import hmac
from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Final


@dataclass(frozen=True)
class MacPolicy:
    """MAC計算に使うポリシーを表す設定。"""

    # ポリシーのバージョン。
    # 形式や既定アルゴリズムを変えたときに、後から区別しやすくする。
    version: int

    # 何の用途のMACなのかを表す。
    # 例: webhook署名、内部API、ファイル改ざん検知など。
    purpose: str

    # HMACで使うハッシュ関数。
    # ここでは説明用に文字列で管理している。
    algorithm: str

    # 鍵そのものではなく、どの鍵を使ったかを示すID。
    # 秘密鍵をログやレスポンスに出してはいけない。
    key_id: str


# 許可するアルゴリズムを明示する。
# 何でも指定できるようにするのではなく、利用可能な方式を絞る。
ALLOWED_HMAC_ALGORITHMS: Final[set[str]] = {
    "sha256",
    "sha384",
    "sha512",
}

# 用途ごとのポリシーを1か所にまとめる。
# 将来、Webhookだけsha384に移す、といった変更を追いやすくする。
MAC_POLICIES: Final[dict[str, MacPolicy]] = {
    "webhook": MacPolicy(
        version=2,
        purpose="webhook",
        algorithm="sha256",
        key_id="webhook-key-2026-07",
    )
}

# 本来、秘密鍵はKMS、HSM、環境変数、シークレット管理サービスなどで扱う。
# ここでは説明のためにダミー値を置いている。
SECRET_KEYS: Final[dict[str, bytes]] = {
    "webhook-key-2026-07": b"dummy-secret-key-for-example",
}


def create_mac_envelope(message: bytes, purpose: str) -> dict[str, str | int]:
    """メッセージのHMACを計算し、メタデータ付きで返す。"""

    # 用途に対応するポリシーを取得する。
    # 未定義の用途なら、意図しない方式で計算せずに止める。
    if purpose not in MAC_POLICIES:
        raise ValueError(f"未定義の用途です: {purpose}")

    policy = MAC_POLICIES[purpose]

    # 許可リストにないアルゴリズムは使わせない。
    # これにより、古い方式や想定外の方式が混ざることを避ける。
    if policy.algorithm not in ALLOWED_HMAC_ALGORITHMS:
        raise ValueError(f"許可されていないアルゴリズムです: {policy.algorithm}")

    # key_idから秘密鍵を取得する。
    # 実務ではKMSやHSMなどから安全に取得することを検討する。
    key = SECRET_KEYS[policy.key_id]

    # HMACを計算する。
    # digestmodにポリシーで指定したハッシュ関数を渡している。
    mac_value = hmac.new(
        key=key,
        msg=message,
        digestmod=policy.algorithm,
    ).hexdigest()

    # MAC値だけでなく、検証に必要なメタデータも一緒に返す。
    return {
        "version": policy.version,
        "purpose": policy.purpose,
        "algorithm": f"HMAC-{policy.algorithm.upper()}",
        "key_id": policy.key_id,
        "created_at": datetime.now(timezone.utc).isoformat(),
        "value": mac_value,
    }


def verify_mac_envelope(message: bytes, envelope: dict[str, str | int]) -> bool:
    """メタデータ付きのHMACを検証する。"""

    # envelopeに保存された用途を取り出す。
    # これにより、用途に対応したポリシーで検証できる。
    purpose = str(envelope["purpose"])

    if purpose not in MAC_POLICIES:
        return False

    policy = MAC_POLICIES[purpose]

    # key_idを確認する。
    # 鍵ローテーション中は、旧key_idも検証用に一定期間許可する設計が必要になる場合がある。
    if envelope.get("key_id") != policy.key_id:
        return False

    key = SECRET_KEYS[policy.key_id]

    expected_mac = hmac.new(
        key=key,
        msg=message,
        digestmod=policy.algorithm,
    ).hexdigest()

    # MAC値の比較には hmac.compare_digest() を使う。
    # 通常の == 比較より、タイミング攻撃の影響を減らす目的で用意されている。
    return hmac.compare_digest(expected_mac, str(envelope["value"]))

この例で見てほしいのは、HMACの細かい内部構造ではありません。
ポイントは、暗号処理を次のように扱っていることです。

  • 用途ごとのポリシーを1か所にまとめている
  • 許可するアルゴリズムを明示している
  • 鍵そのものではなくkey_idで参照している
  • 作成したMAC値に、方式名、鍵ID、バージョン、作成時刻を付けている
  • 検証時に、保存されたメタデータを見て処理している
  • 比較にはhmac.compare_digest()を使っている

このようにしておくと、将来アルゴリズムや鍵を変更するときに、「何を、どのルールで作ったのか」が分かりやすくなります。

ただし、このコードはあくまで説明用です。
実システムでは、鍵の保管、鍵ローテーション、旧鍵での検証期間、ログ、アクセス制御、例外管理、ライブラリやKMSの仕様などを別途検討する必要があります。

5.6 古い方式を安全に止める流れを用意する

暗号アジリティでは、新しい方式を追加できるだけでは不十分です。
古い方式を安全に止められることも重要です。

移行は、次のような段階で進めると考えやすくなります。

段階 目的 注意点
現状把握 旧方式がどこで使われているか確認する ログや設定だけでなく、外部連携先も見る
新方式を追加 新しい方式を使えるようにする 既存処理を壊さないようにする
新方式を既定にする 新規作成分から新方式へ寄せる 旧データの検証・復号は残る場合がある
旧方式を観測する まだ旧方式を使う相手がいるか見る ログ、メトリクス、監視を使う
旧方式を例外扱いにする 残す理由と期限を明確にする 例外が恒久化しないようにする
旧方式を無効化する 弱い方式を使えない状態にする 切り戻し手順も確認する
不要なコードを削除する 攻撃面と保守負荷を減らす 使われていない実装を残し続けない

RFC 7696でも、アルゴリズム識別子だけでは移行は実現できず、実装の保守者やサービス運用者が設定変更、展開、古い方式の非推奨化や無効化を進める必要があるとされています。
参考: RFC 7696 - Guidelines for Cryptographic Algorithm Agility and Selecting Mandatory-to-Implement Algorithms

つまり、暗号アジリティは「切り替えボタンを作ること」ではありません。
そのボタンをいつ押すか、押した後に何を確認するか、問題が起きたらどう戻すかまで含めて考える必要があります。

5.7 テストと監視で「本当に移行できているか」を確認する

暗号方式の移行では、設計書や設定だけを見ていても十分ではありません。
実際にどの方式が使われているか、どのクライアントが旧方式に依存しているか、移行後にエラーが増えていないかを確認する必要があります。

たとえば、次のような観測項目があります。

観測したいこと
利用中のTLSバージョン TLS 1.2、TLS 1.3の割合
利用中の暗号スイート どの暗号スイートが実際に使われているか
署名・MACのバージョン 新形式と旧形式の割合
鍵ID 旧鍵で検証されるデータが残っているか
エラー率 移行後に接続失敗や検証失敗が増えていないか
外部連携先 どの連携先が旧方式に依存しているか

TLSについては、RFC 9325がTLS/DTLSを安全に利用するための推奨を示しており、TLS 1.3への移行や安全な設定の考え方を整理しています。
ただし、実際のサービスでは利用者環境や相互運用性も関係するため、設定変更後の観測が重要になります。
参考: RFC 9325 - Recommendations for Secure Use of TLS and DTLS

この流れは、暗号方式に限らず、一般的なシステム変更にも近いです。
ただし、暗号方式の場合は、通信できなくなる、署名検証に失敗する、過去データが読めなくなる、といった影響が出る可能性があります。

そのため、テストと監視は「念のため」ではなく、暗号アジリティを支える重要な部品だと考えた方がよいです。

5.8 設計時に使える小さなチェックリスト

最後に、暗号アジリティを考えるときのチェック項目をまとめます。

実装の細部に入る前に、次のような質問をしてみると、設計の弱い部分が見つけやすくなります。

質問 確認したいこと
どこで暗号を使っているか分かるか 暗号インベントリがあるか
何の用途で使っているか分かるか 通信、署名、保存、認証などを区別できるか
方式名や鍵IDを記録しているか 後から検証・移行できるか
暗号処理がコードに散らばっていないか 共通モジュールやライブラリに寄せられているか
許可する方式を管理しているか 古い方式や独自方式が勝手に使われないか
旧方式を止める計画があるか 追加だけでなく削除まで考えているか
実際の利用状況を観測できるか ログやメトリクスで確認できるか
外部連携先の対応状況を把握しているか 自社だけで移行できる範囲か
鍵や証明書の更新手順があるか ローテーションや失効に対応できるか
切り戻し手順があるか 移行時の障害に備えているか

このチェックリストは、完璧な正解ではありません。
ただ、暗号アジリティを「難しい標準仕様の話」で終わらせず、自分が作るアプリケーションやシステムに置き換えるための入口になります。

5.9 この章のまとめ

この章では、暗号アジリティを支える設計要素を整理しました。

重要なのは、暗号方式をただ設定で切り替えられるようにすることではありません。
どこで何を使っているかを見えるようにし、暗号処理を分離し、用途ごとのポリシーを管理し、方式名や鍵IDを記録し、古い方式を安全に止める流れを用意することが大切です。

特に、次の流れを意識すると分かりやすいです。

暗号アジリティは、特別な製品を1つ入れれば終わるものではありません。
設計、実装、設定、鍵管理、監視、運用を少しずつ整えながら、将来の変更に備える考え方です。

次の章では、より具体的な例として、TLSを題材に暗号方式がどのように選ばれ、どのように移行されていくのかを見ていきます。

6. TLSを例に見る暗号方式の選び方

ここまで、暗号アジリティを支える設計要素として、暗号利用の見える化、暗号処理の分離、ポリシー管理、段階的な移行、観測の重要性を見てきました。

ただ、考え方だけだと少し抽象的に感じるかもしれません。
そこでこの章では、より具体的な例として TLS を見ていきます。

TLSは、HTTPSの裏側で使われている通信保護の仕組みです。
Webサイトにアクセスするときの https://、API通信、クラウドサービスへの接続など、日常的な通信の多くで使われています。

TLSが暗号アジリティの例として分かりやすいのは、通信を始めるときに、クライアントとサーバーが「どの暗号方式を使うか」をやり取りする仕組みを持っているからです。

もちろん、実際のTLSはとても複雑です。
ここでは仕様の細部すべてではなく、暗号アジリティを理解するために必要な範囲に絞って整理します。

6.1 TLSは「暗号方式を1つ決め打ち」していない

HTTPS通信では、ブラウザやスマホアプリがサーバーに接続するとき、いきなり暗号化されたデータを送り始めるわけではありません。

まず、TLSハンドシェイクと呼ばれる準備のやり取りを行います。
ハンドシェイクでは、ざっくり言うと次のようなことを決めます。

決めること 何のために必要か
TLSのバージョン TLS 1.2、TLS 1.3など どの世代のルールで通信するかを決める
鍵共有の方式 ECDHEで使うグループなど 通信に使う共通鍵の材料を安全に作る
署名アルゴリズム RSA-PSS、ECDSA、EdDSAなど サーバー証明書やハンドシェイクの正当性を確認する
暗号スイート TLS_AES_128_GCM_SHA256など 通信データを守る暗号化・認証方式を決める
証明書 サーバー証明書など 接続先が本物か確認する

RFC 8446では、TLS 1.3の主な目的として、通信相手の認証、通信内容の機密性、改ざん検知を提供することが説明されています。
また、TLSハンドシェイクは、通信相手の認証、暗号モードやパラメータの交渉、共有鍵素材の確立を行い、攻撃者が本来とは異なるパラメータを強制できないように設計されています。
参考: RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3

図にすると、次のようなイメージです。

ここで重要なのは、TLSが「この暗号方式だけを使う」と固定しているわけではないことです。
クライアントが対応できる候補を出し、サーバーがその中から利用する方式を選ぶ、という流れになっています。

このように、通信の最初に利用可能な方式を示し合い、共通して使える方式を選ぶ仕組みは、暗号アジリティを考えるうえでとても分かりやすい例です。

6.2 ClientHelloでは、複数の選択肢を提示する

TLS 1.3では、クライアントは ClientHello というメッセージの中で、対応している暗号関連の候補をサーバーへ伝えます。

RFC 8446では、TLS 1.3の暗号ネゴシエーションにおいて、クライアントが ClientHello で次のような選択肢を提示すると説明されています。

ClientHelloで提示される主な情報 役割
cipher_suites クライアントが対応するAEADアルゴリズムとHKDF用ハッシュの組み合わせを示す
supported_groups 鍵共有で利用できるグループを示す
signature_algorithms ハンドシェイクなどで利用できる署名アルゴリズムを示す
signature_algorithms_cert 証明書内の署名で利用できるアルゴリズムを示す
key_share 鍵共有に必要な公開値などのパラメータを示す
supported_versions 対応するTLSバージョンを示す

参考: RFC 8446 - Section 4.1.1 Cryptographic Negotiation

この仕組みを、少し日常の会話に置き換えると次のようになります。

クライアント:
  私は TLS 1.3 と TLS 1.2 に対応しています。
  暗号スイートは A, B, C に対応しています。
  鍵共有では X, Y に対応しています。
  署名方式は P, Q, R に対応しています。

サーバー:
  では、今回は TLS 1.3 を使いましょう。
  暗号スイートは B を使います。
  鍵共有は Y を使います。
  証明書と署名は Q で確認してください。

もちろん、実際の通信ではこのような日本語ではなく、バイナリ形式のメッセージでやり取りされます。
ただ、考え方としては「使える候補を提示し、共通して使える方式を選ぶ」と理解すると分かりやすいです。

ここで、4章・5章で見た「暗号方式をデータに直書きしすぎない」「利用できる方式を識別できるようにする」という話がつながります。
TLSでは、通信の中に方式を識別するための値があり、それをもとにクライアントとサーバーが共通の方式を選びます。

6.3 TLS 1.3では、暗号スイートの考え方も整理された

TLS 1.2以前を知っていると、「暗号スイート」という言葉に少し混乱するかもしれません。

TLS 1.3では、暗号スイートの意味が整理されています。
RFC 8446では、TLS 1.3でサポートされる対称暗号アルゴリズムは、レガシーなものが削られ、残っているものはAEADアルゴリズムであること、また暗号スイートの考え方が、認証・鍵交換の仕組みと、レコード保護アルゴリズム・HKDF用ハッシュを分ける形に変更されたことが説明されています。
参考: RFC 8446 - Major Differences from TLS 1.2

ざっくり言うと、TLS 1.3の暗号スイート名は、主に次のような情報を表します。

TLS_AES_128_GCM_SHA256
    |   |       |   |
    |   |       |   +-- HKDFなどで使うハッシュ関数: SHA-256
    |   |       +------ 認証付き暗号のモード: GCM
    |   +-------------- 鍵長: 128 bit
    +------------------ 対称暗号: AES

ここで出てくるAEADは、Authenticated Encryption with Associated Data の略です。
日本語では「認証付き暗号」と呼ばれることが多く、暗号化と改ざん検知をまとめて扱う方式です。

派生元の記事でも触れたように、現在の通信保護では「読まれないこと」だけでなく、「途中で書き換えられていないこと」も重要です。
AEADは、その2つを同時に扱うための仕組みだと考えると分かりやすいです。

名前 ざっくりした役割
AES 通信データを暗号化する対称暗号
128 鍵の長さ
GCM 暗号化と改ざん検知を組み合わせるモード
SHA256 鍵導出などに使われるハッシュ関数

ただし、ここで注意したいのは、TLS_AES_128_GCM_SHA256 という名前だけで、TLSのすべてが決まるわけではないことです。
TLS 1.3では、鍵共有や署名アルゴリズムは、暗号スイートとは別に選ばれます。

この分離は、暗号アジリティの観点でも重要です。
たとえば、将来、鍵共有の方式を見直したい場合と、通信データの暗号化方式を見直したい場合では、影響する場所が異なります。

「TLSの暗号方式」と一言でまとめず、どの部分の暗号を変更する話なのかを分けて考えることが大切です。

6.4 署名アルゴリズムと鍵共有も、別々に選ばれる

TLSでは、通信データを暗号化する方式だけでなく、相手を認証するための署名アルゴリズムや、共通鍵を作るための鍵共有グループも重要です。

TLS 1.3では、署名アルゴリズムを示すために、signature_algorithmssignature_algorithms_cert という拡張が用意されています。
RFC 8446では、signature_algorithms_cert は証明書内の署名に、signature_algorithmsCertificateVerify メッセージの署名に適用されると説明されています。
参考: RFC 8446 - Signature Algorithms

また、鍵共有については、supported_groupskey_share が関係します。
supported_groups はクライアントが対応する鍵共有グループを優先順に示す拡張であり、key_share は実際の鍵共有に必要な暗号パラメータを含む拡張です。
参考: RFC 8446 - Supported Groups
参考: RFC 8446 - Key Share

ここでのポイントは、TLSでは複数の暗号要素が分担していることです。

要素 役割 見直しが必要になる例
TLSバージョン 通信全体のルールを決める 古いバージョンの非推奨化
暗号スイート 通信データの保護方式を決める 古い暗号方式やモードをやめる
鍵共有グループ 共通鍵の材料を安全に作る 楕円曲線やPQC移行の検討
署名アルゴリズム サーバーやハンドシェイクの正当性を確認する SHA-1署名の排除、RSAからECDSA/EdDSA/PQC署名への移行検討
証明書 接続先の身元を確認する 鍵長・署名方式・有効期限・認証局の変更

このように見ると、TLSは単に「暗号化する仕組み」ではなく、複数の暗号部品を組み合わせて安全な通信路を作る仕組みだと分かります。

6.5 「選べる」だけでは不十分:ダウングレード攻撃に注意する

ここまで読むと、「複数の方式から選べるなら、それで暗号アジリティは十分では?」と思うかもしれません。

しかし、暗号方式を選べる仕組みには注意点があります。
その代表が ダウングレード攻撃 です。

ダウングレード攻撃とは、攻撃者が通信の途中に入り、より弱い方式を使わせるように誘導する攻撃です。
たとえば、本当は新しい方式で通信できるのに、攻撃者が「古い方式しか使えないように見せかける」ようなイメージです。

このような攻撃を防ぐには、暗号方式の交渉そのものが改ざんされないようにする必要があります。

RFC 8446では、TLSハンドシェイクは攻撃者が本来とは異なるパラメータを強制できないように設計されていると説明されています。
参考: RFC 8446 - Introduction

また、RFC 7696でも、アルゴリズムアジリティにはダウングレード攻撃への注意が必要であり、複雑なネゴシエーションは攻撃機会を生む可能性があると説明されています。
参考: RFC 7696 - Guidelines for Cryptographic Algorithm Agility

つまり、暗号アジリティでは、単に「いろいろな方式に対応する」だけでは不十分です。

  • 安全な方式を優先できること
  • 古い方式をいつまでも残し続けないこと
  • 交渉内容が改ざんされないこと
  • 弱い方式へ誘導されないこと
  • 使われていない方式を観測し、削除できること

これらを含めて、はじめて安全な移行につながります。

6.6 豆知識:TLSには「関節が錆びつかないようにする」GREASEがある

暗号アジリティを考えるうえで、TLSには面白い仕組みがあります。
それが GREASE です。

GREASEは、Generate Random Extensions And Sustain Extensibility の略で、TLSの拡張性が壊れないようにするための仕組みです。
RFC 8701では、TLSのエコシステムでは未知の値を正しく処理できない実装が広がると、将来新しい値を導入したときに相互運用性の問題が起こると説明されています。
参考: RFC 8701 - Applying GREASE to TLS Extensibility

少しやわらかく言うと、GREASEは「将来の拡張に備えて、たまに知らない値を混ぜておき、相手がちゃんと無視できるか確認する」ための仕組みです。

RFC 8701には、「プロトコルの関節が錆びつく」という比喩が出てきます。
未知の値を拒否する実装が広がると、普段は問題なく動いているように見えても、将来新しい値を追加したときに、急に接続できない相手が大量に出てしまう可能性があります。

💡 豆知識
GREASEという名前は、機械の潤滑油を連想させます。
プロトコルの「動くはずの部分」が固まらないように、あえて予約済みの値を流して、未知の値を正しく無視できる状態を保つ、という考え方です。

この話は、暗号アジリティにとてもよくつながります。
新しい暗号方式を導入するには、仕様上の拡張ポイントがあるだけでなく、実装がその拡張を受け入れられる状態である必要があるからです。

6.7 コード例:Pythonで接続先のTLSバージョンと暗号スイートを確認する

ここでは、TLSの暗号アジリティを少し手元で感じるために、Pythonで接続先のTLSバージョンと暗号スイートを確認する簡単なコードを見てみます。

Pythonのsslモジュールは、TLS/SSLの暗号化と相手認証の機能を提供し、内部ではOpenSSLライブラリを利用します。
また、SSLSocket.version()でTLSバージョンを、SSLSocket.cipher()で接続に使われている暗号スイート情報を確認できます。
参考: Python Documentation - ssl

以下のコードは、指定したホストにHTTPS接続し、実際にネゴシエートされたTLSバージョンと暗号スイートを表示する例です。

from __future__ import annotations

import socket
import ssl


def show_tls_parameters(hostname: str, port: int = 443) -> None:
    """指定したホストにTLS接続し、選ばれたTLSバージョンと暗号スイートを表示する。"""

    # OSやPythonが持つ信頼済みCA証明書を利用し、
    # サーバー証明書の検証を有効にしたクライアント用コンテキストを作成する。
    context = ssl.create_default_context()

    # 例として、TLS 1.2以上を許可する。
    # 実際のサービスでは、組織のポリシーや利用者環境に合わせて判断する。
    context.minimum_version = ssl.TLSVersion.TLSv1_2

    # TCP接続を作成する。
    # timeoutを設定し、接続が長時間止まらないようにする。
    with socket.create_connection((hostname, port), timeout=5) as sock:
        # wrap_socketでTLSを開始する。
        # server_hostnameを渡すことで、SNIとホスト名検証に利用される。
        with context.wrap_socket(sock, server_hostname=hostname) as tls_sock:
            # 実際にネゴシエートされたTLSバージョンを取得する。
            tls_version = tls_sock.version()

            # 実際に選ばれた暗号スイートを取得する。
            # 返り値は、暗号スイート名、TLSプロトコル名、秘密鍵ビット数などを含むタプル。
            cipher_name, protocol, secret_bits = tls_sock.cipher()

            print(f"host: {hostname}")
            print(f"tls_version: {tls_version}")
            print(f"cipher: {cipher_name}")
            print(f"cipher_protocol: {protocol}")
            print(f"secret_bits: {secret_bits}")


if __name__ == "__main__":
    # 例としてpython.orgに接続する。
    # 実行環境のOpenSSLや接続先の設定により、結果は変わる可能性がある。
    show_tls_parameters("www.python.org")

このコードで確認しているのは、TLSハンドシェイクの結果として 実際に何が選ばれたか です。

暗号アジリティの観点では、ここがとても重要です。
設定ファイル上はTLS 1.3に対応しているつもりでも、実際の通信ではTLS 1.2で接続されているかもしれません。
あるいは、古いクライアントだけが特定の設定に依存しているかもしれません。

つまり、移行では「設定したか」だけでなく、「実際に使われているか」を観測する必要があります。

6.8 コード例:TLSの最低バージョンをポリシーとして扱う

次に、TLS設定をアプリケーションコードのあちこちに直接書くのではなく、ポリシーとしてまとめる例を見てみます。

以下は、説明用に簡略化したコードです。

from __future__ import annotations

import ssl
from dataclasses import dataclass


@dataclass(frozen=True)
class TlsPolicy:
    """TLS接続に関するポリシーを表す。"""

    # 最低限許可するTLSバージョン。
    # 古いバージョンを無効化したい場合、この値を更新する。
    minimum_version: ssl.TLSVersion

    # 接続先証明書を検証するかどうか。
    # 通常のHTTPSクライアントではTrueにする。
    verify_certificate: bool = True


# アプリケーション全体で使うTLSポリシーを定義する。
# 将来TLS 1.3以上に寄せたい場合も、変更箇所を追いやすくなる。
DEFAULT_TLS_POLICY = TlsPolicy(
    minimum_version=ssl.TLSVersion.TLSv1_2,
    verify_certificate=True,
)


def create_tls_context(policy: TlsPolicy = DEFAULT_TLS_POLICY) -> ssl.SSLContext:
    """TLSポリシーに基づいてSSLContextを作成する。"""

    # create_default_context() は、証明書検証などを含む安全寄りの既定設定を作る。
    context = ssl.create_default_context()

    # 許可するTLSの最低バージョンを設定する。
    # ここをポリシーから渡すことで、アプリ内の複数箇所に設定が散らばるのを避ける。
    context.minimum_version = policy.minimum_version

    if policy.verify_certificate:
        # CERT_REQUIREDにより、接続先証明書の検証を必須にする。
        context.verify_mode = ssl.CERT_REQUIRED
        context.check_hostname = True
    else:
        # 検証を無効化する設定は危険。
        # テスト用途など限定的な場面を除き、安易に使わない。
        context.check_hostname = False
        context.verify_mode = ssl.CERT_NONE

    return context

この例のポイントは、TLSの最低バージョンを TlsPolicy としてまとめていることです。
アプリケーションの各所で ssl.TLSVersion.TLSv1_2 のような値を直接書いてしまうと、後から変更するときに修正箇所を探す必要があります。

一方で、ポリシーとしてまとめておけば、次のような判断を集中的に扱いやすくなります。

  • いつTLS 1.2を許可し続けるか
  • いつTLS 1.3を優先・必須にするか
  • 旧クライアントの例外をどこまで認めるか
  • 証明書検証を無効にするような危険な設定をどう防ぐか

ただし、これも説明用の単純化した例です。
実際のWebサーバーやリバースプロキシでは、Nginx、Apache、ロードバランサ、クラウドサービス、サービスメッシュなど、それぞれの設定方法に従う必要があります。

6.9 TLSの例から分かる暗号アジリティのポイント

TLSの例から、暗号アジリティについて次のことが分かります。

TLSの仕組み 暗号アジリティとしての意味
ClientHelloで候補を提示する 利用可能な方式を識別し、相手に伝えられる
ServerHelloで方式を選ぶ 共通して使える安全な方式を選択できる
暗号スイート、鍵共有、署名が分かれている 変更対象を分解して考えられる
古いTLSバージョンを非推奨化する 新方式の追加だけでなく旧方式の削除が必要
GREASEで未知の値を扱えるか確認する 将来の拡張に備えて実装の硬直化を防ぐ
ログやコードで実際のTLSバージョンを確認する 設定だけでなく運用状態を観測できる

RFC 9325では、TLS 1.3をサポートし、実装している場合は古いTLSよりTLS 1.3を優先して交渉することが推奨されています。
また、TLS 1.0およびTLS 1.1を交渉してはならないことも示されています。
参考: RFC 9325 - Recommendations for Secure Use of TLS and DTLS

このように、TLSでは「選べること」と「古いものをやめること」の両方が重要です。
暗号アジリティは、単に候補を増やすことではなく、時間の経過に合わせて安全な選択へ移行できる状態を保つことだと分かります。

6.10 この章のまとめ

この章では、TLSを例に、暗号方式がどのように選ばれるのかを見てきました。

TLSでは、クライアントとサーバーがハンドシェイクの中で、対応するTLSバージョン、暗号スイート、鍵共有グループ、署名アルゴリズムなどをやり取りします。
そのうえで、共通して利用できる方式を選び、通信内容を保護します。

ただし、複数の方式から選べるだけでは十分ではありません。
ダウングレード攻撃を防ぎ、古い方式を段階的に無効化し、実際にどの方式が使われているかを観測する必要があります。

TLSの例から見ると、暗号アジリティは次のように整理できます。

次の章では、この考え方をさらに大きな移行テーマである 耐量子暗号 とつなげて考えていきます。


7. 耐量子暗号への移行と暗号アジリティ

ここまで、TLSを例にして、暗号方式がどのように選ばれ、どのように移行していくのかを見てきました。

この章では、暗号アジリティが特に注目される大きな理由である 耐量子暗号 への移行を扱います。

耐量子暗号という言葉だけを聞くと、少し遠い未来の話に感じるかもしれません。
しかし、実際には「将来、量子コンピュータが十分に発展したときに備えて、今のうちに暗号方式を移行できる状態を作っておく」という、かなり現実的な設計・運用の話です。

💡 豆知識
耐量子暗号は、英語では Post-Quantum Cryptography と呼ばれ、よく PQC と略されます。
ここでいう「Post」は「量子コンピュータが実用化された後の世界でも使えるように」という意味合いです。
量子コンピュータそのものを使って暗号化する「量子暗号」とは別の話なので、最初はここを分けて考えると分かりやすいです。

7.1 なぜ量子コンピュータが暗号に関係するのか

現在のWeb通信や電子署名では、RSA、ECDH、ECDSAなどの公開鍵暗号が広く使われています。
これらは、現在の一般的なコンピュータでは解くのが難しい数学的問題を安全性の土台にしています。

しかし、将来、暗号解読に十分な規模の量子コンピュータが実現すると、現在広く使われている一部の公開鍵暗号が影響を受ける可能性があります。

CISA、NSA、NISTの資料では、暗号解読に十分な量子コンピュータは、現在の情報システムを守っているRSA、ECDH、ECDSAなどの公開鍵暗号を破る可能性があると説明されています。
参考: CISA / NSA / NIST - Quantum-Readiness: Migration to Post-Quantum Cryptography

ざっくり整理すると、影響を受けやすいのは主に 公開鍵暗号を使う部分 です。

暗号技術の種類 量子時代に向けた主な論点
鍵共有・鍵確立 RSA鍵交換、ECDHなど 将来、量子コンピュータによって安全性が大きく変わる可能性がある
デジタル署名 RSA署名、ECDSAなど 証明書、ソフトウェア署名、電子文書の真正性確認に影響する可能性がある
共通鍵暗号 AESなど 公開鍵暗号とは影響の受け方が異なる。鍵長などの設計判断が重要になる
ハッシュ関数 SHA-256など 公開鍵暗号とは影響の受け方が異なる。用途に応じた安全強度の確認が必要になる

ここで注意したいのは、「量子コンピュータが出てきたら、すべての暗号が一瞬で使えなくなる」という単純な話ではないことです。

特に問題になりやすいのは、TLSの鍵共有やデジタル署名、証明書、ソフトウェア更新の署名、長期保存する重要データの保護など、公開鍵暗号に依存する部分です。

7.2 「あとで復号される」リスクもある

量子コンピュータの話で分かりにくいのは、「まだ実用的な脅威が目の前にないなら、後で対応すればよいのでは?」と感じやすい点です。

ここで重要になるのが、Harvest Now, Decrypt Later という考え方です。
日本語では「今集めて、後で復号する」といった意味になります。

これは、攻撃者が今のうちに暗号化された通信やデータを集めておき、将来それを解読できる環境が整った段階で復号する、というリスクです。

CISA、NSA、NISTの資料でも、長期間保護する必要があるデータについては、今収集されたデータが将来復号される可能性を考えて、早めに移行準備を始める必要があると説明されています。
参考: CISA / NSA / NIST - Quantum-Readiness: Migration to Post-Quantum Cryptography

たとえば、次のような情報は「今すぐ公開されなければよい」だけではなく、数年後、十数年後も守る必要がある場合があります。

データの例 長期的な保護が必要になりやすい理由
医療情報 長期間にわたり個人に結びつく機微な情報になり得る
金融・決済に関する記録 取引履歴や本人確認情報が長く価値を持つ場合がある
企業の研究開発情報 製品化や特許、競争力に関わる場合がある
政府・インフラ関連情報 長期的に機密性が求められる場合がある
認証基盤や証明書関連情報 将来のなりすましや改ざん検証に関係する場合がある

💡 豆知識
暗号の移行では、「今攻撃できるか」だけでなく、「そのデータを何年守る必要があるか」も大切です。
たとえば、今日の天気情報は数年後に漏れても大きな問題になりにくいかもしれません。
一方で、医療情報や研究開発情報は、数年後に漏れても大きな問題になる可能性があります。

7.3 NISTの耐量子暗号標準

耐量子暗号への移行に関して、重要な一次情報の1つがNISTのPQC標準化プロジェクトです。

NISTは2024年8月に、主要な耐量子暗号標準としてFIPS 203、FIPS 204、FIPS 205を公開しました。
NISTのPQCプロジェクトページでは、FIPS 203がML-KEM、FIPS 204がML-DSA、FIPS 205がSLH-DSAを扱う標準として説明されています。
参考: NIST - Post-Quantum Cryptography Project

標準 アルゴリズム 主な役割 ざっくりした説明
FIPS 203 ML-KEM 鍵確立 通信相手と共有秘密を作るための仕組み
FIPS 204 ML-DSA デジタル署名 データの改ざん検知や署名者の確認に使う仕組み
FIPS 205 SLH-DSA デジタル署名 ハッシュベースのデジタル署名方式

FIPS 203では、KEMを「公開チャネル上で2者が共有秘密鍵を確立するために使えるアルゴリズム群」と説明しています。
参考: NIST FIPS 203 - Module-Lattice-Based Key-Encapsulation Mechanism Standard
参考: NIST FIPS 204 - Module-Lattice-Based Digital Signature Standard
参考: NIST FIPS 205 - Stateless Hash-Based Digital Signature Standard

ここで出てくる KEM は、Key-Encapsulation Mechanismの略です。
日本語では「鍵カプセル化メカニズム」と呼ばれます。

初学者向けにかなりざっくり言うと、KEMは「通信相手と同じ秘密情報を共有するための仕組み」です。
TLSのような通信では、この共有秘密をもとにして、実際の通信内容を暗号化するための共通鍵を作ります。

ただし、実際のKEMの内部処理はとても繊細です。
自分で実装するのではなく、標準化された仕様と信頼できるライブラリを使うことが前提になります。

7.4 耐量子暗号への移行は「アルゴリズムを変えるだけ」ではない

耐量子暗号への移行というと、「RSAやECDSAをML-KEMやML-DSAに置き換えればよい」と思うかもしれません。

しかし、実際にはそれだけではありません。

NISTのPQCプロジェクトページでは、組織は量子耐性暗号への移行を始めるべきであり、暗号製品、サービス、プロトコルの更新が必要になること、さらに脆弱なアルゴリズムがどこで使われているかを特定して置き換え計画を立てる必要があると説明されています。
参考: NIST - Post-Quantum Cryptography Project

耐量子暗号への移行では、たとえば次のような点を確認する必要があります。

確認するもの なぜ必要か
通信プロトコル TLS、VPN、SSH、メール暗号化など 鍵共有や証明書の方式が変わる可能性がある
証明書 サーバー証明書、クライアント証明書、CA証明書 署名方式や証明書サイズ、検証環境に影響する可能性がある
ソフトウェア署名 アプリ更新、ファームウェア更新、パッケージ署名 署名検証の方式が変わると配布・更新基盤に影響する
ハードウェア HSM、TPM、ICカード、組込み機器 新方式に対応できない場合、機器更新が必要になる可能性がある
外部サービス クラウド、決済API、認証基盤、取引先システム 自社だけ対応しても接続先が未対応だと移行できない
運用 監視、ログ、障害時の切り戻し、証明書更新手順 移行中のトラブルを検知・復旧できるようにするため

特にPQCでは、鍵や署名、暗号文のサイズが従来方式より大きくなる場合があります。
そのため、単にアルゴリズム名を置き換えるだけでなく、通信量、証明書サイズ、メモリ使用量、組込み機器の制約なども確認する必要があります。

ここまで見ると、耐量子暗号への移行は、まさに暗号アジリティが問われる場面だと分かります。

7.5 まず必要なのは「暗号インベントリ」

耐量子暗号への移行で最初に困るのは、「そもそも自分たちのシステムのどこで、どの暗号を使っているのか分からない」という問題です。

CISA、NSA、NISTの資料では、組織に対して、量子対応ロードマップの作成、暗号インベントリの実施、リスク評価、ベンダーとの対話を始めるよう促しています。
参考: CISA / NSA / NIST - Quantum-Readiness: Migration to Post-Quantum Cryptography

また、NIST NCCoEのPQC移行プロジェクトでは、暗号ディスカバリの取り組みとして、組織が重要なデータやデジタルシステムを守るために、どこでどのように暗号が使われているかを把握するための暗号インベントリツールに焦点を当てています。
参考: NIST NCCoE - Migration to Post-Quantum Cryptography

ここでいう暗号インベントリは、難しく聞こえますが、要するに「暗号の棚卸し」です。

棚卸しする情報
利用箇所 Webサーバー、API、VPN、認証基盤、ファームウェア更新
アルゴリズム RSA、ECDSA、ECDH、AES、SHA-256など
鍵長・パラメータ RSA 2048 bit、P-256、SHA-256など
利用目的 鍵共有、署名、暗号化、MAC、ハッシュ化
保護対象データ 個人情報、決済情報、研究情報、認証情報など
データの保護期間 数日、数年、10年以上など
管理者・ベンダー 社内管理、クラウドサービス、外部製品など
移行難易度 設定変更で済む、ライブラリ更新が必要、機器交換が必要など

次のコードは、本格的なスキャンツールではありません。
暗号インベントリを作るときに、どのような情報を集め、どのように優先順位を付けるかをイメージするための簡単な例です。

from dataclasses import dataclass
from typing import Literal

# 実際の移行判断では、組織の基準、ベンダー情報、標準仕様を確認する必要があります。
# ここでは「考え方」を示すため、かなり単純化しています。

CryptoPurpose = Literal["key_exchange", "signature", "encryption", "hash"]

@dataclass
class CryptoAsset:
    name: str
    algorithm: str
    purpose: CryptoPurpose
    protects_long_term_secret: bool
    owner: str
    vendor_managed: bool


def is_quantum_vulnerable_public_key(asset: CryptoAsset) -> bool:
    """
    量子コンピュータ移行で特に確認したい公開鍵暗号かどうかを判定する例です。
    RSA、ECDH、ECDSAなどは、PQC移行の検討対象になりやすいものとして扱います。
    """
    algorithm = asset.algorithm.upper()
    public_key_keywords = ["RSA", "ECDH", "ECDSA"]

    return (
        asset.purpose in {"key_exchange", "signature"}
        and any(keyword in algorithm for keyword in public_key_keywords)
    )


def migration_priority(asset: CryptoAsset) -> str:
    """
    移行の優先度をざっくり分類する例です。
    長期的に守る必要がある情報を扱い、かつ量子脆弱な公開鍵暗号を使っているものを高優先にします。
    """
    if is_quantum_vulnerable_public_key(asset) and asset.protects_long_term_secret:
        return "high"

    if is_quantum_vulnerable_public_key(asset):
        return "medium"

    return "review"


assets = [
    CryptoAsset(
        name="payment-api.example.com",
        algorithm="ECDHE P-256",
        purpose="key_exchange",
        protects_long_term_secret=True,
        owner="payment-team",
        vendor_managed=False,
    ),
    CryptoAsset(
        name="software-update-signing",
        algorithm="RSA-2048",
        purpose="signature",
        protects_long_term_secret=False,
        owner="platform-team",
        vendor_managed=False,
    ),
    CryptoAsset(
        name="object-storage-encryption",
        algorithm="AES-256-GCM",
        purpose="encryption",
        protects_long_term_secret=True,
        owner="infra-team",
        vendor_managed=True,
    ),
]

for asset in assets:
    print(f"{asset.name}: {migration_priority(asset)}")

この例で大切なのは、暗号方式だけを見ているわけではない点です。

同じRSAやECDHでも、何に使っているのか、どのデータを守っているのか、どのくらい長く守る必要があるのか、自社で変更できるのか、ベンダー依存なのかによって、移行の優先順位は変わります。

7.6 ハイブリッド方式という移行期の考え方

耐量子暗号への移行では、既存の公開鍵暗号とPQCを組み合わせる ハイブリッド方式 が検討されることがあります。

ハイブリッド方式は、かなりざっくり言うと、「従来方式」と「耐量子方式」の両方を使い、少なくとも一方が安全なら全体として安全性を保ちやすくする、という移行期の考え方です。

IETFのTLS関連ドラフトでは、TLS 1.3におけるハイブリッド鍵交換の構成が検討されており、複数の鍵交換アルゴリズムを同時に使い、その結果を組み合わせることで、構成要素の少なくとも一方が破られていなければ安全性を保つことを目標とする、と説明されています。
参考: IETF Internet-Draft - Hybrid key exchange in TLS 1.3

また、IETFの別ドラフトでは、TLS 1.3向けにX25519MLKEM768、SecP256r1MLKEM768、SecP384r1MLKEM1024といった、ECDHEとML-KEMを組み合わせるハイブリッド鍵合意方式が定義されています。
参考: IETF Internet-Draft - Post-quantum hybrid ECDHE-MLKEM Key Agreement for TLSv1.3

ただし、IETF Internet-Draftは作業中の文書です。
記事執筆時点では、最終的なRFCではないものもあるため、「こうした方向で標準化が進められている」と説明するのが安全です。
投稿前には、ドラフトが改版・置換・RFC化されていないかを確認してください。

ハイブリッド方式は、移行期の不確実性に対応する有力な考え方です。
一方で、処理やデータサイズ、実装、検証、相互運用性は複雑になります。

そのため、「2つ使えば必ず安全」というより、標準仕様、実装品質、運用、監視まで含めて慎重に扱う必要があります。

7.7 コード例:KEMを直接書き込まず、差し替えられる形にする

ここでは、実際のML-KEMを実装するのではなく、設計のイメージを示します。

PQCのように今後ライブラリやプロトコル対応が変わり得る領域では、アプリケーションのあちこちに特定の方式名を直接書き込むよりも、鍵確立の処理を抽象化しておく方が移行しやすくなります。

次のコードは、鍵確立処理を差し替えられるようにするための簡単な例です。
実際の暗号処理は書いていません。実運用では、標準に準拠した信頼できる暗号ライブラリを利用してください。

from dataclasses import dataclass
from typing import Protocol

@dataclass
class KeyAgreementResult:
    """
    鍵確立の結果を表すデータです。
    algorithm_nameを残しておくことで、あとから「どの方式で共有秘密を作ったか」を確認できます。
    """
    algorithm_name: str
    shared_secret: bytes


class KeyAgreementProvider(Protocol):
    """
    鍵確立処理の共通インターフェースです。
    具体的な方式をアプリ本体から隠すことで、後から実装を差し替えやすくします。
    """

    def agree(self, peer_public_data: bytes) -> KeyAgreementResult:
        ...


class ClassicalECDHProvider:
    def agree(self, peer_public_data: bytes) -> KeyAgreementResult:
        # ここでは実際のECDH処理は実装しません。
        # 実運用では、検証済みの暗号ライブラリを使います。
        raise NotImplementedError("ECDH処理は信頼できる暗号ライブラリで実装する")


class MlKemProvider:
    def agree(self, peer_public_data: bytes) -> KeyAgreementResult:
        # ここでは実際のML-KEM処理は実装しません。
        # FIPS 203に準拠した実装や、利用環境で認められたライブラリを確認します。
        raise NotImplementedError("ML-KEM処理は標準準拠のライブラリで実装する")


class HybridProvider:
    def __init__(self, classical: KeyAgreementProvider, post_quantum: KeyAgreementProvider):
        self.classical = classical
        self.post_quantum = post_quantum

    def agree(self, peer_public_data: bytes) -> KeyAgreementResult:
        # 従来方式と耐量子方式の両方で共有秘密を作る、という考え方を示しています。
        # 実際に共有秘密をどう組み合わせるかは、標準仕様に従う必要があります。
        classical_result = self.classical.agree(peer_public_data)
        pq_result = self.post_quantum.agree(peer_public_data)

        # この連結は説明用の疑似処理です。
        # 実運用では、標準仕様で定められた鍵導出処理を使います。
        combined_secret = classical_result.shared_secret + pq_result.shared_secret

        return KeyAgreementResult(
            algorithm_name=f"hybrid({classical_result.algorithm_name}+{pq_result.algorithm_name})",
            shared_secret=combined_secret,
        )

このコードのポイントは、ML-KEMそのものを自作することではありません。

重要なのは、アプリケーション本体が「ECDHだけ」「ML-KEMだけ」と決め打ちするのではなく、鍵確立の処理を差し替え可能な部品として扱っていることです。

もちろん、暗号処理では抽象化しすぎても危険です。
開発者が自由に危険な方式を選べるようにするのではなく、組織やライブラリ側で許可された方式だけを使えるようにする必要があります。

7.8 耐量子暗号移行で暗号アジリティが効くポイント

耐量子暗号への移行では、次のような場面で暗号アジリティが効いてきます。

暗号アジリティの要素 PQC移行での意味
暗号インベントリ RSA、ECDH、ECDSAなどをどこで使っているか把握する
アルゴリズム識別子 どの方式で作った鍵・署名・証明書か分かるようにする
ライブラリ分離 標準準拠のPQC実装へ移行しやすくする
設定・ポリシー管理 利用可能な方式を段階的に切り替える
互換性確認 取引先、古い端末、クラウド、HSMとの接続性を確認する
監視・ログ 実際に古い方式が残っていないか確認する
段階的移行 テスト環境、限定リリース、本番移行、旧方式停止を順に進める

NIST IR 8547の初期公開ドラフトでは、量子脆弱な暗号アルゴリズムから、PQCのデジタル署名アルゴリズムや鍵確立方式へ移行するためのNISTの想定アプローチが説明されています。
また、この文書は、IT製品、サービス、インフラをPQCへ移行する取り組みやタイムラインを考えるための材料として位置づけられています。
参考: NIST IR 8547 - Transition to Post-Quantum Cryptography Standards

耐量子暗号への移行は、1回だけの大きな変更ではなく、長い期間をかけて段階的に進む可能性が高いです。
そのため、最初から「差し替えやすい設計」「観測できる運用」「古い方式を止められる計画」を持っておくことが重要になります。

7.9 この章のまとめ

この章では、耐量子暗号への移行と暗号アジリティの関係を見てきました。

耐量子暗号は、単に新しい暗号アルゴリズムを知っていればよいという話ではありません。
現在のシステムのどこでRSA、ECDH、ECDSAなどの公開鍵暗号を使っているかを把握し、どのデータをどれくらい長く守る必要があるかを考え、標準化されたPQC方式や対応ライブラリ、プロトコル、ベンダー製品の状況を確認しながら、段階的に移行する必要があります。

NISTは2024年にFIPS 203、FIPS 204、FIPS 205を公開し、ML-KEM、ML-DSA、SLH-DSAといったPQC標準を示しています。
一方で、実際のシステム移行では、証明書、TLS、HSM、クラウドサービス、外部API、組込み機器、監視、ログ、切り戻し手順まで関係します。

だからこそ、耐量子暗号への移行は、暗号アジリティの重要性を理解するうえでとてもよい具体例になります。

次の章では、暗号アジリティだけで安全になるわけではない理由として、暗号の引っ越しの難しさと、実務で注意したい落とし穴を整理します。

8. 暗号アジリティだけで安全になるわけではない

ここまで、暗号アジリティを「暗号方式を安全に差し替えられるようにする考え方」として見てきました。

では、暗号アジリティを意識していれば、それだけでシステムは安全になるのでしょうか。

答えは、それだけでは足りません

少し身近な例で考えてみます。

引っ越ししやすい部屋にしておくことは大切です。
しかし、引っ越し先の住所変更、鍵の交換、電気・水道の手続き、旧居の解約まで考えなければ、引っ越し全体は完了しません。

暗号方式の移行も同じです。
暗号方式を変更できる設計は重要ですが、実際には、証明書、鍵管理、外部連携、古い端末、ログ監視、例外対応なども一緒に考える必要があります。

この章では、暗号の移行を「引っ越し」にたとえながら、なぜ暗号アジリティだけでは安全にならないのかを整理します。
あわせて、実務で注意したい落とし穴も確認します。

8.1 暗号は、思ったよりいろいろな場所に埋まっている

まず難しいのは、暗号がシステムの表面だけにあるわけではないことです。

たとえば、WebサービスでTLSを使っている場合、暗号に関係する場所はWebサーバーの設定だけではありません。
アプリケーション、フレームワーク、OS、暗号ライブラリ、証明書ストア、クラウドのロードバランサ、API Gateway、HSMやKMSのような鍵管理サービスなど、さまざまな場所が関係します。

この図のように、暗号はシステムの一部だけでなく、複数の層にまたがって使われます。
そのため、ある暗号方式をやめたいと思っても、どこか1か所を変更すれば終わりとは限りません。

NIST CSWP 39upd1でも、暗号アジリティはプロトコル、アプリケーション、ソフトウェア、ハードウェア、ファームウェア、インフラを含めて考える必要があると説明されています。
参考: NIST CSWP 39upd1 - Considerations for Achieving Crypto Agility

💡 豆知識
暗号の移行で最初に難しいのは、「何を変えるか」よりも「どこで暗号を使っているか」を見つけることです。
引っ越しでたとえるなら、まず家の中にどんな荷物があるかを確認する作業に近いです。

8.2 「暗号方式」だけでなく、周辺の形式も一緒に変わることがある

暗号の引っ越しでは、暗号方式だけを入れ替えればよいとは限りません。

たとえば、デジタル署名の方式を変更する場合を考えます。
単に「署名アルゴリズムを変える」だけでなく、署名データの形式、証明書、検証ライブラリ、ログの保存形式、外部APIの仕様、監査で確認する項目まで影響することがあります。

変更したいもの 一緒に確認が必要になりやすいもの
ハッシュ関数 保存済みハッシュ、ハッシュ値の長さ、比較処理、データ形式
デジタル署名方式 証明書、署名フォーマット、検証ライブラリ、署名対象データ
鍵共有方式 TLS設定、クライアント対応状況、鍵交換メッセージのサイズ
鍵長 鍵生成、鍵保管、HSM/KMS対応、性能、証明書発行
暗号ライブラリ API互換性、依存パッケージ、OS対応、ビルド環境

特に、暗号方式が変わると、データのサイズや処理時間が変わることがあります。
耐量子暗号への移行でも、公開鍵、署名、暗号文などのサイズが従来方式より大きくなる場合があり、プロトコルやデータ形式への影響を確認する必要があります。

NISTのPQC移行に関する取り組みでも、暗号をどこでどのように使っているかを把握し、リスク管理や優先順位付けに活用する暗号インベントリの重要性が示されています。
参考: NIST NCCoE - Migration to Post-Quantum Cryptography

8.3 古い相手との互換性が、移行を難しくする

暗号方式を新しくしたいと思っても、通信相手がその方式に対応していなければ通信できません。

たとえば、自分たちのサーバーが新しいTLS設定に対応していても、利用者の古い端末や、取引先の古いシステム、組込み機器が対応していない場合があります。
このとき、セキュリティだけを考えれば古い方式を止めたい一方で、業務やサービス継続を考えると急に止められない、という難しさが出てきます。

RFC 7696では、暗号を使うプロトコルでは通信する相手同士が共通の暗号アルゴリズムをサポートしている必要があると説明されています。
また、アルゴリズム識別子があるだけでは移行は完了せず、実装の保守者やサービス運用者が、新しい方式の有効化、古い方式の無効化、設定変更、展開を行う必要があるとも述べられています。
参考: RFC 7696 - Guidelines for Cryptographic Algorithm Agility and Selecting Mandatory-to-Implement Algorithms

このあたりは、引っ越しでいうと「自分は新居に移れるけれど、家族や荷物の一部がまだ旧居に残っている」状態に近いです。
全員が同じタイミングで移動できるとは限らないため、段階的な移行計画が必要になります。

8.4 選択肢を増やしすぎると、かえって危なくなることがある

暗号アジリティという言葉だけを見ると、「たくさんの暗号方式に対応できるほどよい」と感じるかもしれません。
しかし、これは少し注意が必要です。

対応する方式が多すぎると、設定が複雑になります。
その結果、意図せず古い方式が有効なまま残ったり、弱い方式へ誘導される余地が生まれたり、ほとんど使われていないコードが保守されずに残ったりする可能性があります。

状態 一見よさそうな点 注意点
多くの暗号方式をサポートする 古い相手とも接続しやすい 弱い方式が残りやすく、設定も複雑になる
新しい方式だけを許可する セキュリティを強くしやすい 古い端末や外部連携が切れる可能性がある
移行期間を設ける 段階的に切り替えられる いつまでも旧方式が残らないよう期限が必要
利用状況を測定する 実態を見ながら止められる ログ設計や監視が必要になる

RFC 9325では、TLS/DTLSの安全な利用に関する推奨事項が示されており、TLS 1.3への移行を促しつつも、実装の普及状況や相互運用性も考慮されています。
また、セキュリティに関するBest Current Practiceはその時点での知見であり、将来の攻撃や環境変化に注意する必要があるとも述べられています。
参考: RFC 9325 - Recommendations for Secure Use of TLS and DTLS

💡 豆知識
暗号アジリティは、「何でも選べる状態」にすることではありません。
大切なのは、許可する方式、優先する方式、廃止する方式を管理しながら、安全に移行できる状態にすることです。

8.5 証明書の世界では、1枚だけを見ても足りない

HTTPSで使われるサーバー証明書を考えるときも、暗号の引っ越しは少し複雑です。

ブラウザがWebサイトを信頼するためには、サーバー証明書だけでなく、その証明書を発行した中間CA証明書、さらに上位のルートCA証明書まで含めた「証明書チェーン」を検証します。
そのため、証明書に使われている署名アルゴリズムや鍵長を見直す場合、サーバー証明書だけでなく、発行元やチェーン全体の対応状況も関係することがあります。

このように、証明書は単独で使われるというより、信頼のつながりの中で使われます。
暗号方式の移行では、「自分のサーバー証明書だけ更新すれば終わり」とは限らない点に注意が必要です。

💡 豆知識
証明書の移行は、名刺を新しくするだけでなく、その名刺を発行した会社や、その会社を信用している仕組みまで確認する作業に近いです。

8.6 「切り替え日」はゴールではない

暗号方式の移行では、「この日から新方式に切り替える」という日付を決めることがあります。
しかし、実際には切り替え日だけで移行が完了するわけではありません。

移行後も、古い方式で接続しているクライアントが残っていないか、想定外のエラーが増えていないか、外部連携が失敗していないか、監視する必要があります。
場合によっては、一定期間だけ旧方式を残し、ログを見ながら段階的に無効化していくこともあります。

ここで大切なのは、移行を「イベント」ではなく「プロセス」として見ることです。
暗号アジリティは、差し替える瞬間だけでなく、差し替える前の棚卸し、差し替え中の互換性確認、差し替え後の監視まで含めて考える必要があります。

8.7 暗号の引っ越しで使える簡単な確認メモ

実際に暗号方式の移行を考えるときは、いきなり具体的なアルゴリズム名から入るよりも、まず次のような問いで整理すると分かりやすくなります。

確認すること
どこで使っているか TLS、JWT、パスワード保存、ファイル暗号化、DB暗号化、署名処理
何を使っているか アルゴリズム名、鍵長、ライブラリ、証明書、プロトコルバージョン
誰とつながっているか 利用者端末、外部API、取引先、クラウド、社内システム
どれくらい守る必要があるか 一時的な通信、長期保存データ、法令・契約上の保存対象
何から変えるべきか 重要データを扱う箇所、外部公開箇所、古い方式が残る箇所
どう戻すか 切り戻し手順、監視、ログ、障害時の連絡先

この確認メモは、暗号インベントリの入り口としても使えます。
NIST NCCoEのPQC移行プロジェクトでも、暗号インベントリツールによって、組織が重要なデータやデジタルシステムを保護するために、どこでどのように暗号を使っているかを把握することが重視されています。
参考: NIST NCCoE - Migration to Post-Quantum Cryptography

8.8 暗号の引っ越しから見えるポイント

この章では、暗号の移行を「引っ越し」にたとえて、なぜ暗号方式の変更が難しいのかを整理しました。

暗号は、アプリケーションの表面だけでなく、ライブラリ、OS、証明書、外部API、クラウド、HSM、組込み機器など、さまざまな場所に埋まっています。
また、暗号方式だけでなく、鍵長、証明書、データ形式、通信相手、監視、運用手順まで関係するため、1か所を変えれば終わりとは限りません。

そのため、暗号アジリティでは「新しい方式を使えるようにすること」だけでなく、次のような考え方が重要になります。

  • どこで暗号を使っているかを見える化する
  • 古い方式をいつ、どのように止めるかを決める
  • 通信相手や外部サービスとの互換性を確認する
  • 移行中と移行後のログを見て、実際の利用状況を確認する
  • 切り替え後も、運用手順や設定を更新し続ける

暗号の引っ越しは、荷物を別の家に運ぶだけではなく、住所変更、鍵の交換、電気・水道の切り替え、旧居の解約まで含めた作業に近いです。
だからこそ、普段から「差し替えやすい設計」と「移行できる運用」を意識しておくことが、暗号アジリティの大切なポイントになります。

ここまでの「引っ越し」のイメージを踏まえて、続いて暗号アジリティを考えるときに注意したい落とし穴を整理します。

8.9 暗号アジリティで注意すべき落とし穴

ここまで、暗号アジリティを「暗号方式を安全に差し替えられるようにする考え方」として見てきました。

ただし、暗号アジリティは便利な考え方である一方で、使い方を間違えると、かえってシステムを複雑にしたり、弱い設定を残してしまったりすることがあります。
ここでは、暗号アジリティを考えるときに注意したい落とし穴を整理します。

ここでのポイントは、「変更できること」と「安全に変更できること」は別物 だという点です。

8.10 落とし穴1:何でも選べるようにすれば安全だと思ってしまう

暗号アジリティという言葉を聞くと、つい「たくさんの暗号方式に対応できるほどよい」と考えたくなります。

しかし、これは少し危険な考え方です。

対応する暗号方式が増えすぎると、設定が複雑になります。
その結果、本来は使わないはずの古い方式が有効なまま残ったり、弱い方式へ誘導される余地が生まれたりする可能性があります。

IETFのRFC 7696では、暗号アルゴリズムの識別子を持つことは重要ですが、それだけでは移行は完了しないと説明されています。
新しい方式を有効化し、古い方式を無効化し、設定を展開するには、実装を保守する人やサービスを運用する人の作業が必要です。
参考: RFC 7696 - Guidelines for Cryptographic Algorithm Agility and Selecting Mandatory-to-Implement Algorithms

つまり、暗号アジリティは「選択肢を無限に増やすこと」ではありません。
許可する方式、優先する方式、廃止する方式を管理すること が重要です。

💡 豆知識
レストランのメニューが多すぎると、何を選べばよいか迷ってしまいます。
暗号方式も同じで、選択肢が多すぎると、設定する人や実装する人が迷いやすくなります。
大切なのは「何でも選べること」ではなく、「安全な選択肢を、管理された形で選べること」です。

8.11 落とし穴2:設定ファイルで変えられれば十分だと思ってしまう

暗号方式をコードに直接書かず、設定ファイルで切り替えられるようにすることは大切です。

ただし、設定ファイルで自由に何でも変えられるようにすると、別の問題が生まれます。
たとえば、開発中の検証目的で一時的に弱い方式を有効にしたまま、本番環境に反映されてしまうかもしれません。

そのため、実際には「自由に変更できる設定」ではなく、組織やシステムで許可された暗号ポリシーの範囲内で変更できる設定 にする必要があります。

次のコードは、設定値をそのまま信頼せず、許可リストに含まれる方式だけを使う簡単な例です。

from dataclasses import dataclass

# システムで許可する暗号ポリシーを定義する。
# 実システムでは、組織のセキュリティ基準や利用ライブラリの対応状況に合わせて管理する。
APPROVED_POLICIES = {
    "2026-default": {
        "hash": {"SHA-256", "SHA-384"},
        "mac": {"HMAC-SHA-256"},
        "min_tls_version": "TLSv1.2",
    },
    "2026-strict": {
        "hash": {"SHA-384"},
        "mac": {"HMAC-SHA-384"},
        "min_tls_version": "TLSv1.3",
    },
}


@dataclass
class CryptoConfig:
    """アプリケーションが使いたい暗号設定を表すクラス。"""
    policy_name: str
    hash_algorithm: str
    mac_algorithm: str


def validate_crypto_config(config: CryptoConfig) -> None:
    """暗号設定が、許可済みポリシーの範囲内か確認する。"""

    # 存在しないポリシー名を指定された場合は、設定ミスとして止める。
    if config.policy_name not in APPROVED_POLICIES:
        raise ValueError(f"Unknown crypto policy: {config.policy_name}")

    policy = APPROVED_POLICIES[config.policy_name]

    # ハッシュ関数が許可リストに含まれているか確認する。
    if config.hash_algorithm not in policy["hash"]:
        raise ValueError(f"Hash algorithm is not allowed: {config.hash_algorithm}")

    # MAC方式が許可リストに含まれているか確認する。
    if config.mac_algorithm not in policy["mac"]:
        raise ValueError(f"MAC algorithm is not allowed: {config.mac_algorithm}")


# 例: 許可された設定
valid_config = CryptoConfig(
    policy_name="2026-default",
    hash_algorithm="SHA-256",
    mac_algorithm="HMAC-SHA-256",
)
validate_crypto_config(valid_config)

# 例: 古い方式や未承認の方式を指定すると、ここでエラーにできる。
invalid_config = CryptoConfig(
    policy_name="2026-default",
    hash_algorithm="SHA-1",
    mac_algorithm="HMAC-SHA-256",
)
# validate_crypto_config(invalid_config)  # ValueError: Hash algorithm is not allowed

この例で伝えたいのは、SHA-256SHA-384 の細かい使い分けではありません。
大切なのは、暗号方式を設定で変えられるようにしつつ、その設定が安全な範囲から外れないようにすること です。

暗号アジリティは、自由度を上げる考え方ではありますが、自由にしすぎると危険です。
そのため、実際には「設定可能」と「許可済み」を分けて考える必要があります。

8.12 落とし穴3:新しい方式を追加しただけで満足してしまう

新しい暗号方式に対応すると、それだけで安全になったように感じるかもしれません。

しかし、新しい方式を追加しても、古い方式がそのまま残っていれば、システム全体としてはまだ移行が完了していません。
特にTLSのように通信相手と方式を選ぶ仕組みでは、古い方式が残っていると、設定ミスや互換性維持のために使われ続ける可能性があります。

RFC 9325では、TLS/DTLSを安全に利用するための推奨事項が整理されており、TLS 1.3への移行が推奨される一方で、実装の普及状況や相互運用性も考慮されています。
また、セキュリティに関するBest Current Practiceはその時点での知見であり、将来の攻撃や環境変化に注意する必要があるとも述べられています。
参考: RFC 9325 - Recommendations for Secure Use of TLS and DTLS

暗号方式の移行では、次の3つをセットで考える必要があります。

段階 目的 注意点
追加 新しい方式を使えるようにする 既存環境との互換性を確認する
優先 可能な場合は新しい方式を選ばせる ログで実際に使われているか確認する
廃止 古い方式を無効化する 期限と例外条件を決めておく

新しい方式を入れるだけで止まってしまうと、古い方式が「念のため」として残り続けます。
この状態は、引っ越し先を用意したのに旧居をいつまでも解約していないようなものです。

8.13 落とし穴4:暗号方式だけを見て、鍵管理を見落とす

暗号方式を差し替える話では、ついアルゴリズム名に注目しがちです。

たとえば、RSA、ECDSA、AES-GCM、SHA-256、ML-KEMなどの名前を見ると、「どの方式を使うか」が中心の話に見えます。
しかし、実際のセキュリティでは、鍵をどのように生成し、保管し、更新し、廃棄するかも非常に重要です。

NIST SP 800-131A Rev.2では、暗号アルゴリズムや鍵長の移行について、より強い鍵やより堅牢なアルゴリズムへの移行に関するガイダンスが示されています。
また、アルゴリズムの破壊やより強力な計算技術の登場に備えて、暗号利用の変更を計画しておく必要があると説明されています。
参考: NIST SP 800-131A Rev.2 - Transitioning the Use of Cryptographic Algorithms and Key Lengths

暗号方式を変更するときは、少なくとも次のような点を一緒に確認する必要があります。

見るべきもの 確認したいこと
鍵長 現在の推奨に合っているか
鍵の生成 安全な乱数生成や生成手順になっているか
鍵の保管 KMS、HSM、環境変数、設定ファイルなど、どこに置かれているか
鍵のローテーション 定期更新や緊急時の更新手順があるか
鍵の廃棄 古い鍵をいつ、どのように使えなくするか
影響範囲 古い鍵で暗号化・署名されたデータをどう扱うか

暗号方式を新しくしても、古い鍵が残り続けたり、鍵の保管場所が分からなかったりすると、移行はうまく進みません。

💡 豆知識
暗号方式は「鍵穴の形」、鍵管理は「鍵そのものの扱い」に近いです。
鍵穴を新しくしても、古い鍵が大量に残っていたり、誰が鍵を持っているか分からなかったりすると、安全とは言い切れません。

8.14 落とし穴5:暗号インベントリを作って終わりにしてしまう

暗号インベントリは、暗号アジリティを考えるうえでとても重要です。

ただし、一覧表を一度作っただけでは十分ではありません。
システムは日々変化します。新しいサービスが追加されたり、ライブラリが更新されたり、外部APIとの接続が増えたり、証明書が更新されたりします。

そのため、暗号インベントリは「一度作る資料」ではなく、継続的に更新する運用情報 として扱う必要があります。

NIST CSWP 39upd1では、暗号アジリティを、プロトコル、アプリケーション、ソフトウェア、ハードウェア、ファームウェア、インフラにおいて、セキュリティと継続運用を保ちながら暗号アルゴリズムを置き換え・適応できる能力として説明しています。
この説明からも、暗号アジリティは一度きりの対応ではなく、システムや運用に組み込む考え方だと分かります。
参考: NIST CSWP 39upd1 - Considerations for Achieving Crypto Agility

暗号インベントリには、たとえば次のような情報を含めると、後から移行計画を立てやすくなります。

項目
利用箇所 Webサーバー、API、DB暗号化、JWT、署名処理
暗号方式 TLS 1.3、AES-GCM、HMAC-SHA-256、ECDSAなど
鍵情報 鍵長、鍵ID、保管場所、ローテーション方針
ライブラリ OpenSSL、言語標準ライブラリ、クラウドKMS SDKなど
依存先 外部API、取引先、クラウドサービス、組込み機器
重要度 扱うデータの機密性、保存期間、外部公開の有無
移行状態 調査中、対応予定、移行中、完了、例外対応中

「どこで何を使っているか」を更新し続けることで、いざ移行が必要になったときに、影響範囲を把握しやすくなります。

8.15 落とし穴6:PQCを入れればすべて解決すると考えてしまう

耐量子暗号への移行は、暗号アジリティを考えるうえで重要なテーマです。

ただし、PQCを導入すればすべての問題が解決する、というわけではありません。
PQCでも、鍵の管理、証明書、プロトコル、ライブラリ、性能、データサイズ、外部サービスとの相互運用性などを考える必要があります。

NISTのPQCプロジェクトでは、FIPS 203、FIPS 204、FIPS 205として、ML-KEM、ML-DSA、SLH-DSAが標準化されています。
一方で、実際にシステムへ導入するには、利用しているプロトコルや製品、ライブラリ、証明書基盤、運用手順との整合を確認する必要があります。
参考: NIST Post-Quantum Cryptography Project

PQC移行では、特に次のような点に注意が必要です。

注意点 内容
データサイズ 公開鍵、署名、暗号文が従来方式より大きくなる場合がある
性能 処理時間、通信量、メモリ使用量への影響を確認する
相互運用性 通信相手や外部サービスが対応しているか確認する
標準化状況 RFC化前の仕様やドラフトを本番利用する場合は慎重に扱う
移行期間 従来方式とPQC方式を併用する期間の設計が必要になる

特にTLSにおけるPQC対応は、IETFで標準化が進められている領域もあります。
Internet-Draftは標準化途中の文書であり、正式なRFCとは扱いが異なるため、記事や設計で取り上げるときは「検討・標準化が進められている方向性」として慎重に表現する必要があります。
参考: IETF Internet-Draft - Post-quantum hybrid ECDHE-MLKEM Key Agreement for TLSv1.3

8.16 落とし穴7:テスト環境だけで安心してしまう

暗号方式を変更するときは、当然テストが必要です。

ただし、テスト環境で成功したからといって、本番環境でも必ず問題が起きないとは限りません。
本番環境には、古い端末、古いOS、古いライブラリ、特殊なネットワーク機器、外部API、想定外のクライアントなど、テスト環境では再現しにくい要素が含まれることがあります。

そのため、暗号方式の移行では、テスト環境での確認に加えて、段階的な展開とログ監視が重要になります。

特に、暗号方式の変更は「接続できるかどうか」に直結します。
小さな設定差でも、一部の利用者だけ接続できない、特定の外部APIだけ失敗する、といった問題が起きる可能性があります。

移行時には、次のようなログを見られるようにしておくと、状況を把握しやすくなります。

見たい情報 目的
使用されたTLSバージョン 古いバージョンが残っていないか確認する
使用された暗号スイート 想定どおりの方式が選ばれているか確認する
接続失敗の理由 古い端末・証明書・外部APIの問題を切り分ける
クライアント種別 古いOSや古いライブラリの影響を確認する
エラー率の変化 移行後に障害が増えていないか確認する

8.17 落とし穴8:例外対応をそのまま放置してしまう

暗号方式の移行では、どうしても例外対応が必要になることがあります。

たとえば、取引先システムがまだ新しい方式に対応していない、古い組込み機器をすぐには交換できない、法令や契約の都合で過去データの検証を続ける必要がある、といったケースです。

例外対応そのものが悪いわけではありません。
問題は、例外対応を「一時的なもの」として管理せず、そのまま放置してしまうことです。

例外対応で決めること
例外の理由 取引先が未対応、機器更新が必要、過去データ検証が必要
期限 いつまで旧方式を許可するか
範囲 どのシステム、どの通信相手、どのデータだけに許可するか
補完策 ネットワーク制限、監視強化、アクセス制御、ログ保存
見直し方法 定期レビュー、期限到来時の再承認、移行完了確認

例外対応は、セキュリティ上の借金のようなものです。
必要な場面では使うことがありますが、返済計画がないまま増やしていくと、後から大きな負担になります。

8.18 この章のまとめ

この章では、暗号アジリティだけで安全になるわけではない理由と、注意すべき落とし穴を整理しました。

暗号アジリティは、単に「複数の暗号方式に対応すること」ではありません。
重要なのは、どの方式を許可し、どの方式を優先し、どの方式をいつ廃止するかを、設計・実装・運用の中で管理することです。

特に、次の点には注意が必要です。

  • 何でも選べる状態にすると、設定が複雑になり、弱い方式が残りやすい
  • 設定ファイルで変えられるだけでは不十分で、許可済みポリシーによる制御が必要
  • 新しい方式を追加しても、古い方式を無効化しなければ移行は終わらない
  • 暗号方式だけでなく、鍵管理、証明書、データ形式、外部連携も一緒に見る必要がある
  • 暗号インベントリは一度作って終わりではなく、継続的に更新する必要がある
  • PQCは重要だが、導入すればすべて解決する魔法の仕組みではない
  • テスト環境だけでなく、本番環境のログや利用状況を見ながら段階的に進める必要がある
  • 例外対応は、期限・範囲・補完策を決めて管理する必要がある

暗号アジリティは、柔軟性を高める考え方です。
しかし、柔軟性だけを高めると、管理しきれない複雑さが生まれます。

そのため、暗号アジリティでは、変えられる設計安全に変えるための管理 をセットで考えることが大切です。

次の章では、ここまでの内容を振り返りながら、暗号アジリティを初学者向けにもう一度整理します。

9. まとめ

最後に、本記事で整理した内容を振り返ります。
暗号アジリティでは、「今どの暗号方式を使うか」だけでなく、「将来必要になったときに、安全に差し替えられるか」まで考えることが大切です。

本記事では、スマホ決済、ネットショッピング、HTTPS、クラウドサービスのような身近な例を入口にして、暗号アジリティの考え方を整理しました。

普段使っているサービスの裏側では、暗号化、署名、鍵共有、証明書、ハッシュ、MACなど、さまざまな暗号技術が組み合わされています。
これらの技術は、通信内容を盗み見られないようにしたり、途中で書き換えられていないか確認したり、本物の相手と通信しているかを確かめたりするために使われます。

ただし、暗号方式は「一度選んだら、ずっとそのまま使えるもの」ではありません。

計算機の性能向上、暗号解析の進展、標準仕様の変更、古いプロトコルの非推奨化、ライブラリの更新、そして耐量子暗号への移行などによって、暗号方式を見直す必要が出てきます。

そのときに、どこで何の暗号を使っているか分からなかったり、アプリケーションの中に特定の方式が強く埋め込まれていたりすると、変更はとても大変になります。

ここで重要になるのが、暗号方式を安全に差し替えられる設計・実装・運用 です。

NIST CSWP 39upd1では、暗号アジリティを、プロトコル、アプリケーション、ソフトウェア、ハードウェア、ファームウェア、インフラにおいて、セキュリティと継続運用を保ちながら暗号アルゴリズムを置き換え・適応できる能力として説明しています。
参考: NIST CSWP 39upd1 - Considerations for Achieving Crypto Agility

また、IETF RFC 7696でも、暗号アルゴリズムは時間とともに弱くなる前提で、プロトコルが別のアルゴリズムスイートへ移行できるようにする必要があると整理されています。
参考: RFC 7696 - Guidelines for Cryptographic Algorithm Agility and Selecting Mandatory-to-Implement Algorithms

9.1 なぜ暗号方式を固定するだけでは足りないのか

暗号技術では、強い方式を選ぶことはもちろん大切です。
しかし、それだけで将来まで安全と言い切ることはできません。

たとえば、現在は問題なく使えている方式でも、将来の標準変更や利用環境の変化によって、別の方式へ移行した方がよい場面が出てくる可能性があります。

TLS 1.0やTLS 1.1のように、かつて広く使われたプロトコルが後に非推奨になる例もあります。
また、SHA-1のように、以前は多くの場面で使われていたハッシュ関数が、現在では暗号的な保護目的での利用から移行する対象になっている例もあります。

ここで大切なのは、古い方式を使っていたこと自体を単純に責めることではありません。
技術や標準は時間とともに変わります。
そのため、システム側も変化に対応できる余地を持っておく必要があります。

今の時点で適切な方式を選ぶ
+ 将来見直せるように設計しておく
+ 古い方式を安全に止められるようにしておく

この3つをセットで考えることが、暗号アジリティの出発点になります。

9.2 暗号アジリティでは「差し替えられる状態」を作る

暗号アジリティは、単に新しい暗号方式を使うことではありません。

本記事で特に意識したいのは、次のような状態を作ることです。

観点 ざっくりした意味
見える化 どこで、どの暗号方式、鍵長、証明書、ライブラリを使っているか把握する
分離 暗号処理をアプリケーション本体に散らばらせず、ライブラリや専用モジュールに寄せる
記録 ハッシュ値、署名、暗号文などに、方式名やバージョンなどの情報を残す
ポリシー管理 使ってよい方式、優先する方式、廃止する方式を設定として管理する
段階的移行 新方式を追加し、利用状況を見ながら旧方式を止めていく
監視 実際にどの方式が使われているか、ログやメトリクスで確認する

これらは、どれか1つだけで完結するものではありません。

たとえば、暗号方式を設定ファイルで変えられるようにしても、どこで何を使っているか分からなければ、移行対象を見落とすかもしれません。
逆に、暗号インベントリを作っても、実装が特定の方式に強く依存していれば、変更には大きな改修が必要になります。

つまり、暗号アジリティでは、設計・実装・運用をつなげて考えることが重要です。

この図のように、暗号アジリティは一度整えたら終わりではありません。
継続的に見直しながら、暗号を安全に使い続けるための考え方です。

9.3 暗号アジリティはセキュリティ設計全体の一部

もう1つ大切なのは、暗号アジリティだけでシステム全体が安全になるわけではないという点です。

暗号方式を差し替えやすくしていても、鍵管理が弱ければ安全とは言えません。
TLSの設定が適切でも、証明書の更新運用に問題があれば障害や信頼性低下につながります。
PQCへの移行計画があっても、どのシステムで量子脆弱な暗号を使っているか把握できていなければ、移行対象を正しく決められません。

つまり、暗号アジリティは、暗号技術を安全に使い続けるための重要な考え方ですが、単独で完結するものではありません。

たとえば、次のような対策と組み合わせて考える必要があります。

対策 主に関係すること
鍵管理 鍵の生成、保管、更新、ローテーション、廃棄を安全に行う
証明書管理 証明書の発行、更新、有効期限、失効、証明書チェーンを管理する
ライブラリ管理 暗号ライブラリの更新、脆弱性対応、依存関係を管理する
設定管理 TLS設定や許可アルゴリズムを一貫して管理する
ログ・監視 旧方式の利用状況や移行後の不具合を確認する
インシデント対応 脆弱な方式が見つかったときに、影響範囲を調べて対応する
サプライチェーン管理 外部サービス、ベンダー製品、HSM、組込み機器の対応状況を確認する

このように見ると、暗号アジリティは「暗号方式を選ぶ話」だけではなく、システム全体の保守性や運用設計にも関わる考え方だと分かります。

セキュリティでは、1つの対策にすべてを任せるのではなく、複数の対策を重ねることが大切です。
暗号アジリティも、その多層防御を支える1つの考え方として位置づけると理解しやすくなります。

9.4 本記事の要点

最後に、本記事の要点を短くまとめます。

よくある考え方 注意点
強い暗号方式を選べば終わり 将来の標準変更や移行まで考える必要がある
暗号方式をコードに直接書く 後から変更するときに修正範囲が大きくなりやすい
設定で自由に選べればよい 弱い方式を選べてしまうと危険なので、許可済みポリシーが必要
新しい方式を追加すれば移行完了 古い方式を止める計画と利用状況の確認が必要
TLSが対応していれば安心 証明書、鍵管理、ライブラリ、外部連携先の対応も必要
PQCを入れればすべて解決 インベントリ、移行計画、互換性、性能影響も考える必要がある
暗号アジリティだけで安全になる 鍵管理、設定管理、監視、インシデント対応と組み合わせる必要がある

この記事で特に伝えたいことは、次の一文です。

暗号アジリティでは、「今どの暗号方式を使うか」だけでなく、「将来必要になったときに安全に差し替えられるか」まで考える必要がある。

そのため、暗号処理をアプリケーションに固定しすぎず、方式名やバージョンを記録し、許可済みポリシーで管理し、古い方式を段階的に止められるようにしておくことが大切です。

9.5 最後に

本記事では、暗号技術の中でも「暗号方式を後から安全に差し替えられる設計」に絞って説明しました。

ただし、実際のサービスでは、暗号アジリティだけでなく、TLS、共通鍵暗号、公開鍵暗号、デジタル署名、MAC、ハッシュ関数、証明書、鍵共有など、さまざまな暗号技術が組み合わされています。

暗号技術は、単語だけを見ると難しく感じます。
しかし、「何を守るための技術なのか」「どの場面で使われるのか」「ほかの技術とどう組み合わさるのか」「将来どう変えられるのか」に分けると、少しずつ整理しやすくなります。

この記事が、暗号アジリティの考え方や、暗号方式を安全に使い続けるための設計・運用を理解する入口になればうれしいです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?