Ariyan
Ariyan

Reputation: 15158

BouncyCastle GCM/CCM ArrayIndexOutOfBoundsException

can anyone give me an example for using GCM and/or CCM modes with AES in BouncyCastle?
My code is this:

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();

but it throws this exception when decrypting in GCM mode (line 70 is 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)

And this Exception when encrypting in CCM mode (line 70 is 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)

Upvotes: 4

Views: 4330

Answers (1)

Maarten Bodewes
Maarten Bodewes

Reputation: 93948

For CCM mode there is a little snag: the size of the IV should be smaller than the block size. Your code crashes on the following:

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);

Try it with an "IV" of 15 bytes instead (the IV is actually a NONCE, but IvParameterSpec is used for the NONCE).

The other problem is that the cipher.doFinal() method is both called when CipherInputStream cannot retrieve any data from the underlying stream, as well as when close() is called. Note that CipherInputStream is a very badly written class that also removes BadPaddingException when it is thrown - this is the exception you get when the tag validation fails (!!!). You are better off creating your own one based on CipherInputStream. I've changed to the code to throw specific IOException based exceptions instead of ignoring exceptions, and keeping a boolean state to see if doFinal() has been executed on the underlying cipher. It should not call doFinal() twice.

So you are running in a Java JCE bug here. I might put it in the Oracle bug database, where it not that up till now all my bug reports have been ignored completely.

Tested against latest version of OpenJDK 7 and Bouncy Castle 1.47 (2012-08-30 or something close).

Upvotes: 7

Related Questions