skip to Main Content

Python Code:

     from Crypto.Random import get_random_bytes
     from Crypto.Cipher import AES
     from Crypto.Util.Padding import pad
     import base64

     def encrypt_string(input_string, key_base64, str_iv):
         try:
            key = key_base64.encode('utf-8')
        
            if str_iv:
               iv = base64.b64decode(str_iv)
            else:
               iv = get_random_bytes(16)

            cipher = AES.new(key, AES.MODE_CBC, iv)
            padded_data = pad(input_string.encode(), AES.block_size)
            cipher_data = cipher.encrypt(padded_data)
            combined_data = iv + cipher_data
            return base64.b64encode(combined_data).decode()

         except Exception as e:
            print(e)

    if __name__ == "__main__":
        key = "1bd393e7a457f9023d9ba95fffb5a2e1"
        iv = "1oTOhV9xGyu1mppmWZWa5w=="
        input_string = "AAAAAAA"
        encrypted_data = encrypt_string(input_string, key, iv)
        print("Encrypted string:", encrypted_data)`

OUTPUT :
Encrypted string: 1oTOhV9xGyu1mppmWZWa5+kzveiTRzRH+gRVHx+7Ad0=

PHP code:

      <?php
       function encrypt_string($input_string, $key_base64, $str_iv) {
        try {
         $key = base64_decode($key_base64);

        if ($str_iv) {
            $iv = base64_decode($str_iv);
        } else {
            $iv = openssl_random_pseudo_bytes(16);
        }
        $ciphertext = openssl_encrypt($input_string, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
        $combined_data = $iv . $ciphertext;
        return base64_encode($combined_data);
       } catch (Exception $e) {
        echo $e->getMessage();
       }
      }
      $key = "1bd393e7a457f9023d9ba95fffb5a2e1";
       $iv = "1oTOhV9xGyu1mppmWZWa5w==";
       $input_string = "AAAAAAA";
       $encrypted_data = encrypt_string($input_string, $key, $iv);
       echo "Encrypted string: " . $encrypted_data . "n";
      ?>

OUTPUT:
Encrypted string: 1oTOhV9xGyu1mppmWZWa53Nc8rxWTultBWLvWitUICQ=

Please, does anyone know how to make the output of these two codes the same?

2

Answers


  1. The issue is in your PHP code. The openssl_encrypt function takes a passpharse argument:

    If the passphrase is shorter than expected, it is silently padded with NUL characters; if the passphrase is longer than expected, it is silently truncated.

    For AES 256, this should be 32 characters. Your $key variable is getting padded with null bytes.

    The difference is in the first line of each function.

    In your Python code, this just converts the ASCII key from a string to bytes. Since the length of the initial key is 32, this is fine.

        key = key_base64.encode('utf-8')
    

    In your PHP, this decodes the from base 64 back to bytes, which shortens the key to 24 bytes.

        $key = base64_decode($key_base64);
    

    To make the Python code the same as the PHP code, you would need to do:

    key = base64.b64decode(key)
    

    However, this will fail later on in the code in Python because the length of the key is only 24, and Python does not silently pad the key.

    One work around is to use a hash of the key for the passphrase and truncate it to 32 characters. Keep in mind this is NOT a good security practice, see @maarten-bodewes comment below. Here are examples in Python and PHP. I removed the exception handling for simplicity.

    from cryptography.hazmat.primitives.ciphers import Cipher, modes
    from cryptography.hazmat.primitives.ciphers.algorithms import AES256
    from cryptography.hazmat.primitives.padding import PKCS7
    import hashlib
    import base64
    
    
    def encrypt_string(input_string: str, key: bytes, iv: bytes):
        # the key must be of length 32
        key_hash = hashlib.sha256(key).digest()[:32]
        cipher = Cipher(AES256(key_hash), modes.CBC(iv))
        pad = PKCS7(AES256.block_size).padder()
        
        encryptor = cipher.encryptor()
        cipher_data = encryptor.update(
            pad.update(input_string.encode('utf-8')) 
            + pad.finalize()
        )
        combined_data = iv + cipher_data
        return base64.b64encode(combined_data)
    
    if __name__ == '__main__':
        key = b'password'
        iv = b'aaaabbbbccccdddd'
        input_string = "abcdefghijklmnopqrstuvwxyz"
        encrypted_data = encrypt_string(input_string, key, iv)
        print("Encrypted string:", encrypted_data.decode())
    
    # prints:
    # Encrypted string: YWFhYWJiYmJjY2NjZGRkZALrlaYfFmHrO6FOv64BdVueK/yYNjjgtdQx+A0eR9iv
    

    And the PHP code:

    <?php
    function encrypt_string($input_string, $key, $iv) {
        // take the first 32 characters, just to be explicit
        $key_hash = substr(hash('sha256', $key, true), 0, 32);
        $ciphertext = openssl_encrypt(
            $input_string, "aes-256-cbc", $key_hash, OPENSSL_RAW_DATA, $iv
        );
        $combined_data = $iv . $ciphertext;
        return base64_encode($combined_data);
    }
    
    $key = 'password';
    $iv = "aaaabbbbccccdddd";
    $input_string = "abcdefghijklmnopqrstuvwxyz";
    $encrypted_data = encrypt_string($input_string, $key, $iv);
    echo "Encrypted string: " . $encrypted_data . "n";
    ?>
    
    // echos:
    // Encrypted string: YWFhYWJiYmJjY2NjZGRkZALrlaYfFmHrO6FOv64BdVueK/yYNjjgtdQx+A0eR9iv
    
    Login or Signup to reply.
  2. If you want to get the same result then just remove the $key = base64_decode($key_base64). I did get the right result for $key = $key_base64.


    Note that using the base64 encoding directly as key means that a maximum of 6 bits contain random information for each character / byte of input. So if you insert 32 characters then the key size is effectively 32 * 6 = 192 bits. That’s more than enough, but clearly less than the 256 bits "promised" by AES-256.

    The other issue is that CBC doesn’t offer integrity / authenticity.


    I’d recommend never to convert keys to strings in the first place – just keep them binary. Furthermore, if you’re going to encrypt with a symmetric key then use something like NaCL or another library that performs higher level "sealing" rather than just encryption. If you want to use a password then you should use a good Password Based Key Derivation Function (PBKDF) such as PBKDF2 or Argon2.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search