3

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?

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you Artjom. One more thing I had to change is double calling base64.b64encode in encrypt method. After that I get the same output from Python as from PHP. I don't know why there is calling base64_encode in PHP although openssl_encrypt returns base64 encoded result.
It's probably doing this, because the original developer had no idea what they were doing.
0

crypto.php

function encrypt($data, $passphrase)
{
    if(is_array($data)) $data = json_encode($data);
    $secret_key = hash('sha256', $passphrase, true);
    $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
    $encrypted_64 = openssl_encrypt($data, 'aes-256-cbc', $secret_key, 0, $iv);
    $iv_64 = base64_encode($iv);
    $json = new stdClass();
    $json->iv = $iv_64;
    $json->data = $encrypted_64;
    return base64_encode(json_encode($json));
}

function decrypt($data, $passphrase)
{
    $secret_key = hash('sha256', $passphrase, true);
    $json = json_decode(base64_decode($data));
    $iv = base64_decode($json->{'iv'});
    $encrypted_64 = $json->{'data'};
    $data_encrypted = base64_decode($encrypted_64);
    $decrypted = openssl_decrypt($data_encrypted, 'aes-256-cbc', $secret_key, OPENSSL_RAW_DATA, $iv);
    $decoded = json_decode($decrypted,true) ;
    return is_array($decoded) ? $decoded : $decrypted;
}

python crypto.py

import base64
import hashlib
from Crypto.Cipher import AES
import json


def pad(data):
   block_size = AES.block_size
   padding = block_size - len(data) % block_size
   return data + bytes([padding] * padding)

def encrypt(data, passphrase):
   if isinstance(data, dict):
      data = json.dumps(data)
   secret_key = hashlib.sha256(passphrase.encode()).digest()
   iv = AES.new(secret_key, AES.MODE_CBC).iv
   cipher = AES.new(secret_key, AES.MODE_CBC, iv)
   data = data.encode('utf-8')
   padded_data = pad(data)   
   encrypted_data = cipher.encrypt(padded_data)
   iv_64 = base64.b64encode(iv).decode()
   encrypted_64 = base64.b64encode(encrypted_data).decode()
   son_data = {"iv": iv_64, "data": encrypted_64}
   return base64.b64encode(json.dumps(json_data).encode()).decode()


def decrypt(encrypted_data, passphrase):
   secret_key = hashlib.sha256(passphrase.encode()).digest()
   json_data = json.loads(base64.b64decode(encrypted_data))
   iv = base64.b64decode(json_data['iv'])
   encrypted_data = base64.b64decode(json_data['data'])
   cipher = AES.new(secret_key, AES.MODE_CBC, iv)
   decrypted_data = cipher.decrypt(encrypted_data) 
   decrypted_data = decrypted_data[:-decrypted_data[-1]]
   decrypted_data = decrypted_data.decode('utf-8')
   loaded_data = json.loads(decrypted_data)
   if isinstance(loaded_data, dict):
      return loaded_data
return decrypted_data

2 Comments

it would be great if you could provide some explanation on how your answer addresses the question. what did you modify? why did you have to modify it? a wall of code is a little overwhelming without some explanation.
Sorry, I shared the codes that I edited without an explanation because I have little knowledge of English and it is easy to understand from the name definition of the function. codes to decrypt the data encrypted in python with the same key value as php. you can decrypt the data encrypted with php with python, in the same way you can decrypt the data encrypted in pyhon in php. what you need to do is to transfer the data returned from the function to the other language with the key.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.