1

I am tasked with converting this java code to python. And I am unable to achieve the same result as java in python. I need help in converting this code. Any hints are greatly appreciated.

Java Code

import org.apache.commons.lang.RandomStringUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import java.util.Base64;
    
        public String decryptTextusingAES(String text, String kek) throws Exception{
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        byte[] keyBytes= new byte[16];  
        byte[] b= kek.getBytes("UTF-8");
        int len= b.length;
        if (len> keyBytes.length) len = keyBytes.length;
        System.arraycopy(b, 0, keyBytes, 0, len);
    
        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
        
        IvParameterSpec ivSpec = new IvParameterSpec(keyBytes);
        
        cipher.init(Cipher.DECRYPT_MODE,keySpec,ivSpec);
        BASE64Decoder decoder = new BASE64Decoder();
        
        byte [] results = cipher.doFinal(decoder.decodeBuffer(text));

        return new String(results,"UTF-8");
    }
    

Python Code

from Crypto.Cipher import AES  
from base64 import b64decode

BLOCK_SIZE = AES.block_size
pad = lambda s: s + (BLOCK_SIZE - len(s.encode()) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s.encode()) % BLOCK_SIZE)
unpad = lambda s: s[:-ord(s[len(s) - 1:])]


class AESCipher:
    def __init__(self, secretkey: str):
        self.key = secretkey  # key
        self.iv = secretkey[0:16]  # offset

    def decrypt(self, encrypted_text):
        encrypted_text = b64decode(encrypted_text)
        cipher = AES.new(key=self.key.encode(), mode=AES.MODE_CBC, IV=self.iv.encode())
        decrypted_text = cipher.decrypt(encrypted_text)
        print("unpad : ",decrypted_text)
        return unpad(decrypted_text).decode('utf-8')

if __name__ == '__main__':
    # message to encrypt 
    message = "mxoqPaEdpujwTOmeKGomHgXAgdVc0Ca7cm0Qqjm2WMQ="
    secret_key = "935554679694777871163316"
    AES_pkcs5_obj = AESCipher(secret_key)
    
    decrypted_message = AES_pkcs5_obj.decrypt(message)
    print("Decrypted : ",decrypted_message)

Python Code keeps giving this output:

unpad :  b'rt\xfb\x98o\x8f.\t\xb1d0tx-i\x88\xd1\xfdV\xef\xece\xf2\x8a\xf7\xf8\x00O\xdb\x8c\xcb\xf2'
Decrypted : 

But it should give this in decrypted: 284174634921775587013963

This is encrpted string "mxoqPaEdpujwTOmeKGomHgXAgdVc0Ca7cm0Qqjm2WMQ="

This is the key that should be used "935554679694777871163316" to decrypt the above string.

  • The Java code is wrong. A text to be decrypted is never of type `String` it should be of type `byte[]`. Converting a byte array to a string without proper encoding e.g. base64 can result in changed data and thus decryption will fail. – Robert Aug 22 '22 at 11:42
  • Try the answers for pkcs5padding in this link. [implementing-aes-ecb-pkcs5-padding-in-python](https://stackoverflow.com/questions/64203881/implementing-aes-ecb-pkcs5-padding-in-python) – Nick Dong Jul 12 '23 at 02:13

1 Answers1

2

Your key appears to have only 192bit (24 chars) ... You rely on an automatic detection of the correct AES algorithm. As far as I know python in this configuration has only AES256 available for which you need a 256bit key (32 chars). There might also be a problem with Algorithms trying to stretch the key to fit allow the use with AES256, which might be done differently between languages/frameworks.

Have a look at those reference implementations, they are compatible with each other. You can go from there and continue your development.

Java:

import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

    private static final IvParameterSpec IV = new IvParameterSpec(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 });

    void testEncryption() throws GeneralSecurityException {
        final byte[] key = "AnyRandomInsecure256bitLongKeyXX".getBytes(StandardCharsets.US_ASCII);

        final SecretKeySpec keySpec = new SecretKeySpec(key, "AES");

        final String encrypted = encrypt("data", keySpec, IV);
        final String decrypted = decrypt(encrypted, keySpec, IV);

        System.out.printf("%s <-> %s", encrypted, decrypted);
    }

    String encrypt(final String data, final SecretKeySpec keySpec, final IvParameterSpec ivSpec)
            throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);

        return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes(StandardCharsets.UTF_8)));
    }

    String decrypt(final String cipherText, final SecretKeySpec keySpec, final IvParameterSpec ivSpec)
            throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

        return new String(cipher.doFinal(Base64.getDecoder().decode(cipherText)), StandardCharsets.UTF_8);
    }

Python:

from Crypto.Cipher import AES
from base64 import b64decode, b64encode

IV = bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])

BLOCK_SIZE = 16
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
unpad = lambda s: s[:-ord(s[len(s) - 1:])]


def encrypt(plain_text, key):
    plain_text = pad(plain_text)
    cipher = AES.new(key, AES.MODE_CBC, IV)
    return b64encode(cipher.encrypt(plain_text))


def decrypt(cipher_text, key):
    cipher_text = b64decode(cipher_text)
    cipher = AES.new(key, AES.MODE_CBC, IV)
    return unpad(cipher.decrypt(cipher_text))

if __name__ == '__main__':
    key = "AnyRandomInsecure256bitLongKeyXX".encode()

    encrypted = encrypt("data", key)
    decrypted = decrypt(encrypted, key)
    print(f"{encrypted} <-> {decrypted}")

It is also a best-practice to generate a random IV and then prefix it to the ciphertext you generate. Have a look here

Horst
  • 334
  • 1
  • 12