0

Created a new class to test something with AES in CBC and CTR mode. So with this code, CTR is working fine, but CBC returns empty arrays. Not sure why this happens, hope somebody can explain that.

import org.junit.Before;
import org.junit.Test;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import java.security.*;
import java.util.Map;

public class AES_retest {

    private static final String plaintext = "Hallo Welt";
    private static final String key = "C0BAE23DF8B51807B3E17D21925FADF2";
    private String iv_string = "I need a initialization vector...";
    private Cipher encrypt_cipher_ctr, decrypt_cipher_ctr, encrypt_cipher_cbc, decrypt_cipher_cbc;

    @Before
    public void prepare_Test() throws GeneralSecurityException {
        byte[] tmp = new byte[16];
        System.arraycopy(iv_string.getBytes(), 0, tmp, 0, 16);

        removeCryptographyRestrictions();

        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES");
        //initialization vector
        IvParameterSpec iv = new IvParameterSpec(tmp);
        encrypt_cipher_cbc = Cipher.getInstance("AES/CBC/PKCS5Padding");
        encrypt_cipher_cbc.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv);
        decrypt_cipher_cbc = Cipher.getInstance("AES/CBC/PKCS5Padding");
        decrypt_cipher_cbc.init(Cipher.DECRYPT_MODE, secretKeySpec, iv);
    }

    @Test
    public void multiple_CBC_update() throws BadPaddingException, IllegalBlockSizeException {
        System.out.println("Testing CBC:");
        System.out.println("Plaintext: " + plaintext);
        System.out.println("Plaintext as HEX: " + bytesToHex(plaintext.getBytes()));
        byte[] first_encryption = encrypt_cipher_cbc.update(plaintext.getBytes());
        byte[] second_encryption = encrypt_cipher_cbc.update(plaintext.getBytes());
        encrypt_cipher_cbc.doFinal();
        byte[] first_decryption = decrypt_cipher_cbc.update(first_encryption);
        byte[] second_decryption = decrypt_cipher_cbc.update(second_encryption);
        decrypt_cipher_cbc.doFinal();
        System.out.println("First encryption: " + bytesToHex(first_encryption));
        System.out.println("Second encryption: " + bytesToHex(second_encryption));
        System.out.println("First decryption: " + bytesToHex(first_decryption));
        System.out.println("Second decryption: " + bytesToHex(second_decryption));
    }
}

Because I want to use both CTR and CBC in my program I would like to use a AES implementation that can handle both at the same time. Is this possible with the given implementation?

I would suppose that it has something to do with the defined Padding. If I use NoPadding in decryption, the Cipher text is decrypted correctly, but the padding is not removed. If I use PCS5Padding on decryption, a empty array or null is returned. Also the first encryption returns a empty array...

Picture of what IntelliJ shows while debugging

Added the doFinal calls as zaph suggested, now I get following error:

Testing CBC:
Plaintext: Hallo Welt
Plaintext as HEX: 48616c6c6f2057656c74

javax.crypto.BadPaddingException: Given final block not properly padded

at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:989)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:845)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at javax.crypto.Cipher.doFinal(Cipher.java:2048)
at AES_retest.multiple_CBC_update(AES_retest.java:117)

In the context of my program I will send and receive packets over a network socket that are encrypted. So I need to decrypt and encrypt every packet independently. I do not want to call doFinal at the end of every packet, because this would mean, that the same packet send twice would be encrypted in the same way. If that would be what I want, than I could use ECB mode. ;)

e.g. I want to send the message "Hello World" twice to an EchoServer, the Server should be able to decrypt the first received packet, without being aware that there are more packets following. Also the Client should be able to encrypt and send the messages, unknowing if the user will provide additional data that should be send. Not sure how and where padding hast to be added in this context.

