5

I am aware of a question very similar to this (How do I encrypt in Python and decrypt in Java?) but I have a different problem.

My problem is, I am not able to decrypt in Java correctly. Despite using the correct key and IV, I still get garbage characters after decryption. I don't have any compile/run-time errors or exceptions in Java so I believe I am using the right parameters for decryption.

Python Encryption Code -

from Crypto.Cipher import AES
import base64
key = '0123456789012345'
iv = 'RandomInitVector'
raw = 'samplePlainText'
cipher = AES.new(key,AES.MODE_CFB,iv)
encrypted = base64.b64encode(iv + cipher.encrypt(raw))

Java Decryption Code -

private static String KEY = "0123456789012345";
public static String decrypt(String encrypted_encoded_string) throws NoSuchAlgorithmException, NoSuchPaddingException,
    InvalidKeyException, IllegalBlockSizeException, BadPaddingException {

      String plain_text = "";
      try{
          byte[] encrypted_decoded_bytes = Base64.getDecoder().decode(encrypted_encoded_string);
          String encrypted_decoded_string = new String(encrypted_decoded_bytes);
          String iv_string = encrypted_decoded_string.substring(0,16); //IV is retrieved correctly.

          IvParameterSpec iv = new IvParameterSpec(iv_string.getBytes());
          SecretKeySpec skeySpec = new SecretKeySpec(KEY.getBytes("UTF-8"), "AES");

          Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
          cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);

          plain_text = new String(cipher.doFinal(encrypted_decoded_bytes));//Returns garbage characters
          return plain_text;

      }  catch (Exception e) {
            System.err.println("Caught Exception: " + e.getMessage());
      }

      return plain_text;
 }

Is there anything obvious that I am missing?

Community
  • 1
  • 1
Srikanth Kandalam
  • 965
  • 3
  • 15
  • 26

1 Answers1

10

The Cipher Feedback (CFB) mode of operation is a family of modes. It is parametrized by the segment size (or register size). PyCrypto has a default segment size of 8 bit and Java (actually OpenJDK) has a default segment size the same as the block size (128 bit for AES).

If you want CFB-128 in pycrypto, you can use AES.new(key, AES.MODE_CFB, iv, segment_size=128). If you want CFB-8 in Java, you can use Cipher.getInstance("AES/CFB8/NoPadding");.


Now that we have that out the way, you have other problems:

  • Always specify the character set you're using, because it can change between different JVMs: new String(someBytes, "UTF-8") and someString.getBytes("UTF-8"). When you do, be consistent.

  • Never use a String to store binary data (new String(encrypted_decoded_bytes);). You can copy the bytes directly: IvParameterSpec iv = new IvParameterSpec(Arrays.copyOf(encrypted_decoded_bytes, 16)); and cipher.doFinal(Arrays.copyOfRange(encrypted_decoded_bytes, 16, encrypted_decoded_bytes.length)).

  • In Java, you're assuming that the IV is written in front of the ciphertext and then encoded together, but in Python, you're never doing anything with the IV. I guess you posted incomplete code.

  • It is crucial for CFB mode to use a different IV every time if the key stays the same. If you don't change the IV for every encryption, you will create a multi-time pad which enables an attacker to deduce the plaintext even without knowing the key.

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • Using ''''AES.new(key, AES.MODE_CFB, iv, segment_size=128)'''' did the trick! And yes, I have posted incomplete code. I am appending IV in python too. Updated the code. Thanks for the other inputs too! – Srikanth Kandalam Oct 31 '16 at 23:33
  • Thanks for this @Artjom B. It worked and I got the first 8 bytes correctly. The problem is that the returned buffer from the Cipher.doFinal is of size 8 instead of 16. Do you have any idea how to get an output buffer with a length of 16? – Oz Shabat Jul 09 '20 at 09:47
  • @OzShabat Sorry, I can't help you there. I also don't see why it would be 8 bytes. Perhaps something else is going on. Are you sure, you're using AES and not DES? Are you sure that the JRE that you use hasn't switched from CFB-128 to CFB-64? – Artjom B. Jul 09 '20 at 12:46
  • @ArtjomB. you saved my a**. This is so obscure. Works great `Cipher.getInstance("AES/CFB8/NoPadding");`. Needed this for Clojure. Do you have a GitHub so I can sponsor you? Thanks again!! – snakes_on_a_keyboard Jun 21 '21 at 19:54
  • @snakes I'm glad I could help you out. I have a GitHub profile with the same name as here, but I haven't yet set up my sponsors account. I've checked some profiles of my heroes and they also didn't set it up yet. So I can't give another suggestion instead. ;) Buy somebody a beer when you're next time at a bar. :) – Artjom B. Jun 21 '21 at 22:14
  • It's a deal! THANK YOU! – snakes_on_a_keyboard Jun 23 '21 at 17:25