Search code examples
phppythonencryptionaespycrypto

PHP openssl AES in Python


I am working on a project where PHP is used for decrypt AES-256-CBC messages

<?php

class CryptService{
    private static $encryptMethod = 'AES-256-CBC';
    private $key;
    private $iv;

    public function __construct(){
        $this->key = hash('sha256', 'c7b35827805788e77e41c50df44441491098be42');
        $this->iv = substr(hash('sha256', 'c09f6a9e157d253d0b2f0bcd81d338298950f246'), 0, 16);
    }

    public function decrypt($string){
        $string = base64_decode($string);
        return openssl_decrypt($string, self::$encryptMethod, $this->key, 0, $this->iv);
    }

    public function encrypt($string){
        $output = openssl_encrypt($string, self::$encryptMethod, $this->key, 0, $this->iv);
        $output = base64_encode($output);
        return $output;
    }
}

$a = new CryptService;
echo $a->encrypt('secret');
echo "\n";
echo $a->decrypt('S1NaeUFaUHdqc20rQWM1L2ZVMDJudz09');
echo "\n";

ouutput

>>> S1NaeUFaUHdqc20rQWM1L2ZVMDJudz09
>>> secret

Now I have to write Python 3 code for encrypting data. I've tried use PyCrypto but without success. My code:

import base64
import hashlib
from Crypto.Cipher import AES

class AESCipher:
    def __init__(self, key, iv):
        self.key = hashlib.sha256(key.encode('utf-8')).digest()
        self.iv = hashlib.sha256(iv.encode('utf-8')).digest()[:16]

    __pad = lambda self,s: s + (AES.block_size - len(s) % AES.block_size) * chr(AES.block_size - len(s) % AES.block_size)
    __unpad = lambda self,s: s[0:-ord(s[-1])]

    def encrypt( self, raw ):
        raw = self.__pad(raw)
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        return base64.b64encode(cipher.encrypt(raw))

    def decrypt( self, enc ):
        enc = base64.b64decode(enc)
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv )
        return self.__unpad(cipher.decrypt(enc).decode("utf-8"))

cipher = AESCipher('c7b35827805788e77e41c50df44441491098be42', 'c09f6a9e157d253d0b2f0bcd81d338298950f246')

enc_str = cipher.encrypt("secret")
print(enc_str)

output

>>> b'tnF87LsVAkzkvs+gwpCRMg=='

But I need output S1NaeUFaUHdqc20rQWM1L2ZVMDJudz09 which will PHP decrypt to secret. How to modify Python code to get expected output?


Solution

  • PHP's hash outputs a Hex-encoded string by default, but Python's .digest() returns bytes. You probably wanted to use .hexdigest():

    def __init__(self, key, iv):
        self.key = hashlib.sha256(key.encode('utf-8')).hexdigest()[:32].encode("utf-8")
        self.iv = hashlib.sha256(iv.encode('utf-8')).hexdigest()[:16].encode("utf-8")
    

    The idea of the initialization vector (IV) is to provide randomization for the encryption with the same key. If you use the same IV, an attacker may be able to deduce that you send the same message twice. This can be considered as a broken protocol.

    The IV is not supposed to be secret, so you can simply send it along with the ciphertext. It is common to prepend it to the ciphertext during encryption and slice it off before decryption.