2
votes

I'm trying to encrypt and decrypt plain text with AES("AES/CBC/PKCS7Padding") with Cipher.
Made secretKey by KeyGenerator and encrypt/decrypt with it. This works well at first.

However, if plain text is a large string(such as 183244 characters), part of it is decrypted and not all of it.

"part of it" means
If plain text is "abcdefghijklmn", decrypted text comes like "abcdefgh"
(this is an example. actually this happens only with too long characters)

Code

        // get key from Android kyestore
        SecretKey secretKey = (SecretKey)mKeyStore.getKey(KEY_ALIAS, null);

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);

        IvParameterSpec ivParams = cipher.getParameters().getParameterSpec(IvParameterSpec.class);

        //
        // Encrypt
        //
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher);
        cipherOutputStream.write(plainString.getBytes("UTF-8"));
        cipherOutputStream.close();

        String encryptedString = Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT);

        //
        // Decrypt
        //
        Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        decryptCipher.init(Cipher.DECRYPT_MODE, secretKey, ivParams);

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Base64.decode(encryptedString, Base64.DEFAULT));
        CipherInputStream cipherInputStream = new CipherInputStream(byteArrayInputStream, decryptCipher);
        outputStream = new ByteArrayOutputStream();

        int b;
        while ((b = cipherInputStream.read()) != -1) {
            outputStream.write(b);
        }
        outputStream.close();

        String decryptedString =  outputStream.toString("UTF-8");

        //
        // results. decrypted string is part of source string and short.
        //
        int sourceStringLength = plainString.length(); // is 183244;
        int decryptedStringLength = decryptedString.length()); // is UNEXPECTED 68553;

Could you please let know the point to fix this?

Edited

I've checked number of bytes on encryption/decryption.

encryption output bytes : "outputStream.toByteArray().length" is 68560
decryption input bytes : "Base64.decode(encryptedString, Base64.DEFAULT).length" is also 68560

I think this means decryption process doesn't lost any part of it and encryption process may truncate part of input plain text. is this because of restriction of AES algorithm?

Solved

As @zaph commented, not all of the bytes is written to steam.

Replacing

  cipherOutputStream.write(plainString.getBytes("UTF-8"));
  cipherOutputStream.close();

with

        // NOTE: workaround. too bit bytes doesn't writted correctly.
        byte[] bytes = plainString.getBytes("UTF-8");

        int oneBulkSize = 1024;// temp value for proof of concept. might be bigger one.
        int numOfBulk = (bytes.length / oneBulkSize);

        for (int i = 0; i < numOfBulk; i++) {
            cipherOutputStream.write(bytes, oneBulkSize * i, oneBulkSize);
        }

        if ((bytes.length % oneBulkSize) != 0) {
            cipherOutputStream.write(bytes, oneBulkSize * numOfBulk, bytes.length % oneBulkSize);
        }
        cipherOutputStream.close();

solved the issue.

1
What's the size of (Base64.decode(encryptedString, Base64.DEFAULT)?Maarten Bodewes
On encrypt you have Cipher.getInstance("AES/CBC/PKCS7Padding");, but on decrypt you have Cipher.getInstance(ALGORITHM);. So what is ALGORITHM?President James K. Polk
When I use "AES/CBC/PKCS7Padding" for ALGORITHM the code works for me.President James K. Polk
@JamesKPolk I've edited the code on question. ALGORITHM is "AES/CBC/PKCS7Padding". but not works with long plain text input.nsht
@MaartenBodewes I've checked number of bytes on encryption/decryption. encryption : "outputStream.toByteArray().length" is 68560 decryption : "Base64.decode(encryptedString, Base64.DEFAULT).length" is also 68560 I think this means encryption process truncated part of plain text. is this restriction of AES algorithm?nsht

1 Answers

1
votes

AES does not have a data length limitation. The encrypted data should be the same length as the input plus up to an additional 16-bytes for padding.

Add some debugging code in the encryption loop and verify that the entire input is being read. Perhaps the encryption stream needs a loop as does the decryption stream.

Another possibility is that the 183244 is the byte size with UTF-16 encoding but you are reading as UTF-8, most of the UTF-16 characters can be as one UTF-8 byte.

The following is the actual solution code by the OP @nsht, included here for completeness (from a deleted answer by @nsht):

Replacing

  cipherOutputStream.write(plainString.getBytes("UTF-8"));
  cipherOutputStream.close();

with

        // NOTE: workaround. too bit bytes doesn't writted correctly.
        byte[] bytes = plainString.getBytes("UTF-8");

        int oneBulkSize = 1024;// temp value for proof of concept. might be bigger one.
        int numOfBulk = (bytes.length / oneBulkSize);

        for (int i = 0; i < numOfBulk; i++) {
            cipherOutputStream.write(bytes, oneBulkSize * i, oneBulkSize);
        }

        if ((bytes.length % oneBulkSize) != 0) {
            cipherOutputStream.write(bytes, oneBulkSize * numOfBulk, bytes.length % oneBulkSize);
        }
        cipherOutputStream.close();

solved the issue.