BouncyCastle GCM/CCM ArrayIndexOutOfBoundsException

Кто-нибудь может привести пример использования режимов GCM и/или CCM с AES в BouncyCastle?
Мой код таков:

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)

person Ariyan    schedule 01.09.2012    source источник
comment
Я думаю, вы ошибаетесь в том, что такое строка 70. Разве это не while ((i = cIn.read(block)) != -1)?   -  person Duncan Jones    schedule 01.09.2012
comment
@DuncanJones: нет, это строка .write!   -  person Ariyan    schedule 06.09.2012


Ответы (1)


Для режима 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);

Вместо этого попробуйте использовать «IV» из 15 байтов (на самом деле IV — это NONCE, но IvParameterSpec используется для NONCE).

Другая проблема заключается в том, что метод cipher.doFinal() вызывается как тогда, когда CipherInputStream не может получить какие-либо данные из базового потока, так и при вызове close(). Обратите внимание, что CipherInputStream — это очень плохо написанный класс, который также удаляет BadPaddingException при его генерировании — это исключение, которое вы получаете, когда проверка тега не удалась (!!!). Вам лучше создать свой собственный на основе CipherInputStream. Я изменил код, чтобы генерировать определенные исключения на основе IOException вместо того, чтобы игнорировать исключения, и сохранять состояние boolean, чтобы увидеть, было ли выполнено doFinal() на базовом шифре. Он не должен вызывать doFinal() дважды.

Итак, вы работаете с ошибкой Java JCE. Я мог бы поместить это в базу данных ошибок Oracle, где до сих пор все мои сообщения об ошибках полностью игнорировались.

Протестировано на последней версии OpenJDK 7 и Bouncy Castle 1.47 (30 августа 2012 г. или что-то близкое).

person Maarten Bodewes    schedule 02.09.2012
comment
Если вы уберете вызов закрытия, тогда код должен запуститься, но вы не получите никакой аутентификации, что было важно. - person Maarten Bodewes; 02.09.2012