1

I am trying to encrypt and decrypt a string using java Cipher with the AES algorithm. But somehow it does not want to encrypt the whole string. My goal is not to encrypt something particullary safely, I want that someone just cant read the String very easily (later stored in a file).

To encrypt, I use the following code:

Cipher cipher = Cipher.getInstance("AES");
byte[] input = plain.getBytes();
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encrypted = cipher.doFinal(input);

return new String(encrypted);

And to decrypt, I use the following code:

Cipher cipher = Cipher.getInstance("AES");
byte[] input = encrypted.getBytes();
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decrypted = cipher.doFinal(input);

return new String(decrypted);

To test the code mentioned above, I use the following parameters:

SecretKeySpec key = new SecretKeySpec(new byte[]{103,38,125,-67,-71,-23,-119,102,78,-3,-33,-23,-5,32,-112,-124}, "AES");
String plain = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur auctor ornare bibendum."

That test results in the encrypted and then again decrypted string:

�T���Ѩ�%���Kr sit amet, consectetur adipiscing elit. Curabitur auctor ornare bibendum.
Virt
  • 145
  • 2
  • 11

1 Answers1

3

There are several issues:

byte[] encrypted = cipher.doFinal(input);
return new String(encrypted);

The problen is that you cannot just "stringify" a byte array, you need to represent the byte array in a different format, such as Base64 or Hex. String is intended only for printable characters.

Problems converting byte array to string and back to byte array

byte[] encrypted = cipher.doFinal(input);
return Base64.getEncoder().encodeToString(encrypted);

And the same way - you need to decode the encoded string into a byte array before decryption.

Edit:

Another issue - you should specify full mode of operation

By default the "AES" parameter should use the CBC mode if IV (initialization vector) is supplied and ECB mode if IV is not defined (at lease this is how Oracle JDK works as far I know).

it is not guaranteed for all runtimes. Which platform/jre are you using? . As your first block of bytes is scrambled I assume the implementation is using random IV (you can use cipher. getIV()) and you have to pass the IV when decrypting.

cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
SecretKey symmetricKey = new 
SecretKeySpec(encryptionParams.getKey(), SYMMETRIC_KEY_ALG);

cipher.init(Cipher.ENCRYPT_MODE, symmetricKey, ivParamSpec);

For CBC the IV must be a random byte array of length of the AES block size (128 bit =16 bytes) and the common practice is to prepend the IV to the ciphertext.

Please do learn something about using encryption properly, otherwise you may end up with unsecure solution.

I have a small reference project you may try to use.

gusto2
  • 11,210
  • 2
  • 17
  • 36
  • "By default the "AES" parameter should use the CBC mode if IV (initialization vector) is supplied and ECB mode if IV is not defined (at lease this is how Oracle JDK works as far I know)." It doesn't. It simply selects the ECB mode when initializing, and then fails on `init` if you provide an IV. The only thing that is done lazily is selecting the provider based on the key, afaik. – Maarten Bodewes Sep 09 '20 at 08:21
  • @MaartenBodewes I stay corrected. However in the question I see no IV provided and the 1st block scrambled which implies CBC mode in use with wrong IV (zero vector?). I'd be curious for the JRE verion/platform as invalid use of encoding should scramble much bigger portion of the ciphertext than only a 1st block. – gusto2 Sep 09 '20 at 08:43
  • It could also be that the first block of ciphertext is mangled because of the botched encoding, that's the only thing I can come up with. Possibly if a ciphertext "character" is replaced with a � then yeah... Very often you'll only find out when the actual situation of the asker is fully known, sometimes it's just the odd mistake copying or whatnot. – Maarten Bodewes Sep 09 '20 at 08:58