説明
JavaやKotlinでCipher.getInstance("AES")のようにアルゴリズム名だけを指定すると、モードとパディングはJCAプロバイダーの既定値に委ねられます。Oracle JDK 25の文書では、SunJCEとSunPKCS11の複数の共通鍵暗号でECBとPKCS5Paddingが既定値とされています。SunJCEではAESはAES/ECB/PKCS5Paddingと同じです。ECBは同じ平文ブロックを同じ暗号文ブロックにするため、繰り返しのパターンが見え、完全性や認証も提供しません。他のプロバイダーでは既定値が異なる場合があり、安全性と互換性が不明確になります。
想定される影響
- 平文の繰り返しから、データの構造や内容を推測される可能性があります。
- 認証機能のないモードでは、暗号文の改ざんを検出できない場合があります。
- プロバイダーや配備環境の変更で、復号の失敗や想定外の安全性の変化が生じる可能性があります。
対処方法
- アルゴリズム、モード、パディングを明示してください。
- 機密性と完全性が必要なら、
AES/GCM/NoPaddingなど、検証されたAEADモードを優先してください。 - GCMでは同じ鍵による暗号化ごとに一意の96ビットnonceを使用し、再利用しないでください。ランダムなnonceには
SecureRandomで12バイトを生成し、復号できるよう暗号文と一緒に保存・送信します。nonce自体は秘密情報ではありません。 - 既存のECB暗号文を読み取る必要がある場合は、新規暗号化とは別に移行を計画してください。
例
変更前
java
import javax.crypto.Cipher;
Cipher cipher = Cipher.getInstance("AES");
変更後
keyは別途安全に生成・管理するAESのSecretKeyです。暗号初期化の抜粋であり、暗号文の保存と復号エラーの処理は省略しています。
java
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
byte[] nonce = new byte[12];
new SecureRandom().nextBytes(nonce);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, nonce));
変更前は、ECBなど要件に合わない既定値が選ばれる可能性があります。変更後は認証付きのGCMと96ビットnonceを明示します。同じ鍵で次に暗号化するときは新しいnonceを使い、復号側には暗号文と一緒に渡してください。
Kotlinも同じjavax.crypto.Cipher APIを使うため、この原則が当てはまります。