Cipher.doFinal has this annoying affect to call Cipher.init after every call. So if I would Encrypt "Hello World" twice and call 'doFinal' after every encryption, both packets (if every "Hello World" gets its own packet) would look the same.

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
TheOkeland
  • 37
  • 8
  • 1
    The line numbers do not help. The extra code that is working (CTR) does not help. Provide a [mcve] where minimal means only the code that is failing. Provide the encryopted data in hex. You have `println` statements but have not provided their output. – zaph Jul 21 '17 at 18:50
  • The text to be encrypted is 10 characters so there will only be one block of output entirely generated by `doFinal` but you are making two calls prior to that which exhaust the input so there is none for `doFinal`. The IV is 33 characters but the required IV is only 16-bytes. You need to do some studying on block based encryption such as AES. – zaph Jul 21 '17 at 19:04
  • If you are thinking CTR mode is the solution there are issues, the major one is that the IV can **never be reused** with the same key. – zaph Jul 21 '17 at 19:05
  • The IV is only 16 bytes long: byte[] tmp = new byte[16]; System.arraycopy(iv_string.getBytes(), 0, tmp, 0, 16); In the real program key and iv are derived from a DH_key_exchange, so that shouldn't be a problem. I will try to make the problem more clearly. – TheOkeland Jul 21 '17 at 20:48
  • `private String iv_string = "I need a initialization vector...";` 33-bytes. – zaph Jul 21 '17 at 20:56
  • Yes, but it is copied into another array that is only 16 bytes long. And this one is used, not the iv_string itself. If the iv passed to the cipher would have the wrong length, the cipher would throw an exception. – TheOkeland Jul 21 '17 at 21:00
  • Look here: `byte[] tmp = new byte[16];` `System.arraycopy(iv_string.getBytes(), 0, tmp, 0, 16);` `IvParameterSpec iv = new IvParameterSpec(tmp);` – TheOkeland Jul 21 '17 at 21:01
  • I realize that, it is just sloppy. – zaph Jul 21 '17 at 21:07
  • How many bytes will `byte[] first_encryption = encrypt_cipher_cbc.update(plaintext.getBytes());` consume? – zaph Jul 21 '17 at 21:08
  • first_encryption consumes exact zero bytes after calling update and I don't have a clue why this could be. `encrypt_cipher_cbc.update(plaintext.getBytes));` returns an empty array... – TheOkeland Jul 21 '17 at 21:15
  • It's not clear if you're saying you want one CBC message to extend over more than one network transmission; if so that is insecure and your system will be broken, google BEAST. If you want to understand why CBC works only in complete blocks, see Wikipedia on 'block cipher mode of operation'. – dave_thompson_085 Jul 21 '17 at 21:20
  • BEAST should not be a problem, because in the real implementation the IV is chosen completely (secure) random. I want to use the IV for a single session, but for multiple packets. Also this packets should be encrypted and decrypted independently, because the receiver hast to react based on the data he receives. Because of that, as far as I understand this, calling `doFinal` is not an option, because the IV would be reseted within a session. – TheOkeland Jul 21 '17 at 21:31
  • Read the documentation in the link. The `doFinal` call returns the encrypted data and you are ignoring that `encrypt_cipher_cbc.doFinal();`. There is no reason that the `update` calls need to return anything, if the data is less than a block in size there is nothing to return until the `doFinal` call. `"Hallo Welt"` is less than a block in size. – zaph Jul 21 '17 at 22:04
  • So the only possibility for me is to wait until I updated enough data so that I get encrypted blocks to send over the network? And if there is not enough data I would have to wait until additional Data is provided by the User? That would mean, if the user does only want to send "Hello World", than I would not be able to get encrypted Data, because I doesn't want to call `doFinal`. Calling `doFinal` would ruin the encryption if done before the connection will be closed, because of the reinitialization of the IV. – TheOkeland Jul 21 '17 at 22:26
  • Probably true, you might see how much data you have to process before you get output. Check the documentation on the other update methods. You have not provided your use case, if it is streaming data CTR might be the best choice. – zaph Jul 21 '17 at 22:42

1 Answers1

1

You are missing encrypt_cipher_cbc.doFinal.

From: Class Cipher - doFinal

byte[] doFinal() Finishes a multiple-part encryption or decryption operation, depending on how this cipher was initialized.

byte[] doFinal(byte[] input) Encrypts or decrypts data in a single-part operation, or finishes a multiple-part operation.

zaph
  • 111,848
  • 21
  • 189
  • 228
  • There is just one problem with using doFinal. I am not able to do all the encryption at once. Because this is used in a network protocol. Using doFinal would result in resetting the IV, but than the next encryption would use "old" parameters again. – TheOkeland Jul 21 '17 at 17:31
  • Do as many `encrypt_cipher_cbc.update` as necessary. Then `encrypt_cipher_cbc.doFinal`. The `doFinal` has options to either have some final text or none. See the link in the answer. Since CBC mode by default provides padding it must have a final call at the end so it can add the padding. CTR mode does not have padding to so get away without calling `doFinal`. – zaph Jul 21 '17 at 18:10
  • 1
    You are ignoring the output of `encrypt_cipher_cbc.doFinal()`, that is where the padding is created along with the data for the last block. – zaph Jul 21 '17 at 18:57
  • So does this mean that I need to update my second_encryption byte[] with the result? But if this would be as I suppose, how could I use CBC a network-usecase? Every packet would have to be encrypted, but the IV should not be reseted between encrypting two different packets. Also the receiver should be able to decrypt on packet as it is. – TheOkeland Jul 21 '17 at 19:29