😎

暗号化機能実装時の初期化ベクトルの扱い

2023/01/09に公開約1,800字

※2022年7月に執筆した記事の再公開 (旧ブログから移行)

概要

AES CBCモード等で暗号化機能を実装する際に、初期化ベクトル (IV) を適切に扱うプラクティスを紹介します。

結論

  1. 暗号化を行う度に、IVをランダムに生成する
  2. 暗号化によって得られた暗号文の先頭にIVを付与する (IV + 暗号文 のワンセットを暗号化データとして送信側から受信側に渡す)

1.の内容 (IVを毎回ごとに変える必要がある) は極めて普遍的な事実です。一方2.は、IVをどのように処理するかという具体的な実装に関する取り決めで、当然これ以外にも方法は考えられます。

解説

初期化ベクトルについて

IVを利用する目的は、攻撃者が暗号文の類似性 (同一性) から平文に関する情報を得ることを防ぐためです。したがって、上記「結論」のとおり暗号化の度に異なるIVを利用する必要があります。
同一のIVを繰り返し利用した場合、同一 (類似) の平文から同一 (類似) の暗号文が生成されてしまいます。よって、攻撃者が複数回に渡り暗号文を入手した場合に、入手した暗号文の同一性 (類似性) から平文が同一 (類似) であることを知ることができる可能性があります。
IVが異なっていれば、それぞれのIVを特定できたとしても暗号文から平文の同一性の情報を得ることはできないため、上記のような攻撃を防ぐことができます。

初期化ベクトルの扱い

IVは暗号化鍵と異なり、攻撃者に知られても問題ありません。これは、下記に示すCBCモードの暗号化/復号のアルゴリズムから明白です。

暗号化 (平文から暗号文の第Nブロックを得る式):
ENC(N) = encrypt(PLAIN(N)⊕ENC(N-1), key) (for N≧2),
ENC(1) = encrypt(PLAIN(1)⊕IV, key).

復号 (暗号文から平文の第Nブロックを得る式):
PLAIN(N) = decrypt(ENC(N), key)⊕ENC(N-1) (for N≧2),
PLAIN(1) = decrypt(ENC(1), key)⊕IV.

ここに、
ENC(N): 暗号文の第Nブロック,
PLAIN(N): 平文の第Nブロック,
key: 暗号化鍵,
IV: 初期化ベクトル,
encrypt: 暗号化に相当する関数 (第1引数は暗号化対象データ、第2引数は暗号化鍵),
decrypt: 復号に相当する関数 (第1引数は復号対象データ、第2引数は暗号化鍵).

※IVを「暗号文の第0ブロック」と解釈すれば、N≧2の場合の式をN=1にも拡張できて統一的に理解することができる。

攻撃者はIVを入手できても、鍵を入手しない限り暗号文を復号することは一切できません。よってIVは暗号文とともに (またはその一部として) 扱ってよいと言えます。

実装方法

冒頭で述べた「暗号文の先頭にIVを付与」以外に、暗号文・IVそれぞれをJSONのフィールドとする等の方法も考えられます。この辺りはアプリケーション側の仕様や処理のしやすさを考えて方法を決めるとよいでしょう。

「暗号文の先頭にIVを付与」する方法を採った場合、受信側 (復号側) は受信したデータからIVを抜き出して復号処理を行う必要があります。
例えば、受信データが以下のとおりだったとします (分かりやすくするために16進数表記とし、オクテットごとにスペースを入れています):

C7 9E 60 00 E1 61 EE 53 F9 62 0E 60 5E 1A D5 CB ED 12 E9 EA 11 D6 9B 32 2F 33 09 B4 B7 5E B8 CF 70 80 26 F2 08 0C 11 E6 EA 0C 8A 95 48 34 00 D9 ……

受信側はまず、先頭128bit (16オクテット) C7 9E 60 00 E1 61 EE 53 F9 62 0E 60 5E 1A D5 CB を抜き出してIVとして利用します。次に、このIVと暗号化鍵を用いて、残りの部分 ED 12 E9 EA …… を復号することで元の平文を得ることができます。

Discussion

ログインするとコメントできます