0

We are using below java code to decrypt the data which is encrypted using AES-256 in CBC mode and PKCS7 padding.

Java Code:

import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import java.security.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;


public class AES256 {

    private static byte[] initVector = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

    public String decrypt (String encryptedDataBase64, String keyBase64)
    {
       try {
           Security.setProperty("crypto.policy", "unlimited");
           IvParameterSpec ivSpec = new IvParameterSpec(initVector);  // Get the init vector

           // Get the Base64-encoded key
           byte[] key = Base64.decodeBase64(keyBase64.getBytes("UTF-8"));
           Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");  // AES / CBC / PKCS5 padding
           SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
           cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec);
           byte[] encryptedData = Base64.decodeBase64(encryptedDataBase64.getBytes("UTF-8"));
           byte[] decryptedData = cipher.doFinal(encryptedData);

           return new String(decryptedData);
       }
       catch (Exception e) {
           logger.error("AES256 Decrypt: Decryption exception: "+ e.getMessage());
           return null;
       }
    }
}

Now we need to convert this decryption logic to Python as our app is sending the encrypted data in the headers while requesting for index.html from server. I tried to decrypt using Crypto. It is giving the decrypted string but also some additional characters in the end.

import base64
from Crypto.Cipher import AES

key = base64.b64decode(myKeyBase64)
enc = base64.b64decode(encDataBase64)
ivBase = base64.b64decode('AAAAAAAAAAAAAAAAAAAAAA==');
cipher = AES.new(key, AES.MODE_CBC, ivBase)
cipher.decrypt(enc).decode('utf-8')

It is decrypting properly but in the end it is giving some extra characters which are not in the original string like 'myText\x06\x06\x06\x06\x06\x06'.

I tried this after reading some of stack over flow questions. Can any one please let me know if there is any error in the code.

kadina
  • 5,042
  • 4
  • 42
  • 83
  • On Java side you are instantiate your cipher with '...PKCS5PADDING' that means that the plaintext is filled up with characters to have a length of 16 (or multiple of 16). The number of character that are added is the value added, so when adding 6 characters each padding character gets the value x06 as in your example. When decrypting (that's your part above) you need to strip of the last 6 x06 characters. – Michael Fehr Sep 17 '20 at 22:42
  • Is it true for all the strings? I mean for any string I encrypt, does it always return 6 extra characters if I decrypt through my python code. Btw, my team is using PKCS7 padding when they are encrypting on Java side. I guess I didn't understand your comment properly. Can you please elaborate. – kadina Sep 17 '20 at 23:00

1 Answers1

2

To encrypt a byte array with AES you do need exactly a 16 byte long array - then you could use a '...NoPadding'.

Having e.g. a 13 character long string and you transform the string to a byte array this array is 13 bytes long. Using PKCS5 the byte array is filled up with 3 bytes of value x03. If you need to fill up 7 characters all 7 bytes will have the value x07, missing 10 chars result in 10 bytes of x0a.

To strip of the padding just read the last byte (e.g. x0a = '10') and remove the last 10 bytes to get the original string.

On Java-side the naming is PKCS5Padding that is (mostly) identical to PKCS7Padding (this naming is used in other frameworks/languages).

Michael Fehr
  • 5,827
  • 2
  • 19
  • 40
  • Excellent. Thanks Michael. – kadina Sep 17 '20 at 23:14
  • I don't know if it's the same case in Python but in C# it is not recommended to use `None` in the `paddingMode` when configuring the AES cryptographic object used to perform the symmetric algorithm. Refer to the reply from @rossum in [this](https://stackoverflow.com/a/29589427/8855858) answer to a similar question. – badjuice Mar 15 '22 at 12:04
  • 1
    @badjuice: using a "None" padding forces the programmer that the input is **always** a multiple of 16 bytes. There are only few cases where it makes sense (e.g. if your bank account number is exact 16 digits long). In all other cases you should use a PKCS5/7 padding scheme. – Michael Fehr Mar 15 '22 at 12:57
  • @MichaelFehr the bottom line is that using a padding scheme is better most of the time, specially from the perspective of the programmer since it prevents the invalid padding exceptions. Also, thanks for your reply. – badjuice Mar 23 '22 at 12:14