JCEでAES/CTRを試してみた
TLSの例を調べたついでにJCEで実装する方法を調べてみた。
CTRモードを使う時の最大の問題はIVをどう扱うかということだったけれど、JCEのCipherではinit()時くらいしかIVの指定する方法がないような気がする。この場合、複数のブロックを暗号化する場合にIVが変化するのかどうかが知りたくなるのだけれど、探した限りでは明確な記述が見当たらなかった。
というわけで実際にCTRモードで同じブロックを2回暗号化するプログラムを作って、出力が変化するかどうかを調べてみた*1。
// IVを準備 byte[] iv = new byte[16]; Arrays.fill(iv, (byte)0); // 平文を準備 byte[] clearBlock = new byte[16]; Arrays.fill(clearBlock, (byte)0); // 鍵と暗号用オブジェクトの準備 KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); SecretKey secretKey = keyGenerator.generateKey(); Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); // IVを最初の1回だけ設定 cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv)); byte[] encryptedBlock1 = cipher.update(clearBlock); byte[] encryptedBlock2 = cipher.update(clearBlock); // 2ブロック分の暗号文を出力 System.out.println("block1: " + Arrays.toString(encryptedBlock1)); System.out.println("block2: " + Arrays.toString(encryptedBlock2));
プログラムの実行結果はこのようになった。block1とblock2が異なることから、Cipherの内部でIVが変化していることが分かった。
block1: [-55, -97, -16, -56, 20, 120, -50, -79, 77, -75, 110, 11, 126, 76, -14, -122] block2: [-45, 53, 115, -128, 30, 69, 69, -49, 29, -105, 10, -116, -82, -69, 26, -83]
というわけで次にIVがどのように変化するのか調べてみた。おそらく値をインクリメントしているのだろうと当たりをつけて、手動で1ブロックごとにIVをインクリメントするプログラムを追加してみた。
// IVをブロックごとにインクリメントした場合 cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv)); encryptedBlock1 = cipher.update(clearBlock); // IVをインクリメントしてcipherに設定 iv[15]++; cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv)); encryptedBlock2 = cipher.update(clearBlock); // 2ブロック分の暗号文を出力 // 先ほどと同じ結果であればCipherもiv[15]をインクリメントしていることがわかる。 System.out.println("block1: " + Arrays.toString(encryptedBlock1)); System.out.println("block2: " + Arrays.toString(encryptedBlock2));
先ほどと鍵が違うため値は異なるが、手動でIVをインクリメントした場合とCipherが内部でIVを変えた場合が同じ結果になっている。
block1: [3, -127, 47, -47, 86, -8, 122, -97, -3, -89, 80, 82, 97, 24, -115, 42] block2: [107, 34, -44, 36, -76, -12, -34, -95, -41, 43, 39, -26, -106, 119, -117, 102] block1: [3, -127, 47, -47, 86, -8, 122, -97, -3, -89, 80, 82, 97, 24, -115, 42] block2: [107, 34, -44, 36, -76, -12, -34, -95, -41, 43, 39, -26, -106, 119, -117, 102]
結論としては、Cipherは1ブロックごとに最下位ビットの方からIVをインクリメントする*2ようだ。
ただ仕様として決まっているわけでもなさそうなので、実装によっては異なる動作をするのかもしれない*3。そう考えると1ブロックごとに自分でIVを設定した方が確実なのだろうけれど、それも無駄が多い気がするのでどうしたものだろう。Bouncycastle辺りにプロバイダーを固定してしまえば悩む必要もないのかな。