4

BouncyCastle で AES を使用して GCM や CCM モードを使用する例を教えてください。
私のコードはこれです:

SecretKeySpec   key = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
Cipher          cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
byte[] block = new byte[1048576];
int i;
long st,et;

cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);

BufferedInputStream bIn=new BufferedInputStream(new ProgressMonitorInputStream(null,"Encrypting ...",new FileInputStream("input")));
CipherInputStream       cIn = new CipherInputStream(bIn, cipher);
BufferedOutputStream bOut=new BufferedOutputStream(new FileOutputStream("output.enc"));

int ch;
while ((i = cIn.read(block)) != -1) {
    bOut.write(block, 0, i);
}
cIn.close();
bOut.close();

Thread.sleep(5000);

cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);

BufferedInputStream fis=new BufferedInputStream(new ProgressMonitorInputStream(null,"Decrypting ...",new FileInputStream("output.enc")));
//FileInputStream fis=new FileInputStream("output.enc");
//FileOutputStream ro=new FileOutputStream("regen.plain");
BufferedOutputStream ro=new BufferedOutputStream(new FileOutputStream("regen.plain"));

CipherInputStream dcIn = new CipherInputStream(fis, cipher);

while ((i = dcIn.read(block)) != -1) {
        ro.write(block, 0, i);
}

dcIn.close();
ro.close();

ただし、GCM モードで復号化すると、この例外がスローされます (70 行目はbOut.write(block, 0, i);):

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
    at java.lang.System.arraycopy(Native Method)
    at org.bouncycastle.crypto.modes.CCMBlockCipher.processPacket(Unknown Source)
    at org.bouncycastle.crypto.modes.CCMBlockCipher.doFinal(Unknown Source)
    at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$AEADGenericBlockCipher.doFinal(Unknown Source)
    at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(Unknown Source)
    at javax.crypto.Cipher.doFinal(DashoA13*..)
    at javax.crypto.CipherInputStream.a(DashoA13*..)
    at javax.crypto.CipherInputStream.read(DashoA13*..)
    at javax.crypto.CipherInputStream.read(DashoA13*..)
    at enctest.Main.main(Main.java:70)

そして、CCM モードで暗号化するときのこの例外 (70 行目はbOut.write(block, 0, i);):

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
    at java.lang.System.arraycopy(Native Method)
    at org.bouncycastle.crypto.modes.CCMBlockCipher.processPacket(Unknown Source)
    at org.bouncycastle.crypto.modes.CCMBlockCipher.doFinal(Unknown Source)
    at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$AEADGenericBlockCipher.doFinal(Unknown Source)
    at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(Unknown Source)
    at javax.crypto.Cipher.doFinal(DashoA13*..)
    at javax.crypto.CipherInputStream.a(DashoA13*..)
    at javax.crypto.CipherInputStream.read(DashoA13*..)
    at javax.crypto.CipherInputStream.read(DashoA13*..)
    at enctest.Main.main(Main.java:70)
4

1 に答える 1

6

CCMモードの場合、少し問題があります。IVのサイズはブロックサイズよりも小さくする必要があります。次の場合にコードがクラッシュします。

BlockCipher ctrCipher = new SICBlockCipher(cipher);
byte[] iv = new byte[blockSize];
byte[] out;

iv[0] = (byte)(((15 - nonce.length) - 1) & 0x7);

System.arraycopy(nonce, 0, iv, 1, nonce.length);

代わりに15バイトの「IV」で試してください(IVは実際にはNONCEですが、NONCEにIvParameterSpec使用されます)。

もう1つの問題は、基になるストリームからデータを取得できないcipher.doFinal()場合と、が呼び出された場合の両方でメソッドが呼び出されることです。これは非常にひどく書かれたクラスであり、スローされたときにも削除されることに注意してください。これは、タグの検証が失敗したときに発生する例外です(!!!)。に基づいて独自のものを作成することをお勧めします。例外を無視するのではなく、特定のベースの例外をスローし、基になる暗号で実行されたかどうかを確認するための状態を維持するようにコードを変更しました。2回呼び出すべきではありません。CipherInputStreamclose()CipherInputStreamBadPaddingExceptionCipherInputStreamIOExceptionbooleandoFinal()doFinal()

したがって、ここではJavaJCEのバグで実行しています。Oracleのバグデータベースに入れるかもしれませんが、これまでのところ、すべてのバグレポートが完全に無視されていたわけではありません。

OpenJDK7の最新バージョンとBouncyCastle1.47(2012-08-30またはそれに近いもの)に対してテストされています。

于 2012-09-02T12:21:54.417 に答える