23

I have observed the following when I worked with Cipher.

Encryption code:

Cipher aes = Cipher.getInstance("AES");
aes.init(Cipher.ENCRYPT_MODE, generateKey());
byte[] ciphertext = aes.doFinal(rawPassword.getBytes());

Decryption code :

Cipher aes = Cipher.getInstance("AES");
aes.init(Cipher.DECRYPT_MODE, generateKey());
byte[] ciphertext = aes.doFinal(rawPassword.getBytes());

I get IllegalBlockSizeException ( Input length must be multiple of 16 when ...) on running the Decrypt code.

But If I change the decrypt code to

Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding"); //I am passing the padding too
aes.init(Cipher.DECRYPT_MODE, generateKey());
byte[] ciphertext = aes.doFinal(rawPassword.getBytes());

It works fine. I understand that it is in the pattern algorithm/mode/padding. So I thought it is because I didn't mention the padding. So I tried giving mode and padding during encryption,

Encryption code:

Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding");//Gave padding during encryption too
aes.init(Cipher.ENCRYPT_MODE, generateKey());
byte[] ciphertext = aes.doFinal(rawPassword.getBytes());

Decryption code :

Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding");
aes.init(Cipher.DECRYPT_MODE, generateKey());
byte[] ciphertext = aes.doFinal(rawPassword.getBytes());

But it fails with IllegalBlockSizeException.

What is the reason, why the exception and what is exactly happening underneath. If anyone can help? Thanks in advance

UPDATE

Looks like the issue is with the string I am encrypting and decrypting. Because, even the code that I said works, doesn't always work. I am basically encrypting UUIDs (eg : 8e7307a2-ef01-4d7d-b854-e81ce152bbf6). It works with certain strings and doesn't with certain others.

The length of encrypted String is 64 which is divisible by 16. Yes, I am running it on the same machine.

Method for secret key generation:

    private Key generateKey() throws NoSuchAlgorithmException {
    MessageDigest digest = MessageDigest.getInstance("SHA");
            String passphrase = "blahbl blahbla blah";
    digest.update(passphrase.getBytes());
    return new SecretKeySpec(digest.digest(), 0, 16, "AES");
}
shazinltc
  • 3,616
  • 7
  • 34
  • 49
  • 1
    What is the length of the ciphertext generated by your encryption method? It ought to be a multiple of 16 - if it's not, then please edit your question with all of your encryption code so we can see what's going wrong. Also, this is probably not the issue (I'm assuming you're running the encryption and decryption on the same machine), but you should always use rawPassword.getBytes("UTF-8"), because different JVMs use different default character encodings. – Zim-Zam O'Pootertoot Apr 24 '13 at 13:30
  • I have updated my answer. Yes, I should be passing the encoding standard. – shazinltc Apr 24 '13 at 13:52
  • Does your code consistently fail with one of your UUID values? If so, please edit your question to show a single piece of code (an [SSCCE](http://sscce.org/)) that fails so that we can experiment. – Duncan Jones Apr 24 '13 at 14:44

2 Answers2

13

During decryption, one can only get an IllegalBlockSizeException if the input data is not a multiple of the block-size (16 bytes for AES).

If the key or the data was invalid (but correct in length), you would get a BadPaddingException because the PKCS #5 padding would be wrong in the plaintext. Very occasionally the padding would appear correct by chance and you would have no exception at all.


N.B. I would recommend you always specify the padding and mode. If you don't, you are liable to be surprised if the provider changes the defaults. AFAIK, the Sun provider converts "AES" to "AES/ECB/PKCS5Padding".

Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
  • The 'rawPassword' is a copy/paste issue. Yes, you are right regarding mode and padding. I am doing that now. – shazinltc Apr 24 '13 at 14:57
  • Your answer is close, so I am accepting it :) If anyone happens to read the post someday, There is an answer by myself that explains what exactly the issue was :) cheers!! – shazinltc Apr 24 '13 at 15:51
  • 6
    Note from the future to Android developers: at API 25 (latest at time of writing), you _can_ get `javax.crypto.IllegalBlockSizeException ... stack trace ... Caused by: android.security.KeyStoreException: Key user not authenticated` when using a `SecretKey` directly as loaded from keystore instead of referring to it through a `CryptoObject` wrapper which authenticates use of the key. – Scruffy Nov 23 '16 at 10:49
  • @Scruffy Can you please explain ? – madyx Apr 19 '18 at 16:00
  • @madyx I believe this came about as I was adding fingerprint security. I mentioned it because the name of the exception is particularly unhelpful in finding the cause. Also because Duncan said that input not being a multiple of 16 bytes is the only cause, which does not hold for Android developers. If you have encountered the issue I commented about, feel free to open a question about it. I'd be happy to give a fuller answer with code samples there. – Scruffy Apr 19 '18 at 20:44
  • What does **multiple of the block-size** mean mathematically? – IgorGanapolsky May 27 '18 at 18:17
  • 1
    @IgorGanapolsky It means that the input data has length "*k * n*", where *n* is the block size and *k* is some constant. Hence it can be cleanly divided into blocks of length *n*. – Duncan Jones May 29 '18 at 06:01
  • Hi Duncan, as per my understanding, if the input is not a multiple of the block-size, padding is supposed to make it right. So, how can IllegalBlockSizeException be thrown if the input is not a multiple of the block-size and we have mentioned padding in the transformation (AES/ECB/PKCS5Padding)? I think your statement should be : "During decryption, one can only get an IllegalBlockSizeException if the input data is not a multiple of the block-size (16 bytes for AES) and Padding is not applied by algorithm to make it right." – garnet Jul 25 '18 at 06:28
  • 1
    @garnet When *decrypting*, the error `IllegalBlockSizeException` indicates that the *ciphertext* is not a multiple of the blocksize. Usually this means the ciphertext was corrupted or is in the wrong format. Padding is only removed once the decryption was successful. The [answer from the OP](https://stackoverflow.com/a/16194926/474189) confirms this was the case. – Duncan Jones Jul 25 '18 at 08:00
  • @Scruffy Hey bro, I got an error as you described, can you please explain it for me. https://stackoverflow.com/questions/63664651/key-user-not-authenticated-when-using-a-secretkey-directly-as-loaded-from-keysto – suntzu Aug 31 '20 at 04:06
  • see also this [handling-java-crypto-exceptions](https://stackoverflow.com/questions/15709421/handling-java-crypto-exceptions#15712409) – Nick Dong Mar 13 '23 at 04:51
7

Though I haven't fully understood the internals, I have found what the issue is.

I fetch the encrypted string as a GET request parameter. As the string contains unsafe characters, over the request the string gets corrupted. The solution is, to do URL encoding and decoding.

I am able to do it successfully using the URLEncoder and URLDecoder.

Now the results are consistent. Thanks :)

I would be grateful if anyone can contribute more to this.

shazinltc
  • 3,616
  • 7
  • 34
  • 49
  • Ah, that it explains it. Your corrupted ciphertext was no longer of the correct length, hence the exception (basically see the guts of my answer). – Duncan Jones Apr 24 '13 at 15:01