概要
ネットショッピングで住所や支払い情報を入力する。
メッセージアプリで友人に連絡する。
スマホ決済でお店に支払う。
クラウドにファイルを保存する。
これらはまったく別のサービスに見えますが、裏側では共通して「データを安全に扱うための暗号技術」が使われています。
たとえば、カフェのWi-Fiでネットショッピングをしている場面を考えてみます。
通信内容がそのまま流れていたら、住所や支払い情報を第三者に盗み見られてしまうかもしれません。
そこで、通信内容は第三者が読みにくい形に変換されます。
この「中身を読まれないようにする」ために重要な役割を持つのが、共通鍵暗号です。
共通鍵暗号は、ざっくり言うと「同じ鍵で閉めて、同じ鍵で開ける」方式です。
たとえば、同じ合鍵を持っている人だけが開けられる箱をイメージすると分かりやすいです。
送る側はその鍵でデータを暗号化し、受け取る側は同じ鍵で元に戻します。
代表的な共通鍵暗号として、AES があります。
AESは、現在の共通鍵暗号を学ぶうえで、まず押さえておきたい基本的な方式です。
ただし、ここで注意したいのは「AESという名前を知っている = 安全に使える」ではない、という点です。
実際の通信や保存データでは、AESそのものだけでなく、どのような使い方をするかが重要になります。
たとえば、同じAESでも、利用モードの選び方や、nonce / IV と呼ばれる値の扱い、鍵の管理方法を間違えると、安全性が大きく下がる可能性があります。
さらに、暗号化は基本的に「中身を読まれないようにする」ためのものです。
しかし、実サービスではそれだけでは足りません。
たとえば、支払い金額が第三者に読まれなかったとしても、途中で「1,000円」が「10,000円」に書き換えられてしまったら困ります。
つまり、データを隠すだけでなく、途中で書き換えられていないかも確認する必要があります。
この考え方につながるのが、認証付き暗号や AEAD です。
認証付き暗号は、暗号化によって中身を守りつつ、改ざんされていないかも確認するための仕組みです。
その代表例として、AES-GCM があります。
この記事では、共通鍵暗号をいきなり数式やアルゴリズムから説明するのではなく、身近なサービスの裏側から整理します。
そのうえで、AES、暗号利用モード、認証付き暗号、AEAD、AES-GCMの関係を、初学者にも分かりやすい形でつなげていきます。
💡 豆知識
AESは、ある日突然決まった暗号方式ではありません。
NISTによる選定プロセスを経て、Rijndaelという方式がAESとして標準化されました。
また、FIPS 197は2023年に更新されていますが、NISTはこの更新について、アルゴリズム自体への技術的変更ではなく、説明や図表などを分かりやすくするための編集上の改善だと説明しています。
つまり「AESが2023年に別物へ変わった」という話ではありません。
この記事の立ち位置
この記事は、暗号技術全体を整理する記事から派生した、共通鍵暗号に焦点を当てた個別記事です。
全体像の記事では、暗号技術を次のような「道具箱」として整理しました。
- 共通鍵暗号
- 認証付き暗号
- ハッシュ関数
- MAC
- 公開鍵暗号
- デジタル署名
- 鍵共有
- パスワードハッシュ
その中で、共通鍵暗号は主に「大量のデータをすばやく暗号化する」ために使われる技術です。
ただし、実際には共通鍵暗号だけで安全なシステムが完成するわけではありません。
通信で使う鍵をどう準備するのか。
暗号文が改ざんされていないことをどう確認するのか。
鍵をどこに保存し、どう更新するのか。
こうした点まで含めて考える必要があります。
この記事では、その入口として、共通鍵暗号の考え方からAES-GCMまでをざっくり整理します。
この記事で分かること
この記事では、次の内容を整理します。
- 共通鍵暗号がどのような場面で使われるのか
- 共通鍵暗号と公開鍵暗号の役割の違い
- AESがどのような位置づけの暗号方式なのか
- AESを使うときに暗号利用モードが必要になる理由
- 暗号化だけでは改ざん検知にならない理由
- 認証付き暗号とAEADの基本的な考え方
- AES-GCMを学ぶときに押さえたい用語
- nonce / IV、AAD、認証タグでつまずきやすい点
- 実務や学習で共通鍵暗号を扱うときの注意点
対象読者
この記事は、次のような人を想定しています。
- 情報セキュリティを学び始めた人
- AESという名前は聞いたことがあるが、何をしている技術か整理したい人
- 「暗号化すれば改ざんも防げるのか」が少し曖昧な人
- AES-GCM、AEAD、nonce、認証タグといった用語を初学者向けに整理したい人
- 暗号技術を実装する前に、まず安全に使うための考え方を知りたい人
本記事で扱わないこと
本記事は、共通鍵暗号の入口を整理することを目的にしています。
そのため、次の内容は深く扱いません。
- AESの内部構造の詳細
- SubBytes、ShiftRows、MixColumns、AddRoundKeyなどの各処理の数式的説明
- AESを自力で実装する手順
- 本格的なプロダクション実装コード
- 暗号方式の安全性証明
- TLS 1.3の詳細なハンドシェイク
- 鍵管理システムやHSMの詳細設計
これらは重要なテーマですが、最初からすべてを詰め込むと、共通鍵暗号の全体像が見えにくくなります。
本記事ではまず、「どの場面で、何を守るために、どのような考え方が必要になるのか」を整理します。
1. 身近なサービスの裏側で何が起きているのか
ネットショッピング、スマホ決済、メッセージアプリ、クラウド保存。
普段は「ボタンを押したら終わり」「スマホをかざしたら終わり」のように見えますが、その裏側では多くのデータが送られたり、保存されたりしています。
たとえば、ネットショッピングでは、ログイン情報、配送先住所、購入内容、支払いに関わる情報などがやり取りされます。
スマホ決済では、支払いに必要な情報が、スマホ、店舗側の端末、決済サービスのシステムなどの間で扱われます。
クラウド保存では、手元のファイルがネットワークを通じてクラウド側に送られ、保存されます。
ここで大切なのは、これらのデータが「どこかにある」だけでなく、移動している という点です。
自分のスマホやPCの中だけで完結しているわけではなく、ネットワークを通ってサーバーに届いたり、サーバー側で保存されたりしています。
つまり、身近なサービスを安全に使うには、少なくとも次のようなことを考える必要があります。
| 場面 | 守りたいこと | ざっくりした理由 |
|---|---|---|
| 通信しているとき | 第三者に中身を読まれにくくする | カフェのWi-Fiや通信経路の途中で盗み見られると困るため |
| 保存しているとき | 保存データを勝手に読まれにくくする | 端末やサーバーから情報が漏えいしたときの被害を抑えるため |
| 処理しているとき | 途中で書き換えられていないか確認する | 支払い金額や送信内容が変えられると困るため |
| 相手を確認するとき | 本物のサービスとやり取りしているか確認する | 偽サイトやなりすましに情報を渡すと危険なため |
このような安全確認の一部を支えているのが、暗号技術です。
その中でも、今回の記事で中心に扱う 共通鍵暗号 は、通信内容や保存データを第三者に読まれにくい形にするうえで重要な役割を持ちます。
1.1 カフェのWi-Fiでネットショッピングする場面
まず、カフェのWi-Fiでネットショッピングをしている場面を考えてみます。
商品を選び、ログインし、配送先を入力し、支払い手続きを進める。
利用者から見ると普通の操作ですが、裏側ではブラウザとWebサーバーの間でデータがやり取りされています。
もし通信内容がそのまま読める形で流れていたら、通信経路のどこかで第三者に盗み見られるかもしれません。
そこで、Webの通信では多くの場合、HTTPSが使われます。
HTTPSでは、HTTPの通信をTLSという仕組みで保護します。
TLS 1.3を定めるRFC 8446では、TLSはインターネット上のクライアントとサーバーの通信について、盗聴、改ざん、メッセージ偽造を防ぐように設計されていると説明されています。
参考: RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3
ここでいきなりTLSの細かい仕組みを追う必要はありません。
まずは、次のイメージを持てれば十分です。
ネットショッピングの裏側では、ブラウザとサーバーの間に「安全な通り道」を作り、その中でデータをやり取りしている。
この「安全な通り道」の中では、通信に使う鍵を準備した後、大量のデータを効率よく守るために共通鍵暗号系の技術が使われます。
つまり、共通鍵暗号は、Web通信の安全性を支える重要な部品の一つです。
💡 豆知識
ブラウザの鍵マークは、「そのサイトで買い物をすれば絶対に安全」という意味ではありません。
主に、通信経路がTLSで保護されていることや、証明書によって接続先を確認できていることを示します。
鍵マークがあっても、偽サイトや不審な入力フォームには注意が必要です。
1.2 スマホ決済では「一瞬の支払い」の裏側でデータが動いている
次に、コンビニや飲食店でスマホ決済をする場面を考えます。
利用者から見ると、スマホをかざす、QRコードを読み取る、決済ボタンを押す、といった操作は数秒で終わります。
しかし、その短い時間の裏側では、支払い金額、店舗、利用者、決済サービスなどに関係する情報が処理されています。
ここで重要なのは、単に「内容を見られないようにする」だけでは足りないことです。
たとえば、支払い金額が第三者に読まれなかったとしても、途中で金額が書き換えられてしまったら困ります。
つまり、スマホ決済のような場面では、少なくとも次の2つを考える必要があります。
| 必要なこと | やさしく言うと |
|---|---|
| 中身を読まれにくくする | 支払いに関わる情報を第三者に見えにくくする |
| 途中で変わっていないか確認する | 金額や取引内容が勝手に書き換えられていないか確認する |
共通鍵暗号は、主に1つ目の「中身を読まれにくくする」部分で重要になります。
一方で、2つ目の「途中で変わっていないか確認する」には、MACや認証付き暗号、デジタル署名など、別の考え方も関係します。
この後の記事では、共通鍵暗号から出発して、なぜ認証付き暗号やAEADが必要になるのかまで整理していきます。
1.3 クラウド保存では「送るとき」と「置いておくとき」を分けて考える
クラウドストレージにファイルを保存する場面も、共通鍵暗号を考えるうえで分かりやすい例です。
ファイルをクラウドにアップロードするとき、そのデータはネットワークを通ってクラウド側に送られます。
さらに、送られたファイルはクラウド側のストレージに保存されます。
つまり、クラウド保存では大きく分けて次の2つの場面があります。
| 場面 | 考えること |
|---|---|
| 送るとき | 通信中のデータを第三者に読まれにくくする |
| 置いておくとき | 保存されたデータを勝手に読まれにくくする |
この違いは、英語ではよく data in transit と data at rest のように説明されます。
前者は通信中のデータ、後者は保存中のデータです。
OWASP Cryptographic Storage Cheat Sheetは、保存中のデータを保護するための考え方を整理した資料です。
このような資料を見ると、暗号技術は通信だけでなく、保存データを守る場面でも重要であることが分かります。
参考: OWASP Cryptographic Storage Cheat Sheet
ただし、ここでも「暗号化して保存しているから何でも安全」とは言い切れません。
暗号化に使う鍵をどこに置くのか、誰が使えるのか、漏えいしたときにどう更新するのかまで考える必要があります。
これは、後の章で扱う「鍵管理」の話につながります。
1.4 身近なサービスを暗号技術の視点で見る
ここまでの例をまとめると、身近なサービスの裏側では、次のような安全確認が行われています。
この図で伝えたいのは、暗号技術が「難しい数式だけの世界」ではないということです。
私たちが普段使っているサービスの裏側で、通信内容を守ったり、保存データを守ったり、改ざんを見つけたりするために使われています。
その中でも共通鍵暗号は、特に 大量のデータを効率よく読めない形にする ために重要です。
ネットショッピングの通信、クラウド保存、メッセージのやり取りなど、さまざまな場面で関係します。
一方で、共通鍵暗号だけを知っていれば十分というわけではありません。
暗号化したデータが途中で書き換えられていないか、鍵をどのように共有・管理するか、どのモードで使うか、といった点も重要になります。
1.5 この章のまとめ
この章では、共通鍵暗号の説明に入る前に、身近なサービスの裏側でどのようにデータが動いているのかを見ました。
ネットショッピングでは、ブラウザとサーバーの間で情報がやり取りされます。
スマホ決済では、短い支払い操作の裏側で、支払いに関わるデータが処理されます。
クラウド保存では、ファイルを送るときと保存しておくときの両方でデータ保護を考える必要があります。
ここで共通しているのは、次の点です。
- データを第三者に読まれにくくしたい
- データが途中で書き換えられていないか確認したい
- 本物の相手とやり取りしているか確認したい
- 鍵を安全に扱う必要がある
次の章では、この中でも「データを読まれにくい形にする」ための基本技術として、共通鍵暗号を見ていきます。
まずは、共通鍵暗号を「同じ鍵で閉めて、同じ鍵で開ける方式」として、やさしく整理します。
2. 共通鍵暗号を一言でいうと
前の章では、ネットショッピング、スマホ決済、クラウド保存のような身近なサービスでは、通信中や保存中のデータを守る必要があることを見ました。
ここから、この記事の中心である 共通鍵暗号 に入ります。
いきなり厳密な定義から入ると少し固くなるので、まずは日常のイメージから考えてみます。
共通鍵暗号は、一言でいうと 「同じ鍵で閉めて、同じ鍵で開ける」暗号方式 です。
たとえば、友人と自分だけが同じ合鍵を持っている箱を想像してください。
自分はその箱に手紙を入れて、合鍵で鍵を閉めます。
友人は、同じ合鍵を使って箱を開け、中の手紙を読みます。
このとき、箱を閉める鍵と、箱を開ける鍵は同じです。
この「同じ鍵を使う」という考え方が、共通鍵暗号の基本です。
| 現実のイメージ | 暗号の世界での対応 |
|---|---|
| 手紙 | 平文、つまり暗号化する前のデータ |
| 合鍵で箱を閉める | 暗号化 |
| 鍵のかかった箱 | 暗号文、つまり読みにくい形になったデータ |
| 同じ合鍵で箱を開ける | 復号 |
| 合鍵 | 共通鍵、または秘密鍵 |
暗号の用語で書くと、送る側は 平文 を 鍵 で暗号化して 暗号文 にします。
受け取る側は、同じ鍵を使って暗号文を復号し、元の平文に戻します。
この図で大切なのは、暗号化と復号の両方で 同じ鍵 を使っている点です。
2.1 「共通」という言葉が少し分かりにくい
「共通鍵暗号」という名前を初めて見ると、「共通」という言葉が少し分かりにくいかもしれません。
ここでいう「共通」は、誰でも使ってよい公開情報という意味ではありません。
むしろ逆で、通信する相手同士だけが共通して持っている秘密の鍵 という意味です。
たとえば、次のように考えると分かりやすいです。
| 表現 | 意味 |
|---|---|
| 共通鍵 | 送信者と受信者が共通して持つ秘密の鍵 |
| 秘密鍵 | 第三者に知られてはいけない鍵 |
| 公開鍵 | 他人に公開してもよい鍵 |
共通鍵暗号で使う鍵は、「共通」と言っても、全員に配ってよいものではありません。
鍵を知っている人は、暗号文を復号できる可能性があります。
そのため、共通鍵は慎重に扱う必要があります。
ここは、後で扱う公開鍵暗号との大きな違いです。
公開鍵暗号では、公開してよい鍵と、秘密にする鍵を分けます。
一方、共通鍵暗号では、暗号化する側と復号する側が同じ秘密を持ちます。
💡 豆知識
共通鍵暗号は英語で symmetric-key cryptography や symmetric encryption と呼ばれます。
symmetric は「対称」という意味です。
暗号化と復号で同じ種類の秘密鍵を使うため、「対称鍵暗号」と呼ばれることもあります。
2.2 なぜ共通鍵暗号がよく使われるのか
共通鍵暗号がよく使われる大きな理由は、大量のデータを効率よく処理しやすい ことです。
Webページを開くとき、画像、文章、Cookie、APIのレスポンスなど、さまざまなデータがやり取りされます。
クラウドにファイルを保存するときも、数KBのメモから数GBの動画まで、いろいろな大きさのデータを扱います。
このようなデータを毎回重い処理で守っていると、サービスの動きが遅くなってしまいます。
そこで、実際の通信では、最初に通信相手と安全に鍵を準備し、その後のデータ通信では高速に処理しやすい共通鍵暗号系の技術を使う、という組み合わせがよく使われます。
TLS 1.3でも、通信で使う暗号スイートには、AES-GCMやChaCha20-Poly1305のような認証付き暗号が含まれます。
参考: RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3
ここでは、TLSの細かいハンドシェイクを覚える必要はありません。
まずは、次のように捉えると分かりやすいです。
このように、共通鍵暗号は「安全な通信を始めるための準備」ではなく、準備した鍵を使って 実際のデータを守る部分 で活躍します。
2.3 ただし、鍵をどう渡すかが難しい
共通鍵暗号は便利ですが、大きな課題もあります。
それは、同じ鍵を相手にどうやって安全に渡すのか という問題です。
先ほどの合鍵の例で考えてみます。
友人と同じ合鍵を持っていれば、鍵付きの箱で手紙をやり取りできます。
しかし、最初にその合鍵を友人へ渡す必要があります。
もし合鍵を封筒に入れて普通に郵送し、その途中で誰かにコピーされてしまったらどうでしょうか。
その人も箱を開けられるようになってしまいます。
共通鍵暗号でも同じです。
暗号化と復号に使う鍵が第三者に知られると、その鍵で守られていたデータを読まれる可能性があります。
| 共通鍵暗号の良い点 | 共通鍵暗号の難しい点 |
|---|---|
| 大量データを効率よく処理しやすい | 鍵を安全に共有する必要がある |
| 通信や保存データの保護に向いている | 鍵が漏れると影響が大きい |
| 実サービスで広く使われている | 鍵の生成・保存・更新・破棄まで考える必要がある |
この「鍵をどう安全に共有するか」という問題を解決するために、実際の通信では、公開鍵暗号や鍵共有の仕組みが組み合わされます。
つまり、共通鍵暗号は単独で完結する技術というより、他の暗号技術と組み合わせて使われることが多い技術です。
2.4 小さなコードで雰囲気を確認する
ここまでの説明だけだと、まだ少し抽象的かもしれません。
そこで、Pythonの cryptography ライブラリを使って、同じ鍵で暗号化・復号する流れを最小限のコードで見てみます。
ただし、ここでのコードは 共通鍵暗号のイメージをつかむための学習用サンプル です。
実務で暗号処理を使う場合は、鍵管理、エラー処理、ログ、鍵の更新、保存場所、脅威モデルなどを別途しっかり設計する必要があります。
また、この例ではAES-GCMを使います。
AES-GCMは後の章で詳しく扱いますが、ここでは「同じ鍵で暗号化し、同じ鍵で復号する」という流れを見るために使います。
cryptography ライブラリのAESGCMドキュメントでは、NISTが96ビットIVを推奨していること、また同じ鍵でnonceを再利用してはいけないことが説明されています。
参考: cryptography - Authenticated encryption
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.exceptions import InvalidTag
import os
# 暗号化したい元のデータです。
# 暗号化する前の読めるデータを「平文」と呼びます。
plaintext = "これは共通鍵暗号の動きを確認するための短いメッセージです。".encode("utf-8")
# AADは「暗号化はしないが、改ざんされていないか確認したい関連データ」です。
# 詳しくは後のAEADの章で扱います。
aad = "article=qiita-demo".encode("utf-8")
# AES用の共通鍵を生成します。
# この例では128ビット鍵を使っています。
# 実務では、鍵をどこで生成し、どこに保存し、どう更新するかまで設計が必要です。
key = AESGCM.generate_key(bit_length=128)
# 生成した共通鍵を使って、AES-GCMの処理を行うオブジェクトを作ります。
aesgcm = AESGCM(key)
# nonceは暗号処理ごとに使う補助的な値です。
# AES-GCMでは、同じ鍵のもとで同じnonceを使い回してはいけません。
# ここでは推奨される96ビット、つまり12バイトのランダム値を使っています。
nonce = os.urandom(12)
# 平文を暗号化します。
# 戻り値には暗号文と認証タグが含まれます。
ciphertext = aesgcm.encrypt(nonce, plaintext, aad)
# 同じ鍵、同じnonce、同じAADを使って復号します。
# 認証タグの検証にも成功すれば、元の平文を取り出せます。
decrypted = aesgcm.decrypt(nonce, ciphertext, aad)
print(decrypted.decode("utf-8"))
# ここからは、暗号文が途中で書き換えられた場合の確認です。
# 先頭1バイトを少しだけ変更し、改ざんされた暗号文を作ります。
tampered = bytearray(ciphertext)
tampered[0] ^= 1
try:
# 改ざんされた暗号文を復号しようとします。
# AES-GCMでは認証タグの検証に失敗するため、例外が発生します。
aesgcm.decrypt(nonce, bytes(tampered), aad)
except InvalidTag:
print("改ざん検知: 認証タグの検証に失敗しました。")
実行すると、たとえば次のような出力になります。
これは共通鍵暗号の動きを確認するための短いメッセージです。
改ざん検知: 認証タグの検証に失敗しました。
このコードから読み取ってほしいポイントは、次の3つです。
| 見るポイント | コード上の対応 |
|---|---|
| 同じ鍵で暗号化・復号する |
key を使って encrypt() と decrypt() を行う |
| nonceを使う |
nonce = os.urandom(12) で暗号処理ごとの値を用意する |
| 改ざんを検知する | 暗号文を少し変えると InvalidTag が発生する |
ここでは、暗号化と復号の流れを見せるためにAES-GCMを使いました。
ただし、AES-GCMの本当に重要な点は、「暗号化できること」だけではありません。
暗号文が途中で書き換えられていないかを、認証タグによって確認できる点も重要です。
この話は、後の「認証付き暗号」や「AEAD」の章で詳しく整理します。
2.5 共通鍵暗号を理解するときの最初のポイント
ここまでの内容をまとめると、共通鍵暗号を最初に理解するときのポイントは次のとおりです。
| ポイント | 説明 |
|---|---|
| 同じ鍵を使う | 暗号化と復号に同じ秘密鍵を使う |
| 大量データに向いている | 通信内容や保存データを効率よく守りやすい |
| 鍵の共有が課題 | 同じ鍵を相手に安全に渡す必要がある |
| 鍵管理が重要 | 鍵が漏れると安全性が崩れる可能性がある |
| 暗号化だけでは不十分な場合がある | 改ざん検知には認証付き暗号などが関係する |
共通鍵暗号は、暗号技術の中でもかなり基本的な道具です。
しかし、基本的だからといって簡単に扱ってよいわけではありません。
特に、鍵の扱いを間違えると、どれだけ強い暗号方式を使っていても安全性が大きく下がります。
また、暗号化だけでは「途中で書き換えられていないこと」までは十分に確認できない場合があります。
次の章では、共通鍵暗号が具体的にどのような場面で使われるのかを整理します。
そのうえで、代表的な方式であるAESへ進んでいきます。
3. 共通鍵暗号はどこで使われるのか
前の章では、共通鍵暗号を「同じ鍵で閉めて、同じ鍵で開ける方式」として整理しました。
ここまでで、共通鍵暗号の基本イメージはつかめたと思います。
では、この共通鍵暗号は、実際にはどこで使われているのでしょうか。
結論から言うと、共通鍵暗号は 通信中のデータ と 保存中のデータ の両方で使われます。
たとえば、ブラウザでWebサイトを見るとき、メッセージアプリで文章を送るとき、クラウドにファイルを保存するときなど、私たちが普段意識しない場所で活躍しています。
ただし、ここでも大切なのは「共通鍵暗号だけで全部を守っている」と考えないことです。
実際のサービスでは、共通鍵暗号、鍵共有、証明書、MAC、認証付き暗号、鍵管理などが組み合わされています。
この章では、共通鍵暗号が使われる代表的な場面を、身近な例から整理していきます。
3.1 まず全体像を見る
共通鍵暗号が関係する場面をざっくり整理すると、次のようになります。
| 利用場面 | 守りたいデータ | 共通鍵暗号の役割 | 一緒に考えること |
|---|---|---|---|
| HTTPS通信 | Webページ、ログイン情報、APIレスポンスなど | 通信内容を第三者に読まれにくくする | TLS、証明書、鍵共有、AEAD |
| メッセージアプリ | 送信メッセージ、添付ファイルなど | メッセージ内容を読まれにくくする | 鍵の配布、端末管理、認証 |
| クラウド保存 | アップロードしたファイル、バックアップなど | 保存データを読まれにくくする | 鍵管理、アクセス制御、復旧手順 |
| データベース・バックアップ | 顧客情報、設定情報、ログなど | 保存中の機密情報を保護する | どの層で暗号化するか、鍵をどこに置くか |
| サーバー間API通信 | リクエスト本文、レスポンス、トークンなど | 通信内容を保護する | TLS、HMAC、署名、リプレイ対策 |
| IoT・組込み機器 | センサー値、制御命令、設定情報など | 制約のある環境でデータを守る | 軽量暗号、鍵更新、物理的な攻撃 |
図にすると、共通鍵暗号は次のように「データを読まれにくくする」場面で広く関係します。
この図で見てほしいのは、共通鍵暗号が「特定のアプリだけで使われる技術」ではないという点です。
通信、保存、バックアップ、API連携など、データを扱う多くの場所で関係します。
一方で、どの場面でも同じように使えばよいわけではありません。
通信で使う場合と、保存データで使う場合では、注意すべき点が少し変わります。
3.2 HTTPS/TLS:通信中のデータを守る
まず分かりやすい例が、HTTPS通信です。
ブラウザで https:// から始まるWebサイトにアクセスすると、ブラウザとサーバーの間でTLSによる通信保護が行われます。
TLS 1.3を定めるRFC 8446では、TLSはクライアントとサーバーがインターネット上で通信するときに、盗聴、改ざん、メッセージ偽造を防ぐように設計されていると説明されています。
参考: RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3
ここで共通鍵暗号が関係するのは、主に 通信内容そのものを守る部分 です。
TLSでは、最初に通信相手の確認や鍵共有を行い、その後の通信データを保護します。
RFC 8446でも、TLSはハンドシェイクプロトコルで通信相手の認証、暗号方式の交渉、共有鍵材料の確立を行い、レコードプロトコルで確立されたパラメータを使って通信を保護すると説明されています。
参考: RFC 8446 - 1. Introduction
かなり簡略化すると、次のような流れです。
ここでのポイントは、公開鍵暗号だけで通信全体を守っているわけではないことです。
公開鍵暗号や鍵共有は、安全に通信を始めるための準備で重要です。
一方、実際に大量のデータをやり取りする部分では、共通鍵系の暗号技術が重要になります。
TLS 1.3の暗号スイートでは、AEADアルゴリズムとHKDFで使うハッシュの組み合わせが示されます。
たとえば、RFC 8446では TLS_AES_128_GCM_SHA256、TLS_AES_256_GCM_SHA384、TLS_CHACHA20_POLY1305_SHA256 などが定義されています。
参考: RFC 8446 - Appendix B.4 Cipher Suites
ここで出てくる AES-GCM や ChaCha20-Poly1305 は、単に中身を隠すだけでなく、改ざん検知も一緒に行う認証付き暗号です。
このあたりは、後の章で詳しく整理します。
💡 豆知識
HTTPSの裏側では、「公開鍵暗号で全部を暗号化している」と思われることがあります。
しかし実際には、公開鍵暗号や鍵共有で安全に会話を始め、その後のデータ通信では共通鍵系の暗号が活躍します。
ざっくり言うと、公開鍵暗号は“安全な会話を始めるための準備役”、共通鍵暗号は“会話の中身を効率よく守る実務担当”のような関係です。
3.3 クラウド保存・ファイル暗号化:保存中のデータを守る
次に、保存中のデータを考えます。
クラウドストレージにファイルを保存する。
PCのディスクを暗号化する。
データベースのバックアップを保護する。
このような場面でも、共通鍵暗号は重要です。
通信中のデータは、ネットワークを通っている間に読まれないように守る必要があります。
一方、保存中のデータは、ディスク、クラウドストレージ、バックアップ媒体などに置かれている間に、勝手に読まれないように守る必要があります。
| 状態 | 例 | 守りたいこと |
|---|---|---|
| 通信中のデータ | ブラウザとWebサーバー間の通信 | 通信経路で盗み見られにくくする |
| 保存中のデータ | クラウド上のファイル、DB、バックアップ | 保存場所から漏えいしたときの被害を抑える |
OWASP Cryptographic Storage Cheat Sheetでは、暗号化を行う層として、アプリケーション、データベース、ファイルシステム、ハードウェアなど複数の選択肢があると説明されています。
また、どの層で暗号化するのが適切かは、脅威モデル、つまり「何から守りたいのか」によって変わるとされています。
参考: OWASP Cryptographic Storage Cheat Sheet - Where to Perform Encryption
たとえば、ノートPCを落としてしまった場合に備えるなら、ディスク全体の暗号化が役立つ場合があります。
一方、アプリケーションの不具合や権限設定ミスによる情報漏えいを考えるなら、アプリケーション側でどのデータを暗号化するかを設計する必要があります。
つまり、保存データの暗号化では「暗号化するかどうか」だけでなく、どこで暗号化するのか が重要です。
💡 豆知識
「暗号化して保存しています」と聞くと、それだけで安心に見えるかもしれません。
しかし、暗号化されたデータと復号用の鍵が同じ場所に無防備に置かれていると、鍵ごと持っていかれる可能性があります。
金庫に大事な書類を入れても、金庫の鍵を扉に貼り付けていたら意味が薄い、というイメージです。
小さなファイル暗号化の例
ここで、保存中のデータを守るイメージとして、小さなテキストファイルをAES-GCMで暗号化して保存する例を見てみます。
このコードは、実務用の完成版ではありません。
実務では、鍵をどこに保存するか、誰が復号できるか、鍵をどう更新するか、復号失敗時にどう扱うかなどを別途設計する必要があります。
from pathlib import Path
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
# 暗号化したいファイルを読み込みます。
# ここでは例として memo.txt という小さなテキストファイルを想定します。
plaintext = Path("memo.txt").read_bytes()
# AES-GCMで使う共通鍵を生成します。
# この例では256ビット鍵を使っています。
# 実務では、この鍵をファイルにそのまま保存せず、KMSや秘密情報管理の仕組みを検討します。
key = AESGCM.generate_key(bit_length=256)
# 生成した鍵を使ってAES-GCMの処理オブジェクトを作ります。
aesgcm = AESGCM(key)
# nonceは暗号化処理ごとに使う値です。
# AES-GCMでは、同じ鍵で同じnonceを再利用してはいけません。
nonce = os.urandom(12)
# AADは、暗号化しないが改ざん検知の対象にしたい関連データです。
# ここでは例として、ファイル名を関連データとして扱っています。
aad = b"filename=memo.txt"
# ファイルの中身を暗号化します。
# 戻り値には暗号文と認証タグが含まれます。
ciphertext = aesgcm.encrypt(nonce, plaintext, aad)
# 復号に必要なnonceと暗号文をまとめて保存します。
# 実際の形式は、利用するライブラリや設計方針によって変わります。
Path("memo.txt.enc").write_bytes(nonce + ciphertext)
# 学習用に、暗号化後のサイズを表示します。
print(f"暗号化したファイルを保存しました: {len(nonce + ciphertext)} bytes")
この例では、memo.txt の中身を暗号化し、memo.txt.enc として保存しています。
ただし、ここで生成した key を安全に保存していなければ、後から復号できません。
逆に、鍵を雑に保存してしまうと、暗号化していても第三者に読まれる可能性があります。
つまり、保存データの暗号化では、暗号方式だけでなく 鍵管理 がとても重要です。
3.4 サーバー間API通信:TLSだけで十分かを考える
Webサービスでは、ユーザーのブラウザとサーバーだけでなく、サーバー同士がAPIで通信する場面も多くあります。
たとえば、次のような連携です。
- 自社サービスが決済サービスのAPIを呼び出す
- アプリケーションサーバーが認証基盤に問い合わせる
- バックエンドが外部のクラウドAPIへリクエストを送る
- 複数の社内システムがデータをやり取りする
このようなAPI通信でも、通信経路を保護するためにTLSが使われることが多いです。
ただし、APIの設計によっては、TLSだけでなく、メッセージ単位の検証を追加することがあります。
たとえば、「このリクエストは本当に正しい相手から来たのか」「途中で本文が書き換えられていないか」「過去のリクエストをコピーして再送していないか」を確認したい場合です。
| 確認したいこと | 使われることがある考え方 |
|---|---|
| 通信経路で読まれにくくしたい | TLS、共通鍵系の暗号 |
| リクエスト本文が改ざんされていないか確認したい | HMAC、デジタル署名、AEAD |
| 過去のリクエストの再送を防ぎたい | タイムスタンプ、nonce、リクエストID |
| 正しい相手から来たか確認したい | APIキー、HMAC、署名、mTLSなど |
ここで出てくるHMACや署名は、共通鍵暗号そのものとは別の技術です。
しかし、実サービスでは「通信を暗号化する」「メッセージを検証する」「鍵を管理する」といった複数の仕組みを組み合わせます。
そのため、共通鍵暗号を学ぶときも、API通信のような利用場面を意識すると理解しやすくなります。
3.5 メッセージアプリ:便利に見える裏側で鍵を扱っている
メッセージアプリも、共通鍵暗号の利用場面としてイメージしやすい例です。
利用者から見ると、文章を入力して送信ボタンを押すだけです。
しかし裏側では、メッセージ本文、画像、動画、通話データなどをどう守るかが問題になります。
ここでも、共通鍵暗号は大量データを効率よく暗号化する部分で重要になります。
特に、画像や動画のような大きなデータを扱う場合、効率よく処理できることは大切です。
ただし、メッセージアプリの安全性は、共通鍵暗号だけでは決まりません。
| 観点 | 考えること |
|---|---|
| メッセージ本文 | 誰が読める状態にするのか |
| 鍵の共有 | 相手端末とどのように鍵を準備するのか |
| 端末の管理 | 端末を紛失した場合どうするのか |
| バックアップ | クラウドバックアップも暗号化するのか |
| なりすまし対策 | 本当に相手の端末とやり取りしているのか |
このように、共通鍵暗号は「中身を読まれにくくする」ための重要な部品ですが、アプリ全体の安全性は、鍵共有、認証、端末管理、バックアップ設計などによっても変わります。
3.6 共通鍵暗号が向いている場面・向いていない場面
ここまで見ると、共通鍵暗号はとても便利な技術に見えます。
実際、大量データを効率よく守る場面では非常に重要です。
ただし、すべての場面にそのまま向いているわけではありません。
| 観点 | 向いていること | 注意が必要なこと |
|---|---|---|
| 処理効率 | 大量のデータを暗号化しやすい | 鍵を安全に共有する仕組みが別途必要 |
| 通信保護 | TLSなどで通信内容を守る部品になる | 相手確認や鍵共有は別の仕組みと組み合わせる |
| 保存データ保護 | ファイルやバックアップを暗号化できる | 鍵を失うと復号できない。鍵が漏れると読まれる可能性がある |
| 改ざん検知 | AEADを使えば改ざん検知も扱える | 単なる暗号化モードだけでは不十分な場合がある |
| 多人数への配布 | 同じ鍵で効率よく処理できる | 鍵を持つ人が増えるほど管理が難しくなる |
特に注意したいのは、共通鍵暗号では 鍵を知っている人が強い権限を持つ という点です。
鍵を知っている人は、暗号文を復号できたり、場合によっては正しい暗号文を作れたりします。
そのため、誰に鍵を渡すのか、鍵をどこに保存するのか、退職者や不要になった端末からどう鍵を無効化するのか、といった運用上の設計が重要になります。
NIST SP 800-57 Part 1 Rev.5は、暗号鍵管理に関する一般的なガイダンスとベストプラクティスを扱っており、鍵材料の管理や鍵を保護する方法などを整理しています。
参考: NIST SP 800-57 Part 1 Rev.5 - Recommendation for Key Management
3.7 この章のまとめ
この章では、共通鍵暗号がどのような場面で使われるのかを見てきました。
共通鍵暗号は、HTTPS通信、クラウド保存、ファイル暗号化、API通信、メッセージアプリなど、さまざまな場面で関係します。
特に、大量のデータを効率よく読めない形にするための基本技術として重要です。
一方で、実際のサービスでは、共通鍵暗号だけで安全が完成するわけではありません。
- 通信相手をどう確認するか
- 共通鍵をどう安全に準備するか
- 暗号文が改ざんされていないことをどう確認するか
- 鍵をどこに保存し、どう更新・失効するか
- どの暗号方式・モードを使うか
こうした点まで含めて考える必要があります。
次の章では、共通鍵暗号の代表的な方式である AES を見ていきます。
AESは現在広く使われる基本的な共通鍵暗号ですが、「AESという名前を知っている」だけでは十分ではありません。
まずは、AESがどのような位置づけの方式なのかを整理します。
4. AESとは何か
前の章では、共通鍵暗号がHTTPS通信、クラウド保存、ファイル暗号化、API通信など、さまざまな場面で使われることを見ました。
ここからは、その代表例として AES を見ていきます。
AESは、共通鍵暗号を学ぶときにかなり高い確率で出てくる名前です。
ただ、最初に注意したいのは、AESを「なんとなく強そうな暗号」として覚えるだけでは不十分だということです。
AESはとても重要な方式ですが、実際に安全に使うには、暗号利用モード、nonce / IV、認証タグ、鍵管理なども一緒に考える必要があります。
この章ではまず、AESそのものがどのような位置づけの暗号方式なのかを整理します。
4.1 AESは代表的な共通鍵ブロック暗号
AESは Advanced Encryption Standard の略です。
日本語では「高度暗号化標準」と訳されることがあります。
AESは、NISTの FIPS 197 として標準化されている共通鍵暗号です。
FIPS 197では、AES-128、AES-192、AES-256の3種類が規定されており、それぞれ128ビット、192ビット、256ビットの鍵を使って、128ビットのデータブロックを暗号化・復号します。
参考: NIST FIPS 197 - Advanced Encryption Standard (AES)
ここで出てくる ブロック という言葉は、データを一定の大きさに区切ったまとまりのことです。
AESは、任意の長さの文章やファイルをそのまま一気に変換するというより、まずは 128ビット、つまり16バイトの固定長ブロックを変換する部品 として理解すると分かりやすいです。
たとえるなら、AESは「16バイトずつ入れられる変換装置」のようなものです。
鍵を使って、その16バイトの入力を、別の16バイトの出力へ変換します。
この図では1ブロックだけを描いています。
しかし実際のファイルや通信データは、16バイトぴったりとは限りません。
そのため、AESを実データに使うには、複数のブロックをどのようにつなげて処理するかを決める必要があります。
この話が、後の章で扱う 暗号利用モード につながります。
4.2 AES-128、AES-192、AES-256の違い
AESについて調べると、AES-128、AES-192、AES-256という名前が出てきます。
この数字を見ると、「256の方がブロックサイズが大きいのかな」と思うかもしれません。
しかし、この数字は ブロックサイズではなく鍵長 を表しています。
AESでは、ブロックサイズはどれも128ビットです。
違うのは、暗号化・復号に使う鍵の長さです。
| 方式 | 鍵長 | バイト数 | ブロックサイズ | ラウンド数 |
|---|---|---|---|---|
| AES-128 | 128ビット | 16バイト | 128ビット | 10 |
| AES-192 | 192ビット | 24バイト | 128ビット | 12 |
| AES-256 | 256ビット | 32バイト | 128ビット | 14 |
ラウンド数とは、AESの内部で繰り返される変換処理の回数のようなものです。
鍵が長くなるほど、ラウンド数も増えます。
ここで大切なのは、AES-256の「256」は、1回に処理するデータの大きさではなく、鍵の長さを示している という点です。
💡 豆知識
「AES-256は256ビットずつデータを処理する方式」と誤解されることがあります。
しかし、AESのブロックサイズはAES-128でもAES-256でも128ビットです。
AES-256の256は鍵長を表しています。
4.3 AESの中では何をしているのか
AESの内部では、データに対していくつかの変換を繰り返します。
ここでは数式までは追いませんが、「中で何となく混ぜている」ではなく、決まった手順で変換していることを押さえておくと、後でモードや実装上の注意を理解しやすくなります。
AESの1ラウンドでは、代表的に次のような処理が使われます。
| 処理 | ざっくりした役割 | かなりやさしいイメージ |
|---|---|---|
| SubBytes | バイトを別の値に置き換える | 文字ごとに別の文字へ置き換える表を見る |
| ShiftRows | 行をずらす | 表の行を横にずらして並びを変える |
| MixColumns | 列方向に混ぜる | 列ごとの値を混ぜ合わせる |
| AddRoundKey | ラウンド鍵を加える | その段階専用の鍵を混ぜる |
AESでは、元の鍵から各ラウンドで使う鍵を作り、それを使いながら上のような変換を繰り返します。
この「鍵からラウンドごとの鍵を作る処理」は、鍵スケジュール と呼ばれます。
この図は、AESの内部処理をかなり簡略化したものです。
実際には、最終ラウンドでは一部の処理が異なるなど細かいルールがあります。
ただ、初学者の段階では、まず次の理解で十分です。
AESは、鍵を使って128ビットのブロックに対し、置換・並べ替え・混合・鍵の追加を決められた回数繰り返すブロック暗号である。
ここまでを理解しておくと、「AESそのもの」と「AESを実際の通信やファイルに使う方法」を分けて考えやすくなります。
4.4 小さなコードで鍵長の違いを確認する
AESの内部アルゴリズムを自力で実装するのはおすすめしません。
暗号処理は、少しの実装ミスが大きな脆弱性につながる可能性があるためです。
ただし、AES-128、AES-192、AES-256の違いが「鍵長」であることは、短いPythonコードでも確認できます。
ここでは、暗号化そのものではなく、鍵の長さを確認するだけの学習用コードを示します。
def describe_aes_key(key: bytes) -> str:
"""AESで使える鍵長かどうかを確認する学習用関数です。
AESでは、128ビット、192ビット、256ビットの鍵を使います。
ここでは暗号化は行わず、鍵の長さだけを確認します。
"""
# bytesの長さをビット数に変換します。
# 1バイトは8ビットなので、バイト数に8を掛けます。
bit_length = len(key) * 8
# AESで使える鍵長は、128 / 192 / 256ビットです。
if bit_length not in (128, 192, 256):
raise ValueError("AESの鍵長は128、192、256ビットのいずれかである必要があります。")
# 鍵長に応じて、AES-128 / AES-192 / AES-256の名前を返します。
return f"AES-{bit_length} 用の鍵です。"
# 16バイト = 128ビットなので、AES-128用の鍵として扱えます。
key_128 = b"A" * 16
print(describe_aes_key(key_128))
# 24バイト = 192ビットなので、AES-192用の鍵として扱えます。
key_192 = b"B" * 24
print(describe_aes_key(key_192))
# 32バイト = 256ビットなので、AES-256用の鍵として扱えます。
key_256 = b"C" * 32
print(describe_aes_key(key_256))
実行すると、次のような出力になります。
AES-128 用の鍵です。
AES-192 用の鍵です。
AES-256 用の鍵です。
このコードは、AESの安全な使い方を示すものではありません。
あくまで、AES-128、AES-192、AES-256の数字が鍵長を表していることを確認するためのものです。
実際に暗号化を行う場合は、前の章で使った AESGCM のように、標準的なライブラリが提供する高水準のAPIを使うことを基本にします。
そして、鍵をソースコードに直書きしない、nonceを使い回さない、認証タグを検証する、といった注意点を守る必要があります。
4.5 AESはどのように標準になったのか
少しだけ歴史にも触れておきます。
ただし、ここでは年表を暗記することが目的ではありません。
AESが「なんとなく有名だから使われている」のではなく、標準化の流れを経て広く使われるようになった方式であることを押さえるためです。
NISTのFIPS 197のページでは、2000年にNISTが Rijndael というブロック暗号ファミリーをAES競争の勝者として選定したことが説明されています。
その後、AESはFIPS 197として標準化されました。
参考: NIST FIPS 197 - Advanced Encryption Standard (AES)
| 年 | 出来事 | この記事での見方 |
|---|---|---|
| 2000年 | NISTがRijndaelをAESとして選定 | AESは選定プロセスを経た標準方式 |
| 2001年 | FIPS 197としてAESが公開 | AES-128 / AES-192 / AES-256が標準化 |
| 2023年 | FIPS 197が更新 | アルゴリズム変更ではなく、説明・図表などの編集改善 |
NISTは2023年のFIPS 197更新について、標準で規定されているアルゴリズムへの技術的変更はないと説明しています。
参考: NIST - NIST Updates FIPS 197, Advanced Encryption Standard
💡 豆知識
AESのもとになったRijndaelは、「ラインダール」のように読まれることがあります。
名前だけ見ると少し不思議ですが、提案者であるJoan Daemen氏とVincent Rijmen氏の名前に由来します。
ただし、読み方や由来を覚えることよりも、AESが標準化された共通鍵ブロック暗号であることを押さえる方が大切です。
また、日本国内で暗号技術を調べるときは、CRYPTREC暗号リストも参考になります。
CRYPTREC暗号リストは、電子政府システムの調達などで参照される暗号リストであり、電子政府推奨暗号リスト、推奨候補暗号リスト、運用監視暗号リストから構成されています。
参考: CRYPTREC暗号リスト仕様
4.6 AESを理解するときに気をつけたいこと
ここまでで、AESが代表的な共通鍵ブロック暗号であり、AES-128、AES-192、AES-256は鍵長の違いであることを見ました。
また、AESは128ビットのブロックを処理する方式であり、内部では決められた変換をラウンドとして繰り返すことも確認しました。
ただし、ここで止まってしまうと、少し危険です。
AESは重要な暗号方式ですが、AESという名前だけで安全性が決まるわけではありません。
実際のデータは128ビットぴったりではないため、どのようなモードで処理するかが必要になります。
また、暗号化だけでは、データが途中で書き換えられていないことを十分に確認できない場合があります。
たとえるなら、AESは信頼できる包丁のようなものです。
包丁そのものが良くても、使い方を間違えると安全な料理にはなりません。
暗号でも同じで、標準的な方式を選ぶだけでなく、モード、鍵管理、nonce / IV、認証タグの検証などを含めて考える必要があります。
次の章では、まさにこの点を扱います。
つまり、AESだけを知っていても十分ではない理由 を整理していきます。
5. AESだけを知っていても十分ではない理由
前の章では、AESが代表的な共通鍵ブロック暗号であり、NIST FIPS 197として標準化されていることを見ました。
ここまで読むと、「それならAESを使えば安全なのでは?」と思うかもしれません。
もちろん、AESは現在の共通鍵暗号を学ぶうえで非常に重要な方式です。
しかし、実際のシステムでは AESという名前だけで安全性が決まるわけではありません。
たとえるなら、AESは信頼できる「頑丈な金庫」や「よく切れる包丁」のようなものです。
道具そのものが優れていても、使い方を間違えると期待した安全性は得られません。
暗号でも同じです。
AESを使う場合でも、次のようなことを一緒に考える必要があります。
| 一緒に考えること | なぜ必要か | ざっくりしたイメージ |
|---|---|---|
| 暗号利用モード | AESは128ビット単位で処理するため、実データをどう分割・連結して扱うかが必要 | 包丁そのものではなく、料理の手順 |
| IV / nonce | 同じ鍵で暗号化するときに、毎回の処理を区別するために使う | 使い回さない整理番号 |
| 改ざん検知 | 暗号化だけでは、書き換えられていないことを十分に確認できない場合がある | 封筒に付ける封印 |
| 認証タグ | 認証付き暗号で、改ざんされていないか確認するための値 | 開封前に確認するチェック印 |
| 鍵管理 | 鍵が漏えいすると、暗号化したデータを守れなくなる可能性がある | 金庫の鍵をどこに保管するか |
この章では、AESを安全に使うために、なぜこれらの考え方が必要になるのかを整理します。
5.1 AESは「部品」であって、使い方まで自動で決めてくれるわけではない
AESは、128ビットのブロックを入力として受け取り、同じく128ビットのブロックを出力するブロック暗号です。
つまり、AESそのものは「決まった大きさのかたまりを、鍵を使って別の形に変換する部品」と見ることができます。
しかし、実際に暗号化したいデータは、128ビットちょうどとは限りません。
短いメッセージもあれば、数MBのファイルもあります。
Web通信のように、細かいデータが連続して流れる場面もあります。
そのため、AESを実際のデータに使うには、次のような疑問に答える必要があります。
- 128ビットより長いデータをどう分けるのか
- 最後のブロックが128ビットに満たないときはどう扱うのか
- 同じ内容を暗号化したときに、毎回同じ暗号文になってよいのか
- 暗号文が途中で書き換えられていないか、どう確認するのか
このような「AESをどのように使うか」を決める考え方が、次章で扱う 暗号利用モード につながります。
NIST SP 800-38Aでは、ブロック暗号を使う秘匿用モードとして、ECB、CBC、CFB、OFB、CTRが定義されています。
参考: NIST SP 800-38A - Recommendation for Block Cipher Modes of Operation
ただし、ここで出てくるモードを名前だけ覚えても、まだ十分ではありません。
特に重要なのは、モードによって「守れること」と「守れないこと」が違うという点です。
5.2 同じ内容が同じ暗号文になると、パターンが見えてしまう
暗号化では、平文の中身を読めなくすることが重要です。
しかし、ただ読めなくするだけではなく、元データのパターンを見えにくくすること も大切です。
たとえば、同じ金額、同じ商品ID、同じステータスのような値が何度も出てくるデータを考えます。
もし同じ平文ブロックが毎回同じ暗号文ブロックに変換されると、攻撃者は中身を読めなくても「ここは同じ内容が繰り返されていそうだ」と推測できる可能性があります。
この問題を説明するときによく出てくるのが ECBモード です。
ECBは、ブロックごとに独立して暗号化する単純なモードです。
仕組みは分かりやすい一方で、同じ平文ブロックが同じ暗号文ブロックになってしまうため、パターンが残りやすいという問題があります。
OWASP Cryptographic Storage Cheat Sheetでも、ECBは非常に限定的な状況を除いて使うべきではないと説明されています。
参考: OWASP Cryptographic Storage Cheat Sheet
次のコードは、ECBモードの危険性を雰囲気として理解するための 学習用コード です。
実務でECBモードを使うことを推奨するものではありません。
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
# 学習用の固定鍵です。
# 実務では、鍵をソースコードに直接書かず、安全な乱数で生成し、適切に管理します。
key = b"A" * 16 # 16バイト = 128ビットなので、AES-128用の鍵になります。
# AESの1ブロックは16バイトです。
# 同じ16バイトのブロックを2回並べることで、同じ平文ブロックが繰り返される状況を作ります。
block = b"PAYMENT=1000YEN!" # 16バイトの平文ブロック
plaintext = block + block # 同じブロックを2つ並べます。
# ECBモードでAES暗号化器を作成します。
# これは危険な使い方を確認するための学習用例であり、実務利用は避けます。
cipher = Cipher(algorithms.AES(key), modes.ECB())
encryptor = cipher.encryptor()
# 平文を暗号化します。
# 今回の平文は16バイトの倍数なので、説明を簡単にするためパディング処理は省略しています。
ciphertext = encryptor.update(plaintext) + encryptor.finalize()
# 暗号文を16バイトずつに分けて、1ブロック目と2ブロック目を比較します。
first_cipher_block = ciphertext[:16]
second_cipher_block = ciphertext[16:32]
# ECBでは、同じ平文ブロックが同じ暗号文ブロックになります。
print(first_cipher_block == second_cipher_block)
出力は次のようになります。
True
この結果は、「暗号文そのものは読めないけれど、同じ内容が繰り返されていることは分かってしまう」可能性を示しています。
💡 豆知識
ECBモードの問題は、暗号の説明でよく「暗号化されたペンギン画像」の例として紹介されます。
画像の中身は暗号化されていても、同じ色や同じ模様の部分が似た暗号文になり、元画像の輪郭がうっすら残ってしまう、というイメージです。
ここで大切なのは、暗号文が一見ランダムに見えるかどうかだけでなく、元データの構造が漏れていないかを見ることです。
5.3 暗号化だけでは、改ざんされたかどうかまでは分からない
AESを使ってデータを暗号化すると、第三者から中身を読まれにくくできます。
しかし、ここで注意したいのは、暗号化と改ざん検知は同じではない という点です。
たとえば、スマホ決済で「1,000円を支払う」という情報が暗号化されていたとします。
攻撃者が中身を読めなかったとしても、暗号文の一部を壊したり、別の暗号文と差し替えたりできるなら、受信側の処理に悪影響を与える可能性があります。
もちろん、実際の安全なプロトコルではこのような問題に対処するための仕組みが組み込まれています。
ここで言いたいのは、暗号化だけを単独で見ていると、完全性や認証の観点が抜け落ちやすい ということです。
この問題に対応する代表的な考え方が、後の章で扱う 認証付き暗号 や AEAD です。
NIST SP 800-38Dでは、GCMがAEADのためのモードとして規定され、GMACは暗号化されないデータに対してMACを生成する特殊化として説明されています。
参考: NIST SP 800-38D - GCM and GMAC
また、Pythonの cryptography 公式ドキュメントでも、対称暗号は多くの用途では機密性だけでは不十分であり、真正性を確認する仕組みと組み合わせる必要があると説明されています。
参考: cryptography documentation - Symmetric encryption
整理すると、次のようになります。
| 守りたいこと | AESだけで考えた場合 | 必要になる考え方 |
|---|---|---|
| 中身を読まれないこと | 暗号化で対応する | 共通鍵暗号、暗号利用モード |
| 中身が変わっていないこと | 暗号化だけでは不十分な場合がある | MAC、認証付き暗号、AEAD |
| どのデータと紐づくか | 暗号文だけでは不足する場合がある | AAD、認証タグ |
| 鍵が漏れていないこと | AESの強さとは別問題 | 鍵管理、アクセス制御、ローテーション |
5.4 nonce / IV の扱いを間違えると、安全性が崩れることがある
AESを安全に使うときに、もう一つ大切になるのが nonce や IV です。
nonceは、ざっくり言うと「同じ鍵のもとで暗号化処理を区別するための使い捨て番号」のようなものです。
IVは Initialization Vector の略で、日本語では初期化ベクトルと呼ばれます。
厳密には、方式によってnonceとIVの意味や条件は少し異なります。
ただ、初学者向けにはまず次のように押さえると分かりやすいです。
同じ鍵で何度も暗号化するときに、毎回の暗号化を区別するための補助値が必要になる。
方式によっては、この値を使い回すと安全性が大きく崩れる。
特にAES-GCMのような方式では、同じ鍵でnonce / IVを再利用しないことが重要です。
NISTはGCMに関する説明の中で、IVの一意性が重要な要件であることを示しています。
参考: NIST Block Cipher Modes - Current Modes
ここも、日常の例で考えると少し分かりやすくなります。
宅配便の伝票番号が毎回同じだと、どの荷物の情報なのか分からなくなります。
暗号でも、毎回の処理を区別するための値を誤って使い回すと、暗号文同士の関係から情報が漏れたり、改ざん検知の前提が崩れたりする可能性があります。
5.5 鍵管理を間違えると、AESを使っていても守れない
AESのような標準的な暗号方式を使っていても、鍵の扱いを間違えると安全性は大きく下がります。
たとえば、次のような状態を考えてみます。
- 暗号鍵をソースコードに直接書いている
- GitHubなどの公開リポジトリに鍵を含めてしまう
- 本番環境と開発環境で同じ鍵を使い回している
- 鍵が漏えいしたときのローテーション手順がない
- 誰が鍵にアクセスできるのか分からない
このような場合、AES自体が強くても、鍵が漏えいすれば守りたいデータが復号される可能性があります。
NIST SP 800-57 Part 1 Rev.5は、暗号鍵管理に関する一般的なガイダンスとベストプラクティスを扱う文書です。
また、OWASP Cryptographic Storage Cheat Sheetでも、鍵の生成、保存、配布、ローテーション、廃止などを鍵管理のプロセスとして整理しています。
参考: NIST SP 800-57 Part 1 Rev.5 - Recommendation for Key Management
参考: OWASP Cryptographic Storage Cheat Sheet
ここでのポイントは、暗号技術を「アルゴリズム選び」だけで終わらせないことです。
実際には、鍵を誰が持つのか、どこに保存するのか、いつ更新するのか、漏えい時にどう無効化するのかまで含めて考える必要があります。
5.6 AESを安全に使うために一緒に見るべきもの
ここまでの内容をまとめると、AESを理解するときは、次のように周辺要素もセットで見る必要があります。
この図のように、AESは暗号技術の重要な中心部品ですが、単体で完結するものではありません。
実際に安全な通信や保存を実現するには、暗号利用モード、nonce / IV、認証タグ、鍵管理と組み合わせて考える必要があります。
5.7 この章のまとめ
この章では、AESだけを知っていても十分ではない理由を整理しました。
AESは、標準化された重要な共通鍵ブロック暗号です。
しかし、実際のデータを安全に扱うには、AESをどのモードで使うのか、nonce / IVをどう扱うのか、改ざん検知をどう行うのか、鍵をどう管理するのかまで考える必要があります。
特に重要なのは、次の3点です。
- AESは、実データを安全に扱うための「部品」であり、使い方まで自動で決めてくれるわけではない
- 暗号化だけでは、改ざんされていないことを十分に確認できない場合がある
- 鍵管理を間違えると、強い暗号方式を使っていても安全性が崩れる
次の章では、この中でも特に重要な 暗号利用モード について整理します。
AESというブロック暗号を、実際の長いデータにどう適用するのかを見ていきます。
6. 暗号利用モードとは何か
前の章では、AESを安全に使うには、AESというアルゴリズム名だけでは足りないことを見ました。
その中でも特に重要なのが、暗号利用モードです。
暗号利用モードという言葉は少し固く聞こえます。
ただ、考え方自体はそれほど難しくありません。
AESは、データを 128ビットのブロック 単位で処理するブロック暗号です。
しかし、実際に暗号化したいデータは、短いメッセージかもしれませんし、長いファイルかもしれません。
毎回ちょうど128ビットに収まるとは限りません。
そこで必要になるのが、ブロック暗号を実際のデータにどう適用するかを決めるルールです。
このルールが、暗号利用モードです。
たとえるなら、AESは「1枚ずつ紙を処理できる機械」で、暗号利用モードは「大量の紙をどの順番で、どのように機械へ通すかを決める手順書」のようなものです。
NIST SP 800-38Aでは、ブロック暗号の秘匿用モードとして、ECB、CBC、CFB、OFB、CTRが定義されています。
参考: NIST SP 800-38A - Recommendation for Block Cipher Modes of Operation
6.1 なぜモードが必要なのか
AESは、128ビットのブロックを入力として受け取り、同じく128ビットのブロックを出力します。
ここだけを見ると、次のような処理に見えます。
しかし、現実のデータは1ブロックで終わるとは限りません。
たとえば、文章、画像、PDF、ログファイル、通信データなどは、複数のブロックに分かれます。
このとき、単純に1ブロックずつAESに入れるだけでは、データのパターンが暗号文側に残ってしまう場合があります。
そこで、前のブロックの結果を次のブロックに混ぜたり、カウンタと呼ばれる値を使ったりして、同じ内容が同じ暗号文として見えにくくなるように工夫します。
ここで大切なのは、暗号利用モードは「AESとは別の暗号方式」というより、AESのようなブロック暗号を実際のデータに使うための使い方だという点です。
6.2 代表的なモードをざっくり見る
暗号利用モードにはいくつか種類があります。
この記事では、細かい数式には入らず、初学者がまず押さえたい位置づけに絞って整理します。
| モード | ざっくりした特徴 | 初学者向けの見方 |
|---|---|---|
| ECB | 各ブロックを独立に暗号化する | 同じ平文ブロックが同じ暗号文ブロックになり、パターンが残りやすい |
| CBC | 前の暗号文ブロックを次の処理に混ぜる | IVが必要。機密性用の古典的なモードとして登場する |
| CFB / OFB | ブロック暗号をストリームのように使う | 歴史的・仕様上は重要だが、初学者は名前と位置づけ程度でよい |
| CTR | カウンタ値を暗号化して、平文と組み合わせる | 並列処理しやすい。カウンタやnonceの重複に注意が必要 |
| GCM | CTRに認証機能を組み合わせた考え方 | 暗号化と改ざん検知をまとめて扱えるAEADとして重要 |
この表の中で、今後の記事の流れで特に重要なのは ECB、CTR、GCM です。
ECBは「なぜ単純な使い方が危ないのか」を理解するために重要です。
CTRは、GCMの中で使われる考え方を理解するために役立ちます。
GCMは、後で扱う認証付き暗号やAEADにつながる重要なモードです。
6.3 ECBモード:分かりやすいが、基本的には避けたい
ECBは、Electronic Codebookの略です。
各ブロックを独立に暗号化する、とても単純なモードです。
一見すると分かりやすいのですが、大きな問題があります。
同じ鍵を使って、同じ平文ブロックを暗号化すると、同じ暗号文ブロックになります。
これは、同じ模様のスタンプを何度も押すようなものです。
紙全体はインクで塗られていても、スタンプの並び方から元のパターンがなんとなく見えてしまうかもしれません。
そのため、ECBは学習用には分かりやすいものの、実際のデータ保護では基本的に避けるべきです。
OWASP Cryptographic Storage Cheat Sheetでも、ECBは非常に限定的な状況を除いて使うべきではないと説明されています。
参考: OWASP Cryptographic Storage Cheat Sheet
💡 豆知識
ECBの危険性は、暗号の入門でよく「画像をECBで暗号化すると輪郭が残る」という例で説明されます。
これは、暗号化しても同じパターンが同じ暗号文として現れやすいためです。
もちろん、実際の安全性評価は画像の見た目だけで決まるわけではありませんが、「同じものが同じ形で出ると危ない」という直感をつかむには分かりやすい例です。
6.4 CBCモード:前の結果を次に混ぜる
CBCは、Cipher Block Chainingの略です。
日本語にすると「ブロックを鎖のようにつなげる」イメージに近いです。
CBCでは、前の暗号文ブロックを次の平文ブロックに混ぜてからAESに入れます。
最初のブロックには前の暗号文がないため、IVという初期値を使います。
この仕組みによって、同じ平文ブロックが出てきても、前後の流れによって暗号文が変わりやすくなります。
ECBよりも実データ向けの考え方になっています。
ただし、CBCだけで「改ざんされていないこと」まで確認できるわけではありません。
CBCは主に機密性を守るためのモードとして理解するのがよいです。
また、実務ではCBCを単独で使うのではなく、MACなどによる認証と組み合わせる必要があります。
OWASP Cryptographic Storage Cheat Sheetでも、GCMやCCMが使えない場合にCTRやCBCを使うなら、それらはデータの真正性を保証しないため、Encrypt-then-MACのような認証を別途実装する必要があると説明されています。
参考: OWASP Cryptographic Storage Cheat Sheet
6.5 CTRモード:カウンタから鍵ストリームを作る
CTRは、Counter modeの略です。
名前の通り、カウンタと呼ばれる値を使います。
CTRでは、AESで平文そのものを直接暗号化するというより、nonceやカウンタから作った値をAESに入れ、その出力を平文と組み合わせて暗号文を作ります。
かなりざっくり図にすると、次のようなイメージです。
ここで出てくる「組み合わせる」は、実際にはXORというビット演算です。
XORは、同じ値をもう一度組み合わせると元に戻る性質があるため、暗号化と復号の両方で使えます。
学習用に、XORの雰囲気だけを短いPythonコードで確認してみます。
これは暗号として安全な実装ではなく、CTRの内部で「平文と鍵ストリームを組み合わせる」という考え方を直感的に見るための例です。
# 学習用の例です。実際の暗号実装として使わないでください。
# 平文をバイト列として用意します。
plaintext = b"HELLO"
# 本物の暗号では、これはAESなどから安全に生成される鍵ストリームです。
# ここでは仕組みの説明のため、固定のバイト列を使っています。
keystream = b"abcde"
# 平文と鍵ストリームを1バイトずつXORして、暗号文のような値を作ります。
ciphertext = bytes(p ^ k for p, k in zip(plaintext, keystream))
# 復号では、暗号文に同じ鍵ストリームをもう一度XORします。
# XORの性質により、元の平文に戻ります。
decrypted = bytes(c ^ k for c, k in zip(ciphertext, keystream))
print(ciphertext)
print(decrypted)
このコードから分かるのは、同じ鍵ストリームを使えば元に戻せる、という点です。
一方で、同じ鍵ストリームを別の平文にも使い回すと、平文同士の関係が漏れる危険があります。
そのため、CTR系の考え方では、同じ鍵のもとでnonceやカウンタの組み合わせを使い回さないことがとても重要です。
6.6 GCM:暗号化と改ざん検知をまとめて扱う
GCMは、Galois/Counter Modeの略です。
名前の通り、CTRの考え方を使って暗号化しつつ、認証タグを使って改ざん検知も行えるモードです。
NIST SP 800-38Dでは、GCMはAEAD、つまり Authenticated Encryption with Associated Data のためのモードとして規定されています。
また、GMACは、暗号化しないデータに対してMACを生成するGCMの特殊な使い方として説明されています。
参考: NIST SP 800-38D - Recommendation for Block Cipher Modes of Operation: GCM and GMAC
GCMをざっくり見ると、次のような役割があります。
| 要素 | 役割 |
|---|---|
| AES | nonceやカウンタから暗号化に使う値を作る |
| CTRの考え方 | 平文を暗号文に変換する |
| 認証タグ | 暗号文や関連データが改ざんされていないか確認する |
| AAD | 暗号化はしないが、改ざんされていないか確認したいデータ |
図にすると、GCMは次のように「隠す」と「確認する」をまとめて扱うイメージになります。
GCMの大切な点は、認証タグの検証まで含めて意味があるということです。
認証タグの検証に失敗したのに復号結果を使ってしまうと、改ざん検知の意味がなくなってしまいます。
6.7 どのモードを使えばよいのか
初学者向けには、まず次のように整理すると分かりやすいです。
| 目的 | 考え方 |
|---|---|
| 暗号利用モードの概念を理解したい | ECB、CBC、CTRを使って違いを学ぶ |
| 実サービスで安全に使いたい | 独自実装せず、ライブラリが提供するAEADを使う |
| 暗号化と改ざん検知をまとめて扱いたい | AES-GCMやChaCha20-Poly1305などを検討する |
| 古いコードを読む必要がある | CBCやCTRが出てくる可能性を理解しておく |
OWASP Cryptographic Storage Cheat Sheetでは、利用可能であればGCMやCCMのような認証付きモードを最初に検討すべきだと説明されています。
また、ECBは非常に限定的な状況を除いて使うべきではないとされています。
参考: OWASP Cryptographic Storage Cheat Sheet
この記事では、実務で暗号処理を自作することはおすすめしません。
特に、ブロック暗号のモードを低レベルAPIで自分で組み合わせると、padding、IV、nonce、認証タグ、エラー処理などでミスが入りやすくなります。
Pythonで学習・試作する場合も、低レベルな Cipher APIを組み合わせるより、できるだけ AESGCM のような高レベルAPIを使う方が安全な方向に寄せやすいです。
ただし、それでもnonceの一意性や鍵管理を正しく扱う必要があります。
6.8 この章のまとめ
この章では、AESを実際のデータに使うために必要になる暗号利用モードを整理しました。
AESは128ビットのブロックを処理する暗号方式です。
しかし、実際のデータは長さも内容もさまざまです。
そのため、ブロックをどのようにつなげて処理するかを決める暗号利用モードが必要になります。
ECBは仕組みが単純ですが、同じ平文ブロックが同じ暗号文ブロックになり、パターンが残りやすいため、基本的には避けるべきです。
CBCやCTRは、AESを実データに適用するための代表的な考え方ですが、それだけで改ざん検知まで保証するわけではありません。
GCMは、暗号化と改ざん検知をまとめて扱えるAEADとして重要です。
ここまでで、「AESで暗号化する」と言っても、その中には利用モードの選択があることが見えてきました。
次の章では、ここからさらに一歩進んで、暗号化だけでは改ざん検知にならない という点を整理します。
7. 暗号化だけでは改ざん検知にならない
前の章では、AESを実際の長いデータに使うために、暗号利用モードが必要になることを見ました。
ここまでで、「AESを使って、適切なモードで暗号化すればよい」というところまでは見えてきたと思います。
ただし、ここで終わりではありません。
実サービスでは、もう1つ大事な観点があります。
それは、データが途中で書き換えられていないかを確認することです。
暗号化は、主に「中身を読まれないようにする」ための処理です。
一方で、データが途中で変わっていないことを確認する性質は、完全性と呼ばれます。
| 守りたい性質 | やさしく言うと | 例 |
|---|---|---|
| 機密性 | 中身を読まれないこと | 通信内容を第三者に見られにくくする |
| 完全性 | 中身が勝手に変わっていないこと | 支払い金額や送信内容が途中で書き換えられていないか確認する |
| 真正性 | 正しい相手・正しい鍵を持つ相手が作ったものだと確認すること | 本物のサービスや正しい送信元から来たデータか確認する |
この3つは似ているようで、守っているものが少しずつ違います。
特に初学者のうちは、「暗号化しているなら改ざんも防げるのでは?」と考えがちですが、実際には分けて考える必要があります。
7.1 封筒に入れるだけでは、封筒のすり替えまでは分からない
身近な例で考えてみます。
暗号化は、手紙を封筒に入れて中身を見えにくくすることに似ています。
封筒に入っていれば、途中で誰かが見ても中身は分かりにくくなります。
しかし、封筒に入っているだけでは、次のようなことまでは分かりません。
- 封筒が途中で開けられていないか
- 中身が別の紙に差し替えられていないか
- 差出人が本当に正しい人なのか
もちろん、現実の暗号は封筒よりもずっと複雑です。
ただ、イメージとしては、暗号化は「見えにくくする」ためのものであり、改ざん検知には別の確認が必要になると考えると分かりやすいです。
この「封筒が途中で開けられていないか」「中身が差し替えられていないか」を確認する仕組みに近いものが、MAC、認証タグ、認証付き暗号です。
7.2 暗号文を少し変えられると何が困るのか
暗号文は、人間が見ても意味の分からないデータです。
そのため、「暗号文を少し変えても、復号できなくなるだけでは?」と思うかもしれません。
実際には、暗号方式やモードによっては、暗号文の一部を変更した影響が、復号後の平文に現れる場合があります。
このように、暗号文を操作すると平文側にも対応した変化が起きる性質は、ざっくり言うと malleability、日本語では「可鍛性」と呼ばれることがあります。
ここでは、実際のAES実装ではなく、学習用のとても単純なXORの例で雰囲気だけを見てみます。
次のコードは、実用的な暗号ではありません。
「暗号化して読めなくすること」と「改ざんを検知すること」は別である、という点を理解するための小さな例です。
# 注意:
# このコードは、暗号の考え方を学ぶための「おもちゃの例」です。
# 実際の暗号化には使わないでください。
def xor_bytes(data: bytes, key_stream: bytes) -> bytes:
"""data と key_stream を1バイトずつ XOR する学習用関数"""
return bytes(d ^ k for d, k in zip(data, key_stream))
# 例として、送信したい平文を用意する
plaintext = b"PAY:1000"
# 学習用の疑似的な鍵ストリームを用意する
# 実際の暗号では、安全な方法で鍵やnonceから生成される
key_stream = b"ABCDEFGH"
# 平文と鍵ストリームをXORして、暗号文のようなデータを作る
encrypted = xor_bytes(plaintext, key_stream)
# 攻撃者が暗号文の一部を変更したと仮定する
# ここでは、復号後の '1' が '9' になるように1バイトだけ細工している
# これは「認証がない場合、変更に気づけないことがある」例を示すためのもの
tampered = bytearray(encrypted)
tampered[4] ^= ord("1") ^ ord("9")
# 受信側が、変更された暗号文をそのまま復号してしまう
decrypted = xor_bytes(bytes(tampered), key_stream)
print(decrypted) # b'PAY:9000'
この例では、平文の PAY:1000 が、復号後に PAY:9000 のように変わってしまいます。
もちろん、これは実用暗号ではなく、説明用に極端に単純化した例です。
ここで見てほしいポイントは、攻撃方法そのものではありません。
大事なのは、暗号文が途中で変更されていないかを確認する仕組みがないと、受信側が変更に気づけない場合があるという点です。
💡 豆知識
暗号文は「読めないデータ」なので、見た目からは正しいかどうかを判断できません。
そのため、現代的な設計では、暗号文に対して「これは途中で変わっていない」と確認するための情報も一緒に扱うことが重要になります。
7.3 「読めない」と「変えられていない」は別の確認
ここまでの話を図にすると、次のようになります。
この図では、暗号文が通信経路や保存場所を通る途中で変わる可能性があります。
もし「変わっていないか」を確認する仕組みがなければ、受信側は復号結果をそのまま信じてしまうかもしれません。
そのため、暗号技術では次のように役割を分けて考えます。
| 目的 | 必要になる技術の例 | 役割 |
|---|---|---|
| 中身を読まれにくくする | AES、ChaCha20など | 機密性を守る |
| 中身が変わっていないか確認する | MAC、認証タグなど | 完全性を確認する |
| 暗号化と改ざん検知をまとめて扱う | AES-GCM、ChaCha20-Poly1305など | 機密性・完全性・真正性をまとめて扱う |
RFC 5116では、認証付き暗号は、暗号化された平文の機密性に加えて、完全性と真正性を確認する方法を提供すると説明されています。
また、AEADでは、暗号化しない関連データについても完全性・真正性を確認できます。
参考: RFC 5116 - An Interface and Algorithms for Authenticated Encryption
この考え方は、後の章で扱うAEADやAES-GCMを理解するための土台になります。
7.4 MAC・認証タグ・AEADという流れ
改ざん検知のための代表的な考え方に、MAC があります。
MACは Message Authentication Code の略で、日本語では「メッセージ認証コード」と呼ばれます。
MACは、共有鍵を使ってメッセージに確認用の値を付ける仕組みです。
受信側も同じ鍵を使って確認用の値を計算し、送られてきた値と一致するかを確認します。
ざっくり言うと、MACは「同じ秘密を知っている相手だけが作れる確認印」のようなものです。
そして、暗号化とこのような確認をまとめて扱う考え方が、認証付き暗号です。
さらに、暗号化しない関連データも一緒に保護できる形が AEAD です。
OWASP Cryptographic Storage Cheat Sheetでも、利用可能であれば認証付きモードを使うべきであり、GCMやCCMが代表的な選択肢として挙げられています。
また、CTRやCBCのように真正性を保証しないモードを使う場合は、Encrypt-then-MACのように別途認証を実装する必要があると説明されています。
参考: OWASP Cryptographic Storage Cheat Sheet
7.5 認証タグの検証に失敗したら、復号結果を使わない
認証付き暗号を使うときにとても大切なのが、認証タグの検証結果を必ず確認することです。
AES-GCMのような方式では、暗号化の結果として、暗号文だけでなく認証タグも得られます。
復号時には、この認証タグを使って、暗号文や関連データが変わっていないかを確認します。
このとき、検証に失敗したデータは使ってはいけません。
NIST SP 800-38Dでは、GCMはAEADのためのモードとして規定され、GMACは暗号化されないデータに対するMAC生成の特殊化として説明されています。
参考: NIST SP 800-38D - Recommendation for Block Cipher Modes of Operation: GCM and GMAC
ここでの実装上の注意は、エラーを無視しないことです。
「とりあえず復号できた部分だけ使う」「タグ検証に失敗しても処理を続ける」という設計にしてしまうと、認証付き暗号を使っている意味が大きく薄れてしまいます。
また、エラーメッセージの出し方にも注意が必要です。
暗号処理の失敗理由を細かく外部に返しすぎると、攻撃者に余計な手がかりを与える可能性があります。
実務では、ライブラリやフレームワークの推奨に従い、失敗時は安全に処理を中断する設計にすることが重要です。
7.6 この章のまとめ
この章では、暗号化だけでは改ざん検知にならないことを整理しました。
暗号化は、主に中身を読まれないようにするための処理です。
一方で、実サービスでは、データが途中で書き換えられていないことも確認する必要があります。
そのため、現代的な設計では、暗号化だけを単独で考えるのではなく、MAC、認証タグ、認証付き暗号、AEADのような仕組みと一緒に考えることが大切です。
ここまでの流れをまとめると、次のようになります。
| ここまでの章 | 見てきたこと |
|---|---|
| 4章 | AESは代表的な共通鍵ブロック暗号である |
| 5章 | AESという名前だけでは安全性は決まらない |
| 6章 | AESを実データに使うには暗号利用モードが必要になる |
| 7章 | 暗号化だけでは改ざん検知にならない |
次の章では、この流れを受けて、認証付き暗号とは何かをもう少し具体的に整理します。
8. 認証付き暗号とは何か
前の章では、暗号化だけでは改ざん検知にならないことを見ました。
ここまでの流れを一度まとめると、次のようになります。
- AESは、データを読まれにくくするための代表的な共通鍵ブロック暗号
- 暗号利用モードは、AESを実際の長いデータに適用するための使い方
- しかし、暗号化だけでは「途中で書き換えられていないか」までは十分に確認できない
そこで登場するのが、認証付き暗号です。
認証付き暗号は、ざっくり言うと 「中身を隠すこと」と「途中で書き換えられていないことを確認すること」をまとめて行う暗号方式 です。
たとえるなら、普通の暗号化は「中身が見えない封筒」に近いです。
一方で認証付き暗号は、封筒に加えて「開けられたり、すり替えられたりしていないかを確認できる封印」も付けるようなイメージです。
RFC 5116では、Authenticated Encryptionは、暗号化される平文の機密性に加えて、その完全性と真正性を確認する方法を提供すると説明されています。
また、Authenticated Encryption with Associated Data、つまりAEADは、暗号化しない関連データについても完全性と真正性を確認できる仕組みとして説明されています。
参考: RFC 5116 - An Interface and Algorithms for Authenticated Encryption
8.1 認証付き暗号が守るもの
認証付き暗号を理解するために、まず「何を守るのか」を分けて見てみます。
| 守りたい性質 | やさしく言うと | 認証付き暗号との関係 |
|---|---|---|
| 機密性 | 中身を第三者に読まれにくくする | 暗号化で守る |
| 完全性 | 中身が途中で書き換えられていないことを確認する | 認証タグで確認する |
| 真正性 | 正しい鍵を持つ相手が作ったデータか確認する | 認証タグの検証で確認する |
ここでの「認証」は、ログイン画面でIDとパスワードを入力する認証とは少し違います。
認証付き暗号における認証は、主に 「このデータは正しい鍵を使って作られたものか」「途中で変えられていないか」 を確認する意味で使われます。
たとえば、スマホ決済の通信で「支払い金額: 1,000円」というデータを送る場面を考えます。
中身を第三者に読まれないようにするだけでなく、途中で「支払い金額: 10,000円」に書き換えられていないことも確認したいはずです。
このように、実サービスでは「隠す」と「書き換えを見つける」をセットで考える必要があります。
8.2 認証タグは「検査用の印」
認証付き暗号では、暗号化の結果として、暗号文だけでなく 認証タグ も得られます。
認証タグは、ざっくり言うと「この暗号文は、正しい鍵と正しい入力から作られたものです」と確認するための検査用の値です。
受信側は、同じ鍵、同じnonce / IV、同じ関連データを使ってタグを検証します。
このタグ検証に失敗した場合、受信側は「暗号文が改ざんされた」「鍵が違う」「nonce / IVが違う」「関連データが違う」など、何らかの不整合があると判断します。
このときは、復号結果を使わず、エラーとして扱う必要があります。
💡 豆知識
認証タグは、荷物に付ける封印や、ファイルに付ける検査用の印に近いイメージです。
ただし、単なるチェックサムとは違い、秘密鍵を知らない人が正しいタグを作ることは難しいように設計されています。
8.3 MACと認証付き暗号の違い
7章では、改ざん検知のためにMACという考え方にも触れました。
では、MACと認証付き暗号は何が違うのでしょうか。
かなりざっくり整理すると、MACは 「改ざんされていないかを確認するための印を付ける技術」 です。
一方、認証付き暗号は 「暗号化と改ざん検知をまとめて扱う技術」 です。
| 観点 | MAC | 認証付き暗号 |
|---|---|---|
| 中身を隠す | 基本的には隠さない | 隠す |
| 改ざん検知 | できる | できる |
| 使う鍵 | 共有鍵 | 共有鍵 |
| 主な使い方 | メッセージに検査用の値を付ける | 暗号文と認証タグをまとめて作る |
たとえば、暗号化してからMACを付ける Encrypt-then-MAC という設計もあります。
これは、まず暗号化で中身を隠し、その暗号文に対してMACを付ける考え方です。
一方で、AES-GCMのような認証付き暗号を使うと、暗号化と認証タグ生成を1つの仕組みとして扱えます。
そのため、現代的な実装では、暗号化だけを単独で組み立てるよりも、認証付き暗号を使う方が安全な設計にしやすい場合があります。
OWASP Cryptographic Storage Cheat Sheetでも、利用できる場合は認証付きモードを常に使うべきであり、GCMやCCMが代表的な選択肢として挙げられています。
また、GCMやCCMが使えず、CTRやCBCを使う場合は、それら自体は真正性を保証しないため、Encrypt-then-MACのように別途認証を実装する必要があると説明されています。
参考: OWASP Cryptographic Storage Cheat Sheet
8.4 代表的な認証付き暗号
認証付き暗号には、いくつかの代表的な方式があります。
| 方式 | ざっくりした説明 | よく出てくる場面 |
|---|---|---|
| AES-GCM | AESを使った代表的な認証付き暗号モード | TLS、Web API、保存データ保護 |
| AES-CCM | CTRモードとCBC-MACを組み合わせた認証付き暗号モード | 無線・組込み系の文脈で見かけることがある |
| ChaCha20-Poly1305 | ChaCha20で暗号化し、Poly1305で認証する方式 | TLS、モバイル・ソフトウェア実装 |
NIST SP 800-38Dでは、GCMはAEADのためのモードであり、GMACは暗号化されないデータに対するMAC生成の特殊化として説明されています。
参考: NIST SP 800-38D - Recommendation for Block Cipher Modes of Operation: GCM and GMAC
また、RFC 8439では、ChaCha20ストリーム暗号とPoly1305認証子を、単体のアルゴリズムとしてだけでなく、AEADアルゴリズムとして組み合わせる方法も定義されています。
参考: RFC 8439 - ChaCha20 and Poly1305 for IETF Protocols
ここで大切なのは、方式名をたくさん覚えることではありません。
初学者の段階では、まず次のように理解しておくと十分です。
認証付き暗号は、暗号化と改ざん検知を一緒に扱うための考え方。
AES-GCMやChaCha20-Poly1305は、その代表的な方式。
8.5 Pythonで認証タグの検証失敗を見てみる
ここでは、Pythonの cryptography ライブラリを使って、AES-GCMによる認証付き暗号の最小例を見てみます。
このコードは、実務用の鍵管理やエラー処理をすべて含むものではありません。
あくまで 「暗号文を少しでも変更すると、タグ検証に失敗する」 ことを確認するための学習用コードです。
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.exceptions import InvalidTag
import os
# 学習用に128ビットのAES鍵を生成する
# 実務では、鍵の生成・保存・ローテーションを別途きちんと設計する
key = AESGCM.generate_key(bit_length=128)
# 生成した鍵を使ってAES-GCMのオブジェクトを作る
aesgcm = AESGCM(key)
# GCMでは、同じ鍵のもとでnonceを使い回してはいけない
# ここでは学習用に12バイトのランダム値を生成する
nonce = os.urandom(12)
# 暗号化したい平文
plaintext = "支払い金額: 1000円".encode("utf-8")
# AADは暗号化しないが、改ざんされていないか確認したい関連データ
# ここでは「このデータはpayment-api-v1向け」という文脈を表している
aad = b"payment-api-v1"
# encryptの結果には、暗号文と認証タグが含まれる
ciphertext = aesgcm.encrypt(nonce, plaintext, aad)
# 正しいnonce、暗号文、AADを渡すと復号できる
decrypted = aesgcm.decrypt(nonce, ciphertext, aad)
print(decrypted.decode("utf-8"))
# 攻撃者が暗号文の一部を変更した状況を再現する
tampered = bytearray(ciphertext)
tampered[0] ^= 0x01 # 先頭1バイトの一部のビットを反転させる
try:
# 改ざんされた暗号文を復号しようとすると、タグ検証に失敗する
aesgcm.decrypt(nonce, bytes(tampered), aad)
except InvalidTag:
# タグ検証に失敗した場合、復号結果は使わずエラーとして扱う
print("改ざん、または鍵・nonce・AADの不一致を検出しました")
実行すると、たとえば次のような出力になります。
支払い金額: 1000円
改ざん、または鍵・nonce・AADの不一致を検出しました
この例で見たいポイントは、暗号文を少し変えただけで、復号時にエラーになることです。
つまり、AES-GCMでは、暗号文を読まれにくくするだけでなく、途中で変更されていないかも確認しています。
cryptography の公式ドキュメントでも、AESGCMの decrypt はデータと関連データを認証し、タグ検証に失敗した場合は InvalidTag が発生すると説明されています。
また、同じ鍵のもとでnonceを再利用してはいけないことも明記されています。
参考: cryptography - Authenticated encryption
8.6 認証付き暗号を使うときの注意
認証付き暗号は便利ですが、「使えば何でも安全」というものではありません。
特に、次の点には注意が必要です。
| 注意点 | なぜ大切か |
|---|---|
| nonce / IVを使い回さない | 方式によっては安全性が大きく崩れる可能性がある |
| 認証タグを必ず検証する | 検証しないと改ざん検知の意味がない |
| 検証失敗時は復号結果を使わない | 改ざんされたデータを処理してしまう可能性がある |
| 鍵を安全に管理する | 鍵が漏れると暗号化の意味が失われる |
| 独自に組み合わせを作らない | 暗号化とMACの順序や対象を間違えると危険 |
特に、nonce / IVの扱いは重要です。
NISTはSP 800-38Dの改訂提案に関する説明で、GCMの安全性は、異なるメッセージに対してIVが繰り返されないことや、認証タグの長さに強く依存すると説明しています。
参考: NIST - Proposal to Revise SP 800-38D: GCM and GMAC Block Cipher Modes
また、認証付き暗号は「暗号化」と「改ざん検知」をまとめて扱うための便利な仕組みですが、鍵管理やエラー処理まで自動で完璧にしてくれるわけではありません。
実務では、信頼できるライブラリを使い、そのライブラリの推奨に従って実装することが重要です。
8.7 この章のまとめ
この章では、認証付き暗号について整理しました。
認証付き暗号は、データを読まれにくくするだけでなく、途中で書き換えられていないかも確認するための仕組みです。
AES-GCMやChaCha20-Poly1305のような方式は、現代的な通信や保存データ保護でよく登場します。
ただし、認証付き暗号を使う場合でも、nonce / IVの使い回し、認証タグの検証忘れ、鍵管理の不備には注意が必要です。
ここまでの流れを整理すると、次のようになります。
次の章では、認証付き暗号の中でもよく出てくる AEAD について、もう少し詳しく整理します。
特に、AAD、つまり「暗号化はしないけれど、改ざんされていないか確認したい関連データ」がどのような場面で役立つのかを見ていきます。
9. AEADとは何か
前の章では、認証付き暗号を「中身を隠すこと」と「途中で書き換えられていないことを確認すること」をまとめて行う仕組みとして整理しました。
ここからは、その認証付き暗号の説明でよく出てくる AEAD について見ていきます。
AEADは、英語の Authenticated Encryption with Associated Data の略です。
日本語に直訳すると少し固いですが、ざっくり言うと次のような考え方です。
暗号化したいデータ と、
暗号化はしないけれど改ざんされていないか確認したいデータ を、
まとめて安全に扱うための仕組み。
ここで大切なのは、AEADでは「暗号化するデータ」だけでなく、「暗号化しない関連データ」も改ざん検知の対象にできるという点です。
RFC 5116では、AEADは平文の機密性に加え、平文と関連データの完全性・真正性を確認する仕組みとして整理されています。
参考: RFC 5116 - An Interface and Algorithms for Authenticated Encryption
9.1 まずは荷物の例で考える
AEADの考え方は、宅配便の荷物にたとえると少し分かりやすくなります。
荷物の中身は、箱の中に入っているので外からは見えません。
これは、暗号化された本文に近いです。
一方で、配送先や荷物番号のような情報は、配達するために外から見える必要があります。
しかし、もし配送先や荷物番号が途中で勝手に書き換えられたら困ります。
このとき、次のように考えることができます。
| たとえ | AEADでの対応 | 説明 |
|---|---|---|
| 箱の中身 | 平文 | 暗号化して隠したいデータ |
| 鍵付きの箱 | 暗号文 | 第三者には読みにくい形に変換されたデータ |
| 配送ラベル | AAD | 暗号化はしないが、改ざんされていないか確認したいデータ |
| 封印シール | 認証タグ | 中身やラベルが書き換えられていないか確認するための値 |
この例で見ると、AEADは「箱の中身だけを守る」のではなく、箱の外に出ているラベルも含めて、途中で変えられていないか確認する仕組み と考えられます。
9.2 AEADで出てくる主な要素
AEADでは、いくつかの要素が一緒に出てきます。
最初は名前が多く感じますが、役割ごとに分けると理解しやすくなります。
| 要素 | やさしい説明 | 代表的な注意点 |
|---|---|---|
| 鍵 | 暗号化・復号に使う秘密の値 | 漏えいすると安全性が大きく崩れる |
| nonce / IV | 暗号化ごとに使う一度きりの値 | 同じ鍵のもとで使い回さない |
| 平文 | 暗号化したい元データ | 復号後に取り出したい中身 |
| AAD | 暗号化しないが改ざん検知したい関連データ | 秘密情報は入れない |
| 暗号文 | 平文を暗号化した結果 | これだけでは元の中身は読みにくい |
| 認証タグ | 改ざん検知のための確認値 | 検証に失敗したら復号結果を使わない |
図にすると、AEADの流れは次のようになります。
ここで特に重要なのは、復号時にも同じAADが必要になる という点です。
暗号化時と復号時でAADが少しでも変わると、認証タグの検証に失敗します。
つまり、AADは暗号化されていないため外から見える場合がありますが、勝手に書き換えられると検出できます。
9.3 AADはどんな場面で使えるのか
AADは、暗号化したくないけれど、改ざんされたら困る情報に使います。
たとえば、APIリクエストを考えてみます。
リクエスト本文には、支払い金額やユーザー操作に関わる情報が入っているかもしれません。
この本文は、第三者に読まれないように暗号化したい場合があります。
一方で、HTTPメソッド、パス、コンテンツ種別のような情報は、通信処理やルーティングのために参照したい場合があります。
これらをすべて暗号化してしまうと、途中の処理で扱いづらくなることがあります。
そこで、考え方としては次のように分けられます。
| データ | 扱い方 | 理由 |
|---|---|---|
| リクエスト本文 | 暗号化する | 中身を読まれたくないため |
| メソッドやパスなど | AADとして扱う | 隠す必要はないが、書き換えられると困るため |
| 認証タグ | 検証に使う | 本文やAADが変わっていないか確認するため |
もちろん、実際のプロトコルでは仕様によって扱い方が決まります。
そのため、「どの項目をAADにするか」は自分の思いつきで決めるのではなく、利用するプロトコルやライブラリの設計に従う必要があります。
ただ、初学者の段階では、まず次のイメージを持つと分かりやすいです。
AADは、見えていてもよいけれど、勝手に変えられたら困る情報 を守るために使う。
9.4 PythonでAADの役割を見てみる
ここでは、cryptography ライブラリの AESGCM を使って、AADが変わると復号に失敗することを確認します。
AESGCM は、AES-GCMを扱うためのクラスです。
cryptography の公式ドキュメントでは、AES-GCMなどのAEADではnonceを同じ鍵で再利用してはいけないこと、また復号時に認証タグの検証へ失敗すると InvalidTag が発生することが説明されています。
参考: cryptography - Authenticated encryption
注意
以下のコードは、AEADとAADの考え方を理解するための最小例です。
実サービスでは、鍵の保存、鍵のローテーション、nonceの生成・管理、例外処理、ログ設計などを別途きちんと考える必要があります。
from os import urandom
from cryptography.exceptions import InvalidTag
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
# AES-GCMで使う共通鍵を生成する。
# ここでは学習用に128ビット鍵を生成している。
# 実務では鍵の生成だけでなく、保存・配布・更新・失効も設計する必要がある。
key = AESGCM.generate_key(bit_length=128)
# AESGCMオブジェクトを作成する。
# このオブジェクトを使って、暗号化と復号を行う。
aesgcm = AESGCM(key)
# GCMでは、同じ鍵のもとでnonceを使い回してはいけない。
# ここでは学習用に12バイト、つまり96ビットのランダム値を使っている。
nonce = urandom(12)
# 暗号化したい本文。
# たとえば、APIリクエストの本文のようなものをイメージする。
plaintext = b'{"amount":1000,"currency":"JPY"}'
# AADは、暗号化はしないが改ざん検知したい関連データ。
# ここでは、HTTPメソッドやパスのような情報を簡略化して表している。
aad = b"method=POST;path=/payment"
# AES-GCMで暗号化する。
# cryptographyのAESGCMでは、戻り値に暗号文と認証タグが含まれる。
ciphertext = aesgcm.encrypt(nonce, plaintext, aad)
# 正しいAADを使って復号する。
# nonce、暗号文、AADの組み合わせが正しければ、元の平文を取り出せる。
decrypted = aesgcm.decrypt(nonce, ciphertext, aad)
print(decrypted.decode("utf-8"))
# AADを少しだけ変えてみる。
# 本文や暗号文を変えていなくても、AADが変わると検証に失敗する。
tampered_aad = b"method=GET;path=/payment"
try:
aesgcm.decrypt(nonce, ciphertext, tampered_aad)
except InvalidTag:
print("AADが変わったため、認証タグの検証に失敗しました。")
このコードで大切なのは、暗号文そのものを変更していなくても、AADを変更すると復号に失敗する点です。
つまり、AEADでは次の両方をまとめて確認できます。
- 暗号化された本文が書き換えられていないこと
- 暗号化されていない関連データも書き換えられていないこと
9.5 AADには秘密情報を入れない
AADについて、もう1つ注意したい点があります。
それは、AADは暗号化されるわけではない ということです。
AADは、認証タグの計算には含まれます。
そのため、復号時に同じAADを渡さなければ検証に失敗します。
しかし、AADそのものは暗号文のように隠されるわけではありません。
つまり、AADにパスワード、秘密鍵、アクセストークンのような秘密情報を入れるのは適切ではありません。
| 入れてよい可能性があるもの | 入れるべきではないもの |
|---|---|
| メソッド名 | パスワード |
| パス | 秘密鍵 |
| バージョン番号 | アクセストークン |
| メッセージ種別 | 個人情報など秘匿したい値 |
上の表は、あくまで考え方を示すための例です。
実際には、利用するプロトコル、アプリケーションの設計、脅威モデルに応じて判断する必要があります。
初学者向けには、まず次のように覚えるとよいです。
AADは、隠すための場所ではなく、見えていてもよい情報を改ざん検知に含めるための場所。
9.6 AEADとAES-GCMの関係
ここまで見ると、AEADとAES-GCMの関係も整理しやすくなります。
AEADは、特定の1つのアルゴリズム名ではありません。
「暗号化」と「認証」と「関連データの保護」をまとめて扱うための考え方、またはインターフェースのようなものです。
一方で、AES-GCMは、そのAEADを実現する代表的な方式の1つです。
| 用語 | 位置づけ |
|---|---|
| AEAD | 認証付き暗号に関連データを加えて扱う考え方・枠組み |
| AES-GCM | AESを使った代表的なAEAD方式 |
| ChaCha20-Poly1305 | ChaCha20とPoly1305を組み合わせた代表的なAEAD方式 |
| AAD | AEADで扱える「暗号化しないが改ざん検知したい関連データ」 |
TLS 1.3では、暗号スイートとして TLS_AES_128_GCM_SHA256、TLS_AES_256_GCM_SHA384、TLS_CHACHA20_POLY1305_SHA256 などが定義されています。
ここからも、現代的な通信プロトコルではAEAD方式が重要な役割を持っていることが分かります。
参考: RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3
9.7 この章のまとめ
この章では、AEADについて整理しました。
AEADは、暗号化によって中身を隠しつつ、暗号文と関連データが改ざんされていないかも確認するための仕組みです。
特にAADを使うことで、暗号化しない情報も認証タグの検証対象に含めることができます。
ただし、AADは暗号化されません。
そのため、AADには秘密情報を入れず、「見えていてもよいが、勝手に書き換えられると困る情報」を入れるものとして考えるのが大切です。
ここまでで、認証付き暗号とAEADの考え方が見えてきました。
次の章では、AEADの代表例である AES-GCM について、もう少し具体的に整理します。
AES、GCM、nonce / IV、認証タグがどのように関係しているのかを見ていきます。
10. AES-GCMをざっくり整理する
前の章では、AEADが「暗号化するデータ」と「暗号化しないが改ざん検知したい関連データ」をまとめて扱える仕組みであることを見ました。
ここからは、その代表例として AES-GCM をもう少し具体的に整理します。
ここまでの記事では、AES、暗号利用モード、認証付き暗号、AEADを順番に見てきました。
AES-GCMは、それらが一気につながるポイントです。
ざっくり言うと、AES-GCMは AESを使ってデータを暗号化しながら、認証タグによって改ざんも検知できる方式 です。
NIST SP 800-38Dでは、GCMはAEADのためのモードであり、GMACは暗号化されないデータに対してMACを生成する特殊化として説明されています。
参考: NIST SP 800-38D - Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM) and GMAC
10.1 AES-GCMを部品に分けて見る
AES-GCMという名前は、最初は少し長く感じます。
しかし、分けて見ると理解しやすくなります。
| 部品 | ざっくりした役割 |
|---|---|
| AES | 共通鍵を使ってデータを変換するブロック暗号 |
| GCM | 暗号化と改ざん検知をまとめて扱う利用モード |
| nonce / IV | 同じ鍵のもとで使い回してはいけない値 |
| AAD | 暗号化しないが、改ざん検知に含めたい関連データ |
| 認証タグ | 暗号文やAADが変わっていないか確認するための値 |
ここで大切なのは、AES-GCMは「AESをそのまま使うだけ」ではないという点です。
AESという暗号アルゴリズムを、GCMという使い方の中で利用し、暗号文と認証タグを作ります。
たとえるなら、AESは頑丈な鍵そのもの、GCMはその鍵を使って「箱を閉めるだけでなく、開封された形跡も分かるようにする仕組み」と考えるとイメージしやすいです。
10.2 AES-GCMの入出力
AES-GCMでは、暗号化するときに次のような情報を使います。
| 入力 | 秘密にする必要 | 役割 |
|---|---|---|
| 共通鍵 | 秘密にする | 暗号化・復号・認証タグの検証に使う |
| nonce / IV | 秘密でなくてもよいが、同じ鍵で再利用しない | 暗号化ごとに処理を区別する |
| 平文 | 秘密にしたい | 暗号化される本文 |
| AAD | 秘密にしない | 暗号化はしないが、改ざん検知に含める関連データ |
そして、暗号化の結果として、主に次の情報が得られます。
| 出力 | 説明 |
|---|---|
| 暗号文 | 平文を読めない形にしたもの |
| 認証タグ | 暗号文やAADが変わっていないか確認するための値 |
Pythonの cryptography ライブラリでは、AESGCM.encrypt() の戻り値は、暗号文の末尾に16バイトの認証タグが付いた形で返されます。
また、同じ鍵でnonceを再利用してはいけないこと、AADは認証されるが暗号化されないことも公式ドキュメントで説明されています。
参考: cryptography - Authenticated encryption
10.3 図で見るAES-GCMの流れ
AES-GCMの流れをかなり簡略化すると、次のようになります。
この図で特に見てほしいのは、復号時に 認証タグの検証 が入っている点です。
AES-GCMでは、単に暗号文を平文に戻すだけではありません。
暗号文、AAD、nonce / IV、鍵のどれかが合わなければ、認証タグの検証に失敗します。
そのため、復号処理でエラーが出た場合に「とりあえず復号できた部分だけ使う」といった扱いをしてはいけません。
認証タグの検証に失敗したデータは、改ざん、鍵の誤り、nonceの誤り、AADの不一致などが疑われるため、信頼できないデータとして扱う必要があります。
10.4 PythonでAES-GCMの流れを確認する
ここでは、学習用にPythonでAES-GCMの流れを確認します。
実務では鍵管理、nonceの管理、例外処理、ログ、権限管理などを別途きちんと設計する必要があります。
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.exceptions import InvalidTag
import os
# AES-GCMで使う共通鍵を生成する。
# bit_lengthには128, 192, 256を指定できる。
# ここでは学習用に128ビット鍵を使う。
key = AESGCM.generate_key(bit_length=128)
# 生成した鍵からAESGCMオブジェクトを作る。
# この鍵は暗号化と復号の両方で使うため、外部に漏らしてはいけない。
aesgcm = AESGCM(key)
# nonceを生成する。
# cryptographyのAESGCMでは12バイト、つまり96ビットのnonceを使う。
# 同じ鍵で同じnonceを再利用してはいけない。
nonce = os.urandom(12)
# 暗号化したい本文。
# これは暗号文に変換され、第三者から読みにくい形になる。
plaintext = b"payment_amount=1000"
# AADは暗号化されないが、改ざん検知には含められる関連データ。
# たとえば、リクエストの種類やパスなどを想定できる。
aad = b"method=POST;path=/payments"
# AES-GCMで暗号化する。
# 戻り値は、暗号文の末尾に認証タグが付いたbytesになる。
ciphertext_with_tag = aesgcm.encrypt(nonce, plaintext, aad)
print("暗号文 + 認証タグ:", ciphertext_with_tag.hex())
# 同じkey, nonce, aadを使って復号する。
# 認証タグの検証に成功した場合だけ、元の平文が得られる。
decrypted = aesgcm.decrypt(nonce, ciphertext_with_tag, aad)
print("復号結果:", decrypted.decode())
# AADを少し変えてみる。
# 暗号文を変更していなくても、AADが変わると認証タグの検証に失敗する。
tampered_aad = b"method=GET;path=/payments"
try:
aesgcm.decrypt(nonce, ciphertext_with_tag, tampered_aad)
except InvalidTag:
print("AADが一致しないため、認証タグの検証に失敗しました。")
このコードでは、本文である payment_amount=1000 は暗号化されます。
一方、AADとして渡した method=POST;path=/payments は暗号化されません。
しかし、復号時にAADを method=GET;path=/payments に変えると、認証タグの検証に失敗します。
これは、AEADの「暗号化しない関連データも改ざん検知に含められる」という性質を確認するための例です。
注意
このコードは、AES-GCMの流れを理解するための最小例です。
実務では、鍵を毎回ランダム生成してそのまま捨てると復号できなくなります。
逆に、鍵をソースコードへ直接書くと漏えいリスクが高くなります。
そのため、実サービスでは鍵管理システム、環境変数、シークレット管理サービスなどを含めて設計する必要があります。
10.5 AES-GCMで特に注意したいnonce / IV
AES-GCMで特に注意したいのが、nonce / IVの扱いです。
nonce / IVは、秘密にするための値ではありません。
しかし、同じ鍵のもとで同じnonce / IVを使い回すと、安全性が大きく損なわれる可能性があります。
cryptography の公式ドキュメントでも、同じ鍵とnonceの組み合わせを再利用すると、その組み合わせを使ったメッセージの安全性が損なわれると明記されています。
また、AESGCMではNISTが96ビットIVを推奨していることも説明されています。
参考: cryptography - Authenticated encryption
初学者向けには、まず次のように考えるとよいです。
| 値 | 秘密にする必要 | 使い回し | 役割 |
|---|---|---|---|
| 共通鍵 | ある | 同じ用途で管理して使う | 暗号化・復号の中心 |
| nonce / IV | 基本的にはない | 同じ鍵では再利用しない | 暗号化ごとの区別 |
| 認証タグ | ない | 暗号文ごとに変わる | 改ざん検知 |
| AAD | ない | 用途による | 暗号化しない関連データの保護 |
nonce / IVについては、「秘密ではないから雑に扱ってよい」と考えると危険です。
秘密ではなくても、一意性が安全性に関わる値 だからです。
💡 豆知識
nonceは “number used once” と説明されることがあります。
直訳すると「一度だけ使う数」のような意味です。
ただし、実際の仕様ではランダム値、カウンタ、ランダム値とカウンタの組み合わせなど、設計に応じてさまざまな作り方があります。
大切なのは、対象の暗号方式が要求する条件を守ることです。
10.6 AES-GCMは万能ではない
ここまで見ると、AES-GCMはかなり便利に見えます。
実際、暗号化と改ざん検知をまとめて扱えるため、現代的なシステムでよく使われる重要な方式です。
ただし、AES-GCMも万能ではありません。
特に、次のような点には注意が必要です。
| 注意点 | 説明 |
|---|---|
| nonce / IVを再利用しない | 同じ鍵で同じnonceを使い回すと危険 |
| 認証タグを必ず検証する | タグ検証に失敗した復号結果を使ってはいけない |
| AADを秘密情報の置き場所にしない | AADは暗号化されない |
| 鍵を安全に管理する | 鍵が漏えいすると暗号化の意味が崩れる |
| 独自実装しない | 標準的なライブラリやプロトコルを使う |
OWASP Cryptographic Storage Cheat Sheetでは、利用可能な場合はGCMやCCMのような認証付きモードを優先すること、CTRやCBCを使う場合は別途認証を行う必要があること、ECBは非常に限定的な状況を除いて避けるべきことが説明されています。
参考: OWASP Cryptographic Storage Cheat Sheet
また、NISTはSP 800-38Dの改訂作業を進めています。2026年6月時点では、GCMの改訂に加え、256ビットブロックの基盤暗号を想定したwGCMを含める提案についてコメント募集が行われています。
参考: NIST - Second Pre-Draft Call for Comments on SP 800-38D
このような動きを見ると、AES-GCMは現在重要な方式でありつつも、暗号技術は一度覚えたら終わりではなく、標準や推奨の更新を追い続ける必要がある分野だと分かります。
10.7 この章のまとめ
この章では、AES-GCMについて整理しました。
AES-GCMは、AESを使った代表的なAEAD方式です。
中身を読まれにくくする暗号化と、途中で書き換えられていないことを確認する認証をまとめて扱えます。
ただし、安全に使うには、AES-GCMという名前を知るだけでは不十分です。
共通鍵、nonce / IV、AAD、認証タグの役割を理解し、特にnonce / IVの再利用を避け、認証タグの検証結果を正しく扱う必要があります。
ここまでで、AES-GCMの全体像はかなり見えてきました。
次の章では、nonce / IV、AAD、認証タグといった、AES-GCMやAEADで特につまずきやすい要素を、もう一度整理します。
11. nonce / IV・AAD・認証タグでつまずきやすい点
前の章では、AES-GCMを「AESを使って暗号化しながら、認証タグで改ざんも検知できる方式」として整理しました。
ここまで来ると、AES-GCMの全体像はかなり見えてきたと思います。
ただ、実際にドキュメントやコードを読むと、急に似たような言葉が増えてきます。
- nonce
- IV
- AAD
- 認証タグ
- ciphertext
- key
このあたりは、初学者がかなりつまずきやすいポイントです。
しかも、どれも「なんとなく付けておけばよい値」ではありません。
この章では、AES-GCMやAEADでよく出てくる値を、もう一度ゆっくり整理します。
細かい数式ではなく、何のための値なのか、秘密にする必要があるのか、間違えると何が危ないのか を中心に見ていきます。
11.1 まず全体像を見る
AES-GCMで使う主な値を、ざっくり整理すると次のようになります。
| 値 | やさしく言うと | 秘密にする必要 | 特に注意すること |
|---|---|---|---|
| 共通鍵 | 暗号化・復号に使う本物の鍵 | ある | 漏えいさせない。用途ごとに適切に管理する。 |
| nonce / IV | 暗号化ごとに変える値 | 基本的にはない | 同じ鍵のもとで使い回さない。 |
| 平文 | 暗号化したい元データ | 内容による | 暗号化前・復号後の扱いにも注意する。 |
| 暗号文 | 暗号化されたデータ | 基本的にはない | 改ざんされる可能性はあるため、認証タグで検証する。 |
| AAD | 暗号化しないが改ざん検知したい関連データ | ない | 秘密情報を入れない。復号時にも同じAADを使う。 |
| 認証タグ | 改ざんされていないか確認する値 | 基本的にはない | 検証に失敗したら復号結果を使わない。 |
図にすると、AES-GCMでは次のような材料をまとめて扱います。
ここで大切なのは、すべての値を秘密にする必要があるわけではない、という点です。
たとえば、nonce / IV、AAD、認証タグは、基本的には秘密にする値ではありません。
ただし、秘密ではないからといって、雑に扱ってよいわけでもありません。
特に nonce / IV は、秘密ではないが、使い回してはいけない値 として理解しておく必要があります。
11.2 nonce / IV:秘密ではないが、使い回してはいけない値
まず、nonce / IV から見ていきます。
nonceは、よく “number used once” と説明されます。
直訳すると「一度だけ使う数」のような意味です。
IVは、Initialization Vector の略です。
日本語では「初期化ベクトル」と呼ばれることがあります。
厳密な使い分けは暗号方式やライブラリによって少し変わりますが、初学者向けには、まず次のように押さえると分かりやすいです。
nonce / IV は、同じ鍵で暗号化しても毎回違う結果になるようにするための値です。
たとえば、同じメッセージを同じ鍵で2回暗号化したとき、毎回まったく同じ暗号文になると、攻撃者に「同じ内容が送られたのかもしれない」と推測されやすくなります。
そこで、暗号化ごとに違う nonce / IV を使い、同じ平文でも異なる暗号文になるようにします。
イメージとしては、荷物を送るたびに付ける 発送番号 に近いです。
発送番号そのものは秘密ではありません。
しかし、同じ番号を何度も使い回すと、荷物の管理がおかしくなります。
AES-GCMでも同じように、nonce / IVは秘密にする値ではありませんが、同じ鍵のもとで再利用してはいけません。
cryptography の公式ドキュメントでも、同じ鍵とnonceの組み合わせを再利用すると、その組み合わせを使ったメッセージの安全性が損なわれると説明されています。
参考: cryptography - Authenticated encryption
また、NIST SP 800-38Dでは、GCMにおけるIVの扱いが重要な要素として整理されています。
参考: NIST SP 800-38D - GCM and GMAC
PythonでAES-GCMを使う場合、nonceは次のように生成できます。
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
# AES-256用の256ビット鍵を生成する
# 実務では、この鍵を安全に保存・管理する仕組みが別途必要
key = AESGCM.generate_key(bit_length=256)
# AESGCMオブジェクトを作成する
# このオブジェクトを使って暗号化・復号を行う
aesgcm = AESGCM(key)
# AES-GCMでよく使われる96ビット、つまり12バイトのnonceを生成する
# nonceは秘密にする値ではないが、同じ鍵では再利用しない
nonce = os.urandom(12)
# 暗号化したいデータ
plaintext = b"payment amount: 1000 yen"
# 今回はAADを使わないためNoneを指定する
# 暗号文の末尾には認証タグも含まれる
ciphertext = aesgcm.encrypt(nonce, plaintext, None)
# 復号には、同じ鍵・同じnonce・同じAADが必要
restored = aesgcm.decrypt(nonce, ciphertext, None)
print(restored)
このコードでは、nonce を os.urandom(12) で生成しています。
ここでの12バイトは96ビットです。
cryptography のドキュメントでは、AES-GCMにおいてNISTが96ビットIVを推奨していることが説明されています。
そのため、学習段階ではまず「AES-GCMでは96ビットのnonce / IVがよく使われる」と覚えると理解しやすいです。
参考: cryptography - Authenticated encryption
💡 豆知識
nonce / IVは、パスワードや秘密鍵のように隠す値ではありません。
暗号文と一緒に保存・送信されることもあります。
ただし、「秘密ではない」と「使い回してよい」は別です。
AES-GCMでは、同じ鍵のもとでnonce / IVを使い回さないことがとても重要です。
11.3 AAD:見えてよいが、書き換えられると困るデータ
次に、AADを見ていきます。
AADは、Additional Authenticated Data の略です。
日本語にすると「追加の認証対象データ」のような意味になります。
少し固い言葉ですが、考え方はシンプルです。
AADは、暗号化はしないが、改ざんされていないことを確認したい関連データです。
たとえば、API通信で次のようなデータを扱う場面を考えます。
| データ | 暗号化したいか | 改ざん検知したいか | 扱い方の例 |
|---|---|---|---|
| 支払い金額や個人情報 | はい | はい | 平文として暗号化対象にする |
| リクエストID | いいえ | はい | AADに入れることがある |
| APIのバージョン | いいえ | はい | AADに入れることがある |
| 送信日時やメタデータ | 場合による | はい | 設計に応じてAADに入れる |
AADは暗号化されません。
そのため、AADに秘密情報を入れるべきではありません。
一方で、AADは認証タグの計算に含まれます。
つまり、AADが途中で書き換えられると、復号時の認証タグ検証に失敗します。
配送でたとえると、AADは 箱の外側に貼る配送ラベル に近いです。
配送ラベルは配達員が見る必要があるので隠せません。
しかし、宛先や管理番号を勝手に書き換えられると困ります。
AES-GCMでAADを使う例は、次のように書けます。
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.exceptions import InvalidTag
# 256ビットのAES鍵を生成する
key = AESGCM.generate_key(bit_length=256)
aesgcm = AESGCM(key)
# 暗号化ごとに異なるnonceを用意する
nonce = os.urandom(12)
# 暗号化したい本文
plaintext = b"user_id=123&amount=1000"
# 暗号化はしないが、改ざん検知したい関連データ
# 例: APIのパスやリクエストの種類など
aad = b"POST /api/payments"
# plaintextは暗号化され、aadは暗号化されないが認証対象になる
ciphertext = aesgcm.encrypt(nonce, plaintext, aad)
# 復号時には、暗号化時と同じAADを渡す必要がある
restored = aesgcm.decrypt(nonce, ciphertext, aad)
print(restored)
# AADが途中で書き換えられた場合を試す
modified_aad = b"POST /api/refunds"
try:
# AADが一致しないため、認証タグの検証に失敗する
aesgcm.decrypt(nonce, ciphertext, modified_aad)
except InvalidTag:
print("AADが一致しないため、改ざんの可能性があります")
この例では、plaintext は暗号化されます。
一方で、aad は暗号化されません。
しかし、aad を POST /api/payments から POST /api/refunds に変えると、復号時に検証が失敗します。
これは、AADが「見えてよいが、勝手に変えられると困るデータ」として扱われているためです。
RFC 5116では、AEADが平文の機密性に加え、平文と関連データの完全性・真正性を確認する仕組みとして定義されています。
参考: RFC 5116 - An Interface and Algorithms for Authenticated Encryption
11.4 認証タグ:改ざんされていないかを確認する検査値
認証タグは、AES-GCMやAEADで特に重要な値です。
ざっくり言うと、認証タグは 「この暗号文やAADは、途中で書き換えられていないようだ」と確認するための検査値 です。
封筒でたとえると、認証タグは封かんシールに近いです。
封筒の中身を隠すだけなら封筒に入れればよいですが、途中で開けられたり書き換えられたりしていないことを確認するには、封かんシールのような仕組みが必要です。
AES-GCMでは、暗号化の結果として暗号文と認証タグが作られます。
ライブラリによって返し方は異なりますが、cryptography の AESGCM.encrypt() では、暗号文の末尾に16バイトの認証タグが付いた形で返されます。
参考: cryptography - Authenticated encryption
認証タグについて大切なのは、検証に失敗した復号結果を使ってはいけない という点です。
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.exceptions import InvalidTag
key = AESGCM.generate_key(bit_length=256)
aesgcm = AESGCM(key)
nonce = os.urandom(12)
plaintext = b"important message"
# 暗号化する
# 戻り値には、暗号文と認証タグが含まれる
ciphertext = aesgcm.encrypt(nonce, plaintext, None)
# 攻撃者が暗号文の一部を書き換えたと仮定する
# bytearrayに変換して、先頭1バイトを反転させる
tampered = bytearray(ciphertext)
tampered[0] ^= 0x01
try:
# 改ざんされた暗号文を復号しようとする
# 認証タグの検証に失敗するとInvalidTagが発生する
aesgcm.decrypt(nonce, bytes(tampered), None)
except InvalidTag:
print("認証タグの検証に失敗しました。復号結果は使いません。")
このコードでは、暗号文を1バイトだけ変更しています。
それだけでも、認証タグの検証に失敗します。
ここで重要なのは、エラーが出たときに「とりあえず読める部分だけ使う」と考えないことです。
認証タグの検証に失敗した時点で、そのデータは信頼してはいけません。
💡 豆知識
AES-GCMの認証タグは、データの「チェックサム」と似ているように見えるかもしれません。
しかし、単なる誤り検出用のチェックサムとは目的が違います。
認証タグは、攻撃者が意図的にデータを書き換える状況も考えて使う暗号学的な検査値です。
11.5 よくあるつまずきポイント
ここまでの内容をもとに、nonce / IV、AAD、認証タグでつまずきやすい点を整理します。
| つまずきやすい点 | 正しくは |
|---|---|
| nonce / IVは秘密にしないといけない | 基本的には秘密ではない。ただし同じ鍵で使い回してはいけない。 |
| nonce / IVはランダムなら何でもよい | 対象方式・ライブラリが求める長さや一意性を守る必要がある。 |
| AADは暗号化される | AADは暗号化されない。改ざん検知の対象になる。 |
| AADに秘密情報を入れてよい | AADは見える前提なので、秘密情報を入れるべきではない。 |
| 認証タグはなくても復号できる | AES-GCMでは認証タグ検証が重要。検証を省略してはいけない。 |
| タグ検証に失敗しても一部だけ使えばよい | 検証失敗時は復号結果を使わない。 |
| AES-GCMを使えば鍵管理は気にしなくてよい | 共通鍵が漏えいすれば安全性は崩れる。鍵管理は別途重要。 |
この表で特に大切なのは、秘密にする値 と 正しく扱う値 は同じではないという点です。
共通鍵は秘密にする必要があります。
一方で、nonce / IV、AAD、認証タグは基本的には秘密にする値ではありません。
しかし、nonce / IVを使い回したり、AADに秘密情報を入れたり、認証タグの検証を無視したりすると、安全性は大きく損なわれます。
11.6 実装するときの考え方
実装時には、次のような流れで考えると整理しやすいです。
この流れを見ると、暗号化処理は「データを読めなくする」だけではないことが分かります。
実際には、次のような判断が必要になります。
- 何を暗号化対象にするのか
- 何をAADとして扱うのか
- nonce / IVをどう生成し、どう保存するのか
- 認証タグ検証に失敗したとき、どう処理するのか
- 鍵をどこに保存し、どう更新・失効するのか
ここまで考えると、暗号技術はアルゴリズム名だけでなく、設計と運用まで含めて見る必要があることが分かります。
11.7 この章のまとめ
この章では、AES-GCMやAEADでつまずきやすい nonce / IV、AAD、認証タグを整理しました。
nonce / IVは、暗号化ごとに変える値です。
秘密にする必要は基本的にありませんが、同じ鍵のもとで使い回してはいけません。
AADは、暗号化しないが改ざん検知したい関連データです。
見える前提のデータなので、秘密情報を入れるべきではありません。
認証タグは、暗号文やAADが途中で書き換えられていないか確認するための値です。
検証に失敗した場合、その復号結果は使ってはいけません。
AES-GCMは便利な方式ですが、これらの値を正しく扱うことが前提です。
次の章では、ここまでの内容を踏まえて、共通鍵暗号を実務で使うときの注意点を整理します。
12. 実務で使うときの注意点
ここまで、共通鍵暗号、AES、暗号利用モード、認証付き暗号、AEAD、AES-GCMを順番に見てきました。
ここまで読むと、「AES-GCMを使えばだいたい安全なのでは?」と感じるかもしれません。
たしかに、AES-GCMのような認証付き暗号を使うことは、現代的な設計ではとても重要です。
ただし、実務で暗号技術を使うときは、アルゴリズム名だけでは安全性は決まりません。
たとえば、次のようなミスがあると、AES-GCMを使っていても安全性が大きく下がる可能性があります。
- 秘密鍵をソースコードに直接書いてしまう
- 同じ鍵とnonceの組み合わせを再利用してしまう
- 認証タグの検証失敗を無視してしまう
- AADに秘密情報を入れてしまう
- 古い暗号方式や危険なモードを何となく使い続けてしまう
- 鍵の更新・失効・削除の方法を決めていない
この章では、共通鍵暗号を実務や個人開発で扱うときに、特に注意したい点を整理します。
コードの書き方そのものよりも、安全に使うための考え方を中心に見ていきます。
12.1 まず「何を守りたいのか」を決める
暗号技術を使う前に、まず考えるべきことは「どのアルゴリズムを使うか」ではありません。
最初に考えるべきなのは、何を守りたいのかです。
たとえば、次のように目的によって必要な技術は変わります。
| 守りたいこと | 例 | 関係する技術 |
|---|---|---|
| 中身を読まれたくない | 保存ファイル、通信内容、個人情報 | 共通鍵暗号、AES-GCM |
| 途中で書き換えられていないか確認したい | APIリクエスト、支払い内容 | 認証タグ、MAC、AEAD |
| 誰が送ったものか確認したい | サーバー間通信、署名付きリクエスト | MAC、デジタル署名 |
| 後から検証できる証拠を残したい | 電子契約、ブロックチェーン | デジタル署名 |
| パスワード漏えい時の被害を抑えたい | ログイン機能 | パスワードハッシュ |
共通鍵暗号は、主に「中身を読まれないようにする」ために使われます。
AES-GCMのような認証付き暗号を使えば、そこに「改ざん検知」も加えることができます。
ただし、すべての問題を共通鍵暗号だけで解決できるわけではありません。
たとえば、第三者に対して「この人が確かに署名した」と示したい場合は、MACよりもデジタル署名の方が向いていることがあります。
つまり、暗号技術を使うときは、まず次の順番で考えると整理しやすいです。
この流れを飛ばして、いきなり「とりあえずAESで暗号化しよう」と考えると、目的に合わない使い方になってしまう可能性があります。
12.2 独自暗号を作らない
暗号技術を学んでいると、自分で簡単な暗号方式を作ってみたくなることがあります。
学習目的であれば、シーザー暗号やXORのような簡単な例を試すことは理解の助けになります。
しかし、実際にデータを守る目的で独自暗号を作るのは避けるべきです。
暗号は、見た目にはランダムに見えても、安全とは限りません。
攻撃者がどのような情報を持っているか、同じ鍵で何度使われるか、エラー時にどのような情報が漏れるかなど、考えるべき点が非常に多いためです。
たとえば、次のようなコードは、学習用には暗号化の雰囲気をつかめますが、実際の保護には使うべきではありません。
# 学習用の例です。
# 実際のデータ保護には使わないでください。
message = b"secret message"
key = b"mykey"
# 鍵を繰り返して、メッセージと同じ長さにする
# これは安全な鍵ストリーム生成ではありません
repeated_key = (key * ((len(message) // len(key)) + 1))[:len(message)]
# 各バイトをXORする
# XORの仕組みを理解するための例であり、実用的な暗号方式ではありません
ciphertext = bytes(m ^ k for m, k in zip(message, repeated_key))
# 同じ処理をもう一度行うと元に戻る
restored = bytes(c ^ k for c, k in zip(ciphertext, repeated_key))
print(ciphertext)
print(restored)
このコードは、XORの性質を学ぶには分かりやすいです。
しかし、鍵が短く、繰り返し使われており、認証タグもなく、nonceの概念もありません。
実際のセキュリティ目的では不適切です。
OWASP Cryptographic Storage Cheat Sheetでも、独自アルゴリズムを使うのではなく、標準的で広く検証された暗号方式を使うことが推奨されています。
参考: OWASP Cryptographic Storage Cheat Sheet
💡 豆知識
暗号の世界には「自分で暗号を作らない」という有名な考え方があります。
これは、暗号の仕組みを学ぶなという意味ではありません。
学習用に小さな例を作ることと、実際の秘密情報を守るために自作方式を使うことは、まったく別の話です。
12.3 できるだけ認証付き暗号を使う
ここまでの記事で何度も見てきたように、暗号化だけでは改ざん検知まで保証できません。
そのため、現代的な設計では、可能であれば認証付き暗号を使うことを検討します。
OWASP Cryptographic Storage Cheat Sheetでは、利用可能な場合は認証付きモードを使うべきであり、GCMやCCMが一般的な認証付きモードとして挙げられています。
また、CTRやCBCのようなモードを使う場合、それらはデータの真正性を保証しないため、Encrypt-then-MACのような別途認証が必要になると説明されています。
参考: OWASP Cryptographic Storage Cheat Sheet
ここで大切なのは、次の違いです。
| 方式の考え方 | 守れること | 注意点 |
|---|---|---|
| 暗号化だけ | 中身を読まれにくくする | 改ざん検知は別途必要 |
| 暗号化 + MAC | 中身を隠し、改ざんも検知する | 組み合わせ方を間違えない必要がある |
| AEAD | 暗号化と認証をまとめて扱う | nonce / IVや認証タグの扱いが重要 |
初学者向けには、まず次の理解で十分です。
新しく設計するなら、暗号化だけを単独で考えるのではなく、AES-GCMのようなAEADを優先して検討する。
ただし、これは「どんな場面でもAES-GCMだけ使えばよい」という意味ではありません。
既存システムとの互換性、利用するライブラリ、対象環境、規格要件などによって選択は変わります。
そのため、実務では自分だけで判断せず、組織のセキュリティ基準、利用中のフレームワーク、クラウドサービスの推奨設定、公式ドキュメントを確認する必要があります。
12.4 鍵をソースコードに直接書かない
共通鍵暗号で一番大切なものは、共通鍵です。
AES-GCMを正しく使っていても、鍵が漏えいすれば、守りたいデータを復号される可能性があります。
特に避けたいのが、鍵をソースコードに直接書くことです。
# 避けたい例です。
# 鍵をソースコードに直接書くと、GitHubなどに誤って公開される危険があります。
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
# これは例としても危険な書き方です
# 実際のコードでは、秘密鍵をコードに直書きしないでください
key = b"0123456789abcdef0123456789abcdef"
aesgcm = AESGCM(key)
このように書くと、ソースコードを共有したり、GitHubにpushしたり、ログやバックアップに残ったりしたときに、鍵も一緒に漏れてしまう可能性があります。
学習用コードでは、実行時に鍵を生成する形にしていました。
しかし、実務では生成した鍵をどう保存するか、誰がアクセスできるか、漏えい時にどう更新するかまで考える必要があります。
たとえば、ローカル開発の最小例では、環境変数から鍵を読み込む形にすると、少なくともソースコードへの直書きは避けられます。
import base64
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
# 環境変数からBase64形式の鍵を読み込む
# 例: export APP_AES_KEY_BASE64="..."
key_b64 = os.environ.get("APP_AES_KEY_BASE64")
# 鍵が設定されていない場合は、明示的にエラーにする
# 鍵がない状態で暗号化処理を進めないようにする
if key_b64 is None:
raise RuntimeError("APP_AES_KEY_BASE64 が設定されていません")
# Base64文字列をバイト列に戻す
key = base64.b64decode(key_b64)
# AESGCMで使える鍵長か確認する
# AESGCMでは128ビット、192ビット、256ビット鍵を使える
if len(key) not in (16, 24, 32):
raise ValueError("AES-GCMの鍵は16, 24, 32バイトのいずれかである必要があります")
# 鍵を使ってAESGCMオブジェクトを作成する
aesgcm = AESGCM(key)
ただし、環境変数を使えば完全に安全というわけではありません。
環境変数はプロセス情報や設定ミスによって漏れる可能性があります。
本番環境では、クラウドのKMS、シークレット管理サービス、HSMなど、より適切な鍵管理の仕組みを検討します。
NIST SP 800-57 Part 1 Rev.5は、暗号鍵材料の管理に関する一般的なガイダンスとベストプラクティスを扱う文書です。
参考: NIST SP 800-57 Part 1 Rev.5 - Recommendation for Key Management
12.5 nonce / IVを「毎回違う値」として管理する
AES-GCMでは、同じ鍵とnonceの組み合わせを再利用しないことが非常に重要です。
cryptography の公式ドキュメントでも、同じ鍵とnonceの組み合わせを再利用すると、その組み合わせを使ったメッセージの安全性が損なわれると説明されています。
参考: cryptography - Authenticated encryption
学習用コードでは、次のように os.urandom(12) でnonceを生成しました。
import os
# AES-GCMでよく使われる96ビット、つまり12バイトのnonceを生成する
# 同じ鍵では、同じnonceを再利用しない
nonce = os.urandom(12)
このコード自体は学習用として分かりやすいです。
ただし、実務では「nonceを生成する」だけでなく、次の点も考える必要があります。
| 観点 | 確認すること |
|---|---|
| 一意性 | 同じ鍵で同じnonceを再利用しない設計になっているか |
| 保存方法 | 復号時に必要なnonceを暗号文と一緒に保存しているか |
| 並列処理 | 複数サーバーで同じnonceが生成されないか |
| 鍵更新 | 鍵を変えたときにnonce管理も整理されているか |
| ライブラリ仕様 | nonceの推奨長や制約を公式ドキュメントで確認しているか |
nonce / IVは秘密にする値ではありません。
そのため、暗号文や認証タグと一緒に保存・送信されることがあります。
ただし、秘密ではないからといって、使い回してよいわけではありません。
この点は、AES-GCMを使うときの特に重要な注意点です。
12.6 認証タグの検証失敗を無視しない
AES-GCMでは、復号時に認証タグの検証が行われます。
この検証に失敗した場合、そのデータは改ざんされている、または鍵・nonce・AADの組み合わせが正しくない可能性があります。
このとき、絶対に避けたいのは「エラーが出たけれど、読める部分だけ使う」という考え方です。
import os
from cryptography.exceptions import InvalidTag
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
key = AESGCM.generate_key(bit_length=256)
aesgcm = AESGCM(key)
nonce = os.urandom(12)
plaintext = b"important data"
# 暗号化する
ciphertext = aesgcm.encrypt(nonce, plaintext, None)
try:
# 復号時に認証タグの検証も行われる
restored = aesgcm.decrypt(nonce, ciphertext, None)
except InvalidTag:
# 認証タグ検証に失敗した場合は、復号結果を使わない
# ログには秘密情報を出さず、必要に応じて監視・調査につなげる
raise RuntimeError("暗号文または関連データを検証できませんでした")
else:
# 検証に成功した場合だけ復号結果を利用する
print(restored)
この例では、decrypt() が成功した場合だけ復号結果を使っています。
検証に失敗した場合は、復号結果を使わず、エラーとして扱います。
また、エラーメッセージやログに、鍵、平文、復号途中のデータを出さないことも重要です。
ログは調査に役立つ一方で、秘密情報を漏らす経路にもなり得ます。
12.7 AADに秘密情報を入れない
AADは、暗号化されないが改ざん検知したい関連データです。
前章でも説明したように、AADは認証タグの計算には含まれますが、暗号化はされません。
そのため、AADには秘密情報を入れないようにします。
| AADに向きやすい例 | AADに入れるべきでない例 |
|---|---|
| APIバージョン | パスワード |
| リクエスト種別 | アクセストークン |
| メッセージ種別 | 個人番号や秘密情報 |
| テナントIDや公開メタデータ | 秘密鍵 |
| 暗号文の用途を示す公開情報 | クレジットカード番号そのもの |
ここで注意したいのは、「AADに入れると改ざん検知される」ことと、「AADが隠される」ことは別だという点です。
AADは見える前提の値として扱います。
12.8 鍵のライフサイクルを考える
共通鍵暗号では、鍵を作った瞬間だけでなく、その後の扱いも重要です。
鍵には、生成、保存、利用、更新、失効、削除といったライフサイクルがあります。
それぞれの段階で、考えるべきことがあります。
| 段階 | 注意点 |
|---|---|
| 生成 | 十分な乱数を使う。固定値や推測しやすい値を使わない。 |
| 保存 | ソースコードや平文設定ファイルに置かない。権限を絞る。 |
| 利用 | 必要な処理だけで使う。ログに出さない。 |
| 更新 | 定期更新や漏えい時の更新手順を考える。 |
| 失効 | 古い鍵を使えないようにする。影響範囲を整理する。 |
| 削除 | 不要な鍵を残し続けない。バックアップにも注意する。 |
NIST SP 800-57 Part 1 Rev.5は、暗号鍵材料の管理に関する一般的なガイダンスを提供しており、暗号技術を実システムで使う際には鍵管理が重要であることを示しています。
参考: NIST SP 800-57 Part 1 Rev.5 - Recommendation for Key Management
12.9 暗号方式を固定しすぎない
暗号技術は、一度決めたら永久に変わらないものではありません。
標準化、攻撃研究、計算機性能、ライブラリの更新によって、推奨される方式や設定は変わることがあります。
そのため、実務では「今どの方式を使うか」だけでなく、将来変更できるかも考える必要があります。
たとえば、暗号方式や鍵IDをデータと一緒に管理しておくと、将来の移行時に役立つことがあります。
{
"alg": "AES-256-GCM",
"key_id": "key-2026-06",
"nonce": "base64-encoded-nonce",
"aad": "public-metadata",
"ciphertext": "base64-encoded-ciphertext-and-tag"
}
このJSONは、あくまで考え方を示す例です。
実際の設計では、利用するライブラリやプロトコル、保存形式、運用ルールに合わせて決める必要があります。
ここで伝えたいのは、暗号文だけを雑に保存するのではなく、復号や移行に必要な情報を整理しておくことです。
ただし、鍵そのものをこのJSONに入れてはいけません。
💡 豆知識
暗号方式を後から交換しやすくしておく考え方は、暗号アジリティと呼ばれることがあります。
これは、将来の標準更新や移行に備えるための設計上の考え方です。
12.10 実務でのチェックリスト
最後に、共通鍵暗号やAES-GCMを使うときの確認項目をまとめます。
| チェック項目 | 確認すること |
|---|---|
| 目的 | 何を守りたいのか。機密性だけか、完全性・真正性も必要か。 |
| 方式 | 独自暗号ではなく、標準的な方式・ライブラリを使っているか。 |
| モード | 可能ならGCMやCCMなどの認証付きモードを使っているか。 |
| 鍵 | 鍵を十分な乱数で生成し、安全に保存しているか。 |
| 鍵の直書き | ソースコードやGitHubに秘密鍵を含めていないか。 |
| nonce / IV | 同じ鍵でnonce / IVを再利用しない設計か。 |
| AAD | AADに秘密情報を入れていないか。復号時に同じAADを使っているか。 |
| 認証タグ | 検証失敗時に復号結果を使わない設計か。 |
| ログ | 鍵、平文、復号結果、秘密情報をログに出していないか。 |
| 更新 | 鍵の更新・失効・削除手順を考えているか。 |
| 移行 | 将来の方式変更に備えて、アルゴリズム名や鍵IDを管理しているか。 |
このチェックリストを見ても分かるように、暗号技術を安全に使うには、アルゴリズムだけでなく、設計・実装・運用がすべて関係します。
12.11 この章のまとめ
この章では、共通鍵暗号を実務で使うときの注意点を整理しました。
AES-GCMのような認証付き暗号を使うことは重要です。
しかし、それだけで安全が保証されるわけではありません。
独自暗号を避け、標準的なライブラリを使うこと。
鍵をソースコードに直接書かないこと。
同じ鍵とnonceの組み合わせを再利用しないこと。
認証タグの検証失敗を無視しないこと。
AADに秘密情報を入れないこと。
そして、鍵の生成から削除までのライフサイクルを考えること。
このような点を意識してはじめて、AESやAES-GCMのような暗号技術を安全なシステム設計に近づけることができます。
次の章では、ここまでの内容を踏まえて、初学者が特に混同しやすいポイントをまとめます。
13. よくある誤解
前の章では、共通鍵暗号を実務で使うときの注意点を整理しました。
ここまで、AES、暗号利用モード、認証付き暗号、AEAD、AES-GCM、nonce / IV、AAD、認証タグ、鍵管理と、かなり多くの用語が出てきました。
暗号技術は、1つ1つの用語だけを見ると分かった気になりやすい一方で、使う場面を取り違えると安全性につながりません。
そこでこの章では、初学者が特に混同しやすいポイントをまとめます。
ここでは新しい難しい話を増やすというより、これまでの内容を読み返すための「確認ポイント」として整理します。
13.1 まず全体像を見る
共通鍵暗号まわりで混同しやすいポイントは、次のように整理できます。
| よくある誤解 | 正しくは |
|---|---|
| AESを使えば、それだけで安全 | AESは重要な部品だが、モード、nonce / IV、認証タグ、鍵管理も必要 |
| AES-256なら何も考えなくてよい | 鍵長だけでなく、使い方や運用が安全性に大きく関係する |
| 暗号化すれば改ざんも防げる | 改ざん検知にはMAC、認証タグ、AEADなどが必要 |
| AES-GCMはAESそのもの | AES-GCMはAESを使う認証付き暗号モード |
| nonce / IVは秘密にする値 | 秘密である必要はないが、同じ鍵での再利用を避ける必要がある |
| AADは暗号化される | AADは暗号化されず、改ざん検知の対象になる関連データ |
| 認証タグはおまけ | 認証タグは改ざん検知に必要な重要な値 |
| ライブラリを使えば必ず安全 | 設定、鍵管理、エラー処理、ログ出力を間違えると危険 |
| 鍵を環境変数に置けば常に十分 | 環境変数は一つの方法にすぎず、権限管理やローテーションも必要 |
| 暗号は自作した方が理解が深まる | 学習目的の実装と実運用の実装は分ける。実運用では検証済みライブラリを使う |
図にすると、暗号技術でつまずきやすい場所は、だいたい次のような関係にあります。
この図で伝えたいのは、AESだけが単独で安全性を決めているわけではない、ということです。
暗号技術は、アルゴリズム、使い方、鍵管理、エラー処理、運用がつながって初めて意味を持ちます。
13.2 誤解1:AESを使えば、それだけで安全
AESは、現在の共通鍵暗号を学ぶうえで非常に重要な方式です。
NIST FIPS 197では、AES-128、AES-192、AES-256が定義され、いずれも128ビットブロックを処理するブロック暗号として規定されています。
参考: NIST FIPS 197 - Advanced Encryption Standard (AES)
ただし、AESというアルゴリズム名だけで安全性が決まるわけではありません。
たとえば、同じAESでも、使うモードが不適切だったり、nonce / IVを再利用したり、鍵をソースコードに直接書いたりすると、安全性は大きく下がります。
| 見るべき観点 | なぜ必要か |
|---|---|
| 暗号利用モード | AESを長いデータにどう適用するかを決めるため |
| nonce / IV | 同じ入力が同じように見えないようにしたり、モードの安全性条件を満たしたりするため |
| 認証タグ | 暗号文やAADが改ざんされていないか確認するため |
| 鍵管理 | 鍵が漏えいすると、暗号化したデータを守れなくなるため |
| エラー処理 | 検証失敗時に危険なデータを使わないため |
たとえるなら、AESは頑丈な金庫のようなものです。
しかし、金庫が頑丈でも、鍵を玄関前に置いていたら意味がありません。
暗号でも同じで、アルゴリズムだけでなく、使い方と運用まで含めて考える必要があります。
13.3 誤解2:AES-256なら何も考えなくてよい
AES-256は、256ビット鍵を使うAESです。
鍵長が長いという意味ではAES-128より大きな鍵空間を持ちます。
ただし、ここで注意したいのは、鍵長が長ければ、使い方を間違えても安全になるわけではないという点です。
たとえば、次のような問題は、AES-256を使っていても防げません。
- 鍵をGitHubに誤って公開してしまう
- 同じ鍵とnonceを再利用してしまう
- 認証タグの検証失敗を無視してしまう
- 古い鍵をいつまでも使い続ける
- 暗号化すべきでない場所に秘密情報をログ出力してしまう
OWASP Cryptographic Storage Cheat Sheetでは、AESについて少なくとも128ビット、理想的には256ビットの鍵と安全なモードを使うことが説明されています。
ただし、その資料でも鍵管理やモード選択などが重要な観点として扱われています。
参考: OWASP Cryptographic Storage Cheat Sheet
つまり、AES-256は重要な選択肢ですが、「AES-256だから設計や運用を考えなくてよい」という意味ではありません。
13.4 誤解3:暗号化すれば改ざんも防げる
暗号化は、主にデータを読まれにくくするための処理です。
しかし、データが途中で書き換えられていないかを確認するには、完全性や真正性の確認が必要です。
ここは、初学者がかなり混同しやすいポイントです。
| 守りたいこと | 関係する考え方 |
|---|---|
| 中身を読まれたくない | 暗号化、機密性 |
| 中身が変わっていないか確認したい | MAC、認証タグ、完全性 |
| 正しい相手が作ったものか確認したい | MAC、署名、真正性 |
RFC 5116では、Authenticated Encryptionは平文の機密性に加えて、平文の完全性・真正性を確認する方法を提供すると説明されています。
また、AEADでは、暗号化されない関連データも完全性・真正性の確認対象にできます。
参考: RFC 5116 - An Interface and Algorithms for Authenticated Encryption
暗号化を「封筒に入れること」と考えると、封筒に入れるだけでは、中身が途中で差し替えられていないかまでは分かりません。
そのため、封印や検査印にあたる仕組みが必要になります。
AES-GCMの認証タグは、この「改ざんされていないかを確認するための検査値」として理解すると分かりやすいです。
13.5 誤解4:AES-GCMはAESそのもの
AES-GCMという名前を見ると、「AESの少し強いバージョン」のように感じるかもしれません。
しかし、AES-GCMはAESそのものではありません。
AES-GCMは、ざっくり言うと AESを使った認証付き暗号モード です。
| 用語 | ざっくりした意味 |
|---|---|
| AES | 128ビットブロックを処理する共通鍵ブロック暗号 |
| GCM | 暗号化と認証を組み合わせる利用モード |
| AES-GCM | AESを使い、GCMによって暗号化と改ざん検知を行う方式 |
| GMAC | GCMを認証だけに使う特殊化 |
NIST SP 800-38Dでは、GCMはAEADのためのモードとして規定されています。
参考: NIST SP 800-38D - GCM and GMAC
つまり、AES-GCMを理解するには、AESだけでなく、GCM、AEAD、nonce / IV、AAD、認証タグも一緒に見る必要があります。
13.6 誤解5:nonce / IVは秘密にする値
nonce / IVは、暗号処理で使う補助的な値です。
AES-GCMでは、nonce / IVは秘密にする値というより、同じ鍵のもとで使い回さないことが非常に重要な値です。
もちろん、システムによってはnonce / IVの扱い方に設計上の注意が必要です。
しかし、基本的な理解としては、鍵のように秘密にする値ではなく、暗号文と一緒に保存・送信されることもあります。
import os
# AES-GCMでは、96ビット、つまり12バイトのnonceがよく使われる
# この値は秘密鍵ではないが、同じ鍵で再利用してはいけない
nonce = os.urandom(12)
このコードは、学習用にランダムな12バイトのnonceを生成する例です。
実際のシステムでは、ランダム生成だけでなく、重複しない設計や保存形式も含めて考える必要があります。
cryptography のAESGCMドキュメントでも、同じ鍵でnonceを再利用してはいけないことが説明されています。
参考: cryptography - Authenticated encryption
13.7 誤解6:AADは暗号化される
AADは、Additional Authenticated Dataの略です。
日本語では「追加認証データ」や「関連データ」と説明されることがあります。
AADは、暗号化はしないが、改ざんされていないか確認したいデータです。
たとえば、API通信で次のような情報を考えます。
| データ | 暗号化したいか | 改ざん検知したいか |
|---|---|---|
| メッセージ本文 | はい | はい |
| リクエスト種別 | いいえ | はい |
| APIバージョン | いいえ | はい |
| パスワード | はい | はい |
このうち、リクエスト種別やAPIバージョンのような値は、処理の都合上、外から見えていてもよい場合があります。
しかし、途中で勝手に書き換えられると困ります。
このような値をAADとして扱うことで、暗号化はしないまま、認証タグの検証対象に含めることができます。
ただし、AADは暗号化されないため、秘密情報を入れてはいけません。
13.8 誤解7:認証タグはおまけ
AES-GCMを使うと、暗号文と一緒に認証タグが扱われます。
この認証タグは、おまけではありません。
認証タグは、暗号文やAADが改ざんされていないかを確認するための重要な値です。
復号時には、鍵、nonce、AAD、暗号文、認証タグの組み合わせが正しいかを検証します。
認証タグの検証に失敗した場合、復号結果を使ってはいけません。
from cryptography.exceptions import InvalidTag
try:
# decrypt() の中で認証タグの検証も行われる
plaintext = aesgcm.decrypt(nonce, ciphertext, aad)
except InvalidTag:
# タグ検証に失敗した場合、復号結果を使わない
# 鍵・nonce・AAD・暗号文のどれかが合っていない可能性がある
raise RuntimeError("暗号文を検証できませんでした")
この例では、InvalidTag が発生した場合に復号結果を使わないようにしています。
実務では、エラー処理時に鍵や平文をログに出さないことも重要です。
13.9 誤解8:ライブラリを使えば必ず安全
暗号技術を自作しないことは重要です。
実務では、標準的で検証されたライブラリを使うべきです。
ただし、ライブラリを使っているからといって、必ず安全になるわけではありません。
たとえば、次のような使い方をすると危険です。
| 危険な使い方 | なぜ危険か |
|---|---|
| サンプルコードの鍵をそのまま使う | 誰でも同じ鍵を知っている状態になるため |
| nonceを固定値にする | 同じ鍵とnonceの再利用につながるため |
| 認証タグ検証のエラーを握りつぶす | 改ざんされた可能性のあるデータを使ってしまうため |
| 秘密鍵や平文をログに出す | ログ経由で情報漏えいする可能性があるため |
| 古い方式や非推奨設定を使い続ける | 既知の攻撃や標準更新に追従できないため |
ライブラリは、安全な部品を提供してくれます。
しかし、その部品をどう組み合わせ、どのように鍵を管理し、エラー時にどう扱うかは、利用する側の設計に関係します。
13.10 誤解9:暗号化して保存していれば鍵管理はあまり重要ではない
保存データを暗号化することは大切です。
しかし、その暗号化に使う鍵が漏えいすれば、暗号化されたデータを守れなくなる可能性があります。
つまり、暗号化と鍵管理はセットで考える必要があります。
NIST SP 800-57 Part 1 Rev.5は、暗号鍵材料の管理に関する一般的なガイダンスを提供しています。
参考: NIST SP 800-57 Part 1 Rev.5 - Recommendation for Key Management
鍵管理では、少なくとも次のような点を考えます。
| 観点 | 考えること |
|---|---|
| 生成 | 十分な乱数で鍵を作る |
| 保存 | ソースコードや公開リポジトリに置かない |
| 権限 | 必要な人・処理だけが使えるようにする |
| 更新 | 漏えい時や期限到来時に切り替えられるようにする |
| 失効 | 古い鍵を使えないようにする |
| 削除 | 不要になった鍵を残し続けない |
暗号化されたデータだけを見るのではなく、「その鍵を誰が、どこで、どう使えるのか」まで考えることが大切です。
13.11 誤解10:暗号は自分で実装した方がよい
暗号アルゴリズムを学習目的で実装してみることは、仕組みを理解するうえで役に立つ場合があります。
たとえば、AESのラウンド処理や、CTRモードのカウンタの考え方を小さなコードで追うと、理解は深まります。
ただし、学習用の実装と実運用の実装は分けて考える必要があります。
実運用では、暗号アルゴリズムを自作するのではなく、標準的なライブラリやプロトコルを使うべきです。
暗号実装では、アルゴリズムが合っているように見えても、乱数、パディング、エラー処理、タイミング差、鍵管理など、多くの落とし穴があります。
OWASP Cryptographic Storage Cheat Sheetでも、独自の暗号アルゴリズムを作るべきではないという考え方が示されています。
参考: OWASP Cryptographic Storage Cheat Sheet
💡 豆知識
暗号の世界では、アルゴリズムを秘密にすることで安全性を保つ考え方は基本的に避けられます。
標準化された暗号方式は、アルゴリズムが公開され、多くの専門家による検討を受けることを前提にしています。
そのため、「自分だけが知っている独自方式だから安全」と考えるのは危険です。
13.12 この章のまとめ
この章では、共通鍵暗号、AES、AES-GCMを学ぶときに混同しやすいポイントを整理しました。
特に大切なのは、次の3つです。
-
AESという名前だけで安全性は決まらない
モード、nonce / IV、認証タグ、鍵管理まで含めて考える必要があります。 -
暗号化と改ざん検知は別の観点
中身を隠すだけでなく、途中で書き換えられていないかを確認するために、認証付き暗号やAEADが重要になります。 -
実装と運用を分けずに考える
標準的なライブラリを使っても、鍵管理、エラー処理、ログ出力、方式移行を間違えると安全性は下がります。
ここまでで、共通鍵暗号の基本から、AES、暗号利用モード、AEAD、AES-GCM、実務上の注意点まで一通り見てきました。
次の章では、この記事全体の内容を振り返り、共通鍵暗号を学ぶときに押さえておきたいポイントをまとめます。
14. まとめ
ここまで、身近なサービスの裏側で使われる共通鍵暗号について、AES、暗号利用モード、認証付き暗号、AEAD、AES-GCMという流れで整理してきました。
最初は、ネットショッピング、スマホ決済、クラウド保存のような身近な例から入りました。
そこから、「データを読まれないようにするには何が必要か」「暗号化だけで十分なのか」「実際に安全に使うには何に注意すべきか」を順番に見てきました。
この記事で一番伝えたいことは、共通鍵暗号はAESという名前だけを覚えて終わりではないということです。
AESはとても重要な共通鍵暗号です。
NIST FIPS 197として標準化されており、現在の共通鍵暗号を学ぶうえで、まず押さえておきたい基本的な方式です。
ただし、実際のシステムでは、AESそのものだけで安全性が決まるわけではありません。
暗号利用モード、nonce / IV、AAD、認証タグ、鍵管理、ライブラリの使い方まで含めて考える必要があります。
14.1 共通鍵暗号は「大量データをすばやく守る」ための基本技術
共通鍵暗号は、暗号化と復号に同じ秘密鍵を使う方式です。
イメージとしては、同じ合鍵を持っている人だけが開けられる箱に近いです。
この仕組みは、大量のデータを効率よく暗号化する場面に向いています。
たとえば、HTTPS通信では、通信の最初に鍵共有などの仕組みを使って安全に鍵を準備し、その後の通信データは共通鍵系の暗号技術で守られます。
TLS 1.3を定義するRFC 8446でも、TLSは通信の盗聴、改ざん、メッセージ偽造を防ぐよう設計されたプロトコルとして説明されています。
参考: RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3
ここで大切なのは、共通鍵暗号を「単体で完結する技術」として見ないことです。
実際には、鍵共有、認証、改ざん検知、鍵管理などと組み合わせて使われます。
この図のように、共通鍵暗号は暗号技術の中でも重要な土台ですが、それだけで安全なシステムが完成するわけではありません。
14.2 AESを学ぶときは「方式」と「使い方」を分けて考える
AESは、代表的な共通鍵ブロック暗号です。
AES-128、AES-192、AES-256という名前を見たことがある人も多いと思います。
ここで混同しやすいのは、AES-128 / AES-192 / AES-256 の数字がブロックサイズではなく、鍵長を表していることです。
AESはいずれも128ビットのブロックを処理し、鍵長が128ビット、192ビット、256ビットで異なります。
ただし、AESは「128ビットのブロックを変換する部品」です。
実際に長い文章、画像、ファイル、通信データを扱うには、ブロックをどのようにつなげて処理するかを決める必要があります。
そのために出てくるのが、暗号利用モードです。
| 観点 | ざっくりした説明 |
|---|---|
| AES | データをブロック単位で変換する共通鍵暗号 |
| 暗号利用モード | AESを長いデータにどう適用するかを決めるルール |
| AES-GCM | AESを使い、暗号化と改ざん検知をまとめて行う方式 |
NIST SP 800-38Aでは、ブロック暗号の秘匿用モードとしてECB、CBC、CFB、OFB、CTRが定義されています。
参考: NIST SP 800-38A - Recommendation for Block Cipher Modes of Operation
一方で、現代的な実装では、単に「暗号化できる」だけでなく、「改ざんされていないか確認できる」ことも重要です。
そのため、AES-GCMのような認証付き暗号がよく登場します。
14.3 暗号化だけでは「書き換えられていないこと」までは確認できない
この記事の中盤では、暗号化と改ざん検知の違いを整理しました。
暗号化は、第三者に中身を読まれにくくするためのものです。
しかし、それだけでは「途中で書き換えられていないこと」まで十分に確認できるとは限りません。
たとえば、封筒に入った手紙を考えると分かりやすいです。
封筒に入っていれば中身は見えにくくなります。
しかし、封筒が途中で開けられて中身を書き換えられた場合、それに気づける仕組みがなければ困ります。
暗号の世界でも同じです。
| 守りたいこと | 必要になる考え方 |
|---|---|
| 中身を読まれないようにしたい | 暗号化 |
| 中身が書き換えられていないか確認したい | MAC、認証タグ、AEAD |
| 暗号化しない関連情報も含めて確認したい | AADを扱えるAEAD |
RFC 5116では、AEADは平文の機密性に加え、平文と関連データの完全性・真正性を確認する仕組みとして整理されています。
参考: RFC 5116 - An Interface and Algorithms for Authenticated Encryption
この考え方を知っておくと、「AESで暗号化しているから大丈夫」という理解から一歩進んで、「どのように改ざん検知しているのか」まで見られるようになります。
14.4 AES-GCMでは nonce / IV・AAD・認証タグが重要になる
AES-GCMを学ぶと、AES以外にもいくつかの用語が出てきます。
特に重要なのが、次の3つです。
| 用語 | 役割 | 注意点 |
|---|---|---|
| nonce / IV | 暗号処理ごとに使う補助的な値 | 同じ鍵で使い回さない |
| AAD | 暗号化しないが改ざん検知したい関連データ | 秘密情報を入れる場所ではない |
| 認証タグ | 改ざんされていないか確認する検査値 | 検証失敗時は復号結果を使わない |
AES-GCMでは、暗号文だけでなく、AADや認証タグも含めて正しく扱う必要があります。
cryptography の AESGCM ドキュメントでも、同じ鍵とnonceの組み合わせを再利用してはいけないことが説明されています。
参考: cryptography - Authenticated encryption
ここは、実装時に特に注意したいポイントです。
コードが短く書けると、つい「これで安全にできた」と思いたくなります。
しかし、nonceの生成方法、鍵の保存場所、認証タグ検証の失敗時の扱いを間違えると、安全性が大きく崩れる可能性があります。
14.5 実務では「暗号方式」だけでなく「運用」まで見る
暗号技術を学んでいると、どうしてもアルゴリズム名に目が行きます。
AES、GCM、ChaCha20-Poly1305、HMAC、SHA-256。
どれも重要な名前です。
ただし、実務では「どの暗号方式を使うか」だけではなく、次のような運用面も同じくらい重要になります。
| 観点 | 確認したいこと |
|---|---|
| 鍵生成 | 十分な乱数で鍵を作っているか |
| 鍵保存 | ソースコードや公開リポジトリに置いていないか |
| 鍵の権限 | 必要な処理だけが鍵を使えるか |
| nonce管理 | 同じ鍵でnonceを再利用しない設計になっているか |
| タグ検証 | 失敗時に復号結果を使っていないか |
| ログ | 秘密鍵や平文を出力していないか |
| 更新・失効 | 漏えい時や方式変更時に切り替えられるか |
NIST SP 800-57 Part 1 Rev.5は、暗号鍵材料の管理に関する一般的なガイダンスを提供しています。
また、OWASP Cryptographic Storage Cheat Sheetでも、鍵管理や暗号方式の選択、独自暗号を避けることなどが整理されています。
参考: NIST SP 800-57 Part 1 Rev.5 - Recommendation for Key Management
参考: OWASP Cryptographic Storage Cheat Sheet
暗号技術は、数学的な仕組みだけでなく、設計と運用の技術でもあります。
14.6 この記事で押さえたこと
最後に、この記事で見てきた内容をまとめます。
| 章 | 内容 | 押さえたいポイント |
|---|---|---|
| 1章 | 身近なサービスの裏側 | 通信中・保存中のデータを守る必要がある |
| 2章 | 共通鍵暗号の基本 | 同じ鍵で暗号化・復号する |
| 3章 | 使われる場面 | HTTPS、クラウド保存、API通信などで使われる |
| 4章 | AES | 代表的な共通鍵ブロック暗号 |
| 5章 | AESだけでは不十分な理由 | モード、改ざん検知、鍵管理が必要 |
| 6章 | 暗号利用モード | ブロック暗号を実データに適用するルール |
| 7章 | 暗号化と改ざん検知 | 機密性と完全性は別の観点 |
| 8章 | 認証付き暗号 | 隠すことと書き換え検知をまとめて扱う |
| 9章 | AEAD | AADも含めて完全性・真正性を確認する |
| 10章 | AES-GCM | AESを使う代表的なAEAD方式 |
| 11章 | つまずきやすい用語 | nonce / IV、AAD、認証タグを整理する |
| 12章 | 実務上の注意点 | 鍵管理、タグ検証、独自実装回避が重要 |
| 13章 | よくある誤解 | 「AESなら安全」と単純化しない |
共通鍵暗号を学ぶときは、まず「中身を読まれないようにする技術」として理解すると入りやすいです。
ただし、そこから一歩進むと、改ざん検知、認証、鍵管理、ライブラリの使い方まで含めて見る必要があります。
14.7 次に学ぶとよさそうなこと
この記事では、共通鍵暗号、AES、認証付き暗号を中心に整理しました。
次に学ぶなら、次のテーマへ進むと理解が広がります。
| 次に学ぶテーマ | なぜつながるか |
|---|---|
| ハッシュ関数 | 改ざん検知や認証タグの理解につながる |
| MAC / HMAC | 共有鍵を使ったメッセージ認証を理解できる |
| デジタル署名 | MACとの違い、第三者検証、否認防止を理解できる |
| TLS 1.3 | 共通鍵暗号、鍵共有、証明書、AEADの組み合わせを理解できる |
| パスワード保存 | 通常の暗号化やハッシュ化との違いを理解できる |
| 鍵管理 | 暗号技術を実際に安全に使うための運用を理解できる |
14.8 最後に
暗号技術は、最初は難しく見えます。
AES、GCM、AEAD、nonce、AAD、認証タグなど、似たような用語が一気に出てくるためです。
ただ、身近なサービスの例から順番に見ていくと、少しずつ役割が分かれて見えてきます。
- 中身を読まれないようにする
- 途中で書き換えられていないか確認する
- 鍵を安全に扱う
- 将来の変更に備える
このように分けて考えると、共通鍵暗号は単なる暗号アルゴリズムの話ではなく、日常のサービスを安全に動かすための大切な土台だと分かります。
この記事が、AESや認証付き暗号を学び始めるときの入口になればうれしいです。