views:

2196

answers:

3

I am trying to decrypt a file in Java which was encrypted in C# using Rijndael/CBC/PKCS7. I keep getting the following exception:

javax.crypto.BadPaddingException: pad block corrupted at org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(Unknown Source) at javax.crypto.Cipher.doFinal(DashoA13*..) at AESFileDecrypter.decrypt(AESFileDecrypter.java:57)

when the "doFinal(inpbytes)" method is called by the web server for the first byte[]. I am guessing this is a problem with the key or IV. I have the encrypted files on my file system for testing. Is there anything that anyone can see glaringly wrong with my code below:

***keyStr is base64 encoded

public AESFileDecrypter(String keyStr){
    try {
            Security.addProvider(new BouncyCastleProvider());   
            convertIvParameter();
            key = new sun.misc.BASE64Decoder().decodeBuffer(keyStr);

            //use the passed in Base64 decoded key to create a key object
            decryptKey = new SecretKeySpec(key, "AES");

            //specify the encryption algorithm
            decryptCipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");

            //make a parameter object for the initialization vector(IV)             
            IvParameterSpec ivs = new IvParameterSpec(_defaultIv);

            //initialize the decrypter to the correct mode, key used and IV
            decryptCipher.init(Cipher.DECRYPT_MODE, decryptKey, ivs);    
        } 
     catch (Exception e) {
             e.printStackTrace();
     } 
}

public void convertIvParameter() {

   int[] iv = new int[] {11, 190, 165, 33, 68, 88, 11, 200, 245, 35, 68, 23, 60, 24, 223, 67};

   _defaultIv = new byte[16];

   for(int x = 0; x < _defaultIv.length; x++) {
      _defaultIv[x] = (byte)iv[x];
   }
}

public void decryptUpdate(byte[] inpBytes) throws Exception {
   //decrypt the byte passed in from the web server
   decryptCipher.update(inpBytes);  
}

public byte[] decryptFinal() throws Exception {
   //decrypt the byte passed in from the web server
   return decryptCipher.doFinal();
}

//sends bytes to the client for diaply
private void sendBytes(FileInputStream fis, OutputStream os)throws Exception {
    //set the buffer size to send 4k segments of data
aesFileDecrypter = new AESFileDecrypter(<Insert Key string here>);

    byte[] buffer = new byte[4096];
    int bytes = 0, totalBytes = fis.available();

    //while there is still data to be sent keep looping and write the data
    //to the output stream as the buffer is filled
    try {
       while ((bytes = fis.read(buffer)) != -1) {   
          aesFileDecrypter.decryptUpdate(buffer);
          //os.write(buffer, 0, bytes);
       }

       os.write(aesFileDecrypter.decryptFinal(), 0, totalBytes);
   }
   catch(Exception e) {
      e.printStackTrace();
   }
}
+1  A: 

As far as I know AES is based on Rijndael, but the specification is not exactly the same. I would suggest to check the key and block size you are using to cipher in C# and the sizes being use in Java. (.Net differences between Rijndael and AES).

Daniel H.
Thanks for the reply. The AES is for 256-bit, and the cipher in C# is using 4k chunks of data. In thurn I am using that same size each time I call doFinal()
+1  A: 

Firstly, just to be clear, from comments below, you shouldn't call doFinal() on every block, because doFinal() expects any padding at the end, which obviouslly won't be there in intermediate blocks. Either (a) call update() on intermediate data, then doFinal() at the end, or (b) just arrange to have all your data in one buffer or byte array, and call doFinal() once on the whole job lot.

It's not clear from the code you posted that that's actually what you're doing, but it should be mentioned just in case.

Failing that, then as a first step to debugging, I'd suggest whichever of these two is easier for you:

  • Decrypting in ECB mode with no padding and seeing what you get. Look at the first block of data this brings back. If you can XOR this with your IV bytes and get the expected decrypted data, you know your key is OK.
  • Dumping out the actual key bytes from C# before base 64 encoding and Java after decoding and checking they are the same.

As I recall, C# has unsigned bytes (whereas Java signed) so there are a few places where there's room for things subtly going wrong with byte signedness.

Neil Coffey
Another comment I was told is that a major issue is calling "doFinal()" on every block (it should only be done once). It was suggested I either change the doFinal to update, and only perform doFinal at the end. Or use cipher streams
Ah yes, sorry, I obviously didn't read your code carefully enough. If you're calling doFinal() for every block, that's wrong.
Neil Coffey
Thanks Niel...I added in updated code. I was trying to call the update method now from the sendBytes method (which resides in the web server). and after I loop through the input stream, call the doFinal() method. Still get the same exception on the doFinal() method, so it loops successfully through the file stream doing the update() on each byte[]. Any suggestions?
To get a clue, I'd try calling update() on the final block rather than doFinal(). I think this should decrypt and give you the padding-- so e.g. if there are 5 bytes of padding, the last five bytes should be 05,05,05,05,05. Maybe C# is putting it in some other format-- calling update() will hopefully tell you.
Neil Coffey
P.S. On the final pass through, you ONLY call doFinal()-- you don't call update() AND doFinal() on the same data.
Neil Coffey
+1  A: 

The doFinal() was the undoing of the code above, and I ended up just using cipher streams instead of the update/doFinal approach. This way I could use the FileInputStream and my cipher as parameters for the CipherInputStream, and then pass the output to the web browser through an OutputStream. Breaking the update and doFinal out into their own method calls made the task much more difficult and both methods were deleted from the decrypter class (leaving a single while loop that read in chunks of data and output it to the browser). The Bouncy Castle Provider was also not needed in this case and PKCS5Padding was enough, which was given by the SunJCE.