1
votes

I'm writing a program to encrypt and decrypt data. for encrypting, I created a symmetric key using keyGenerator. I transferred the key to the cipher, and created a string version of the key:

String keyString = Base64.getEncoder().encodeToString(symmetricKey.getEncoded());

in order to store it in a configuration file (so I can retrieve the key in the decrypt function).

Now, in the decrypt function I need to get that string back to key format, so I can send it as a parameter to the cipher in dercypt mode. I convert it back to key this way:

byte[] keyBytes = key.getBytes(Charset.forName("UTF-8"));
Key newkey = new SecretKeySpec(keyBytes,0,keyBytes.length, "AES"); 

And I transffer it to the cipher and write the output (the decrypted data) using CipherInputStream:

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, newkey, newiv, SecureRandom.getInstance("SHA1PRNG"));
        CipherInputStream cipherInputStream = new CipherInputStream(
                new ByteArrayInputStream(encryptedBytes), cipher);


        ArrayList<Byte> decryptedVal = new ArrayList<>();
        int nextByte;
        while ((nextByte = cipherInputStream.read()) != -1) {
            decryptedVal.add((byte) nextByte);
        }

        byte[] bytes = new byte[decryptedVal.size()];
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = decryptedVal.get(i);
        }
        String decryptedData = new String(bytes);
        cipherInputStream.close();
        System.out.println("decryptedData: " + decryptedData);

I get this error:

Exception in thread "main" java.io.IOException: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.

So I suspect that there might be a problem with the way I treat the key.

Any suggestions? help would be appreciated!

2
do you have similar IV and Key for encryption and decryption?Afshin
Yes, I transferred them via configuration file.Vic

2 Answers

1
votes

I think you have not sent IV to decryption function. For decryption in CBC mode, you must provide an IV which is used in encryption process.

Update:

IV will affect only first block in CBC decryption mode. So my answer may affect the unpadding if your data is less than 1 block. It will just change the decrypted plaintext of the first block otherwise.

1
votes

Of course you get this error: first you apply base 64 encoding:

String keyString = Base64.getEncoder().encodeToString(symmetricKey.getEncoded());

and then you use character-encoding to turn it back into bytes:

byte[] keyBytes = key.getBytes(Charset.forName("UTF-8"));

which just keeps be base64 encoding, probably expanding the key size from 16 bytes to 24 bytes which corresponds with a 192 bit key instead of a 128 bit key. Or 24 bytes key to a 32 bytes key of course - both seem to work.

To solve this you need to use Base64.getDecoder() and decode the key.

Currently you get a key with a different size and value. That means that each block of plaintext, including the last one containing the padding, will decrypt to random plaintext. As random plaintext is unlikely to contain valid padding, you will be greeted with a BadPaddingException.


Reminder:

  • , e.g. base 64 or hex: encoding bytes to a text string
  • , e.g. UTF-8 or ASCII: encoding a text string into bytes

They are not opposites, that would be decoding and character-decoding respectively.


Remarks:

  • yes, listen to Ashfin; you need to use a random IV during encryption and then use it during decryption, for instance by prefixing it to the ciphertext (unencrypted);
  • don't use ArrayList<Byte>; that stores a reference to each separate byte (!) - use ByteArrayOutputStream or any other OutputStream instead;
  • you can better use a byte buffer and use that to read / write to the streams (note that the read function may not fill the buffer, even if at the start or in the middle of the stream) - reading a single byte at the time is not performant;
  • lookup try-with-resources for Java;
  • using a KeyStore may be better than storing in a config file;
  • GCM mode (AES/GCM/NoPadding) also authenticates data and should be preferred over CBC mode.