Skip to content

Instantly share code, notes, and snippets.

@eoli3n
Last active April 1, 2024 20:08
Show Gist options
  • Star 27 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save eoli3n/d6d862feb71102588867516f3b34fef1 to your computer and use it in GitHub Desktop.
Save eoli3n/d6d862feb71102588867516f3b34fef1 to your computer and use it in GitHub Desktop.
Encrypt - Decrypt AES from/to Python PyCryptodome from/to PHP openssl
<?php
// use to generate key : 'openssl rand -hex 32'
function my_encrypt($data, $passphrase) {
$secret_key = hex2bin($passphrase);
$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 my_decrypt($data, $passphrase) {
$secret_key = hex2bin($passphrase);
$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);
return $decrypted;
}
#!/usr/bin/env python3
# pip install pycryptodome
import json
import binascii
from Crypto.Cipher import AES
from Crypto import Random
import base64
def my_encrypt(data, passphrase):
"""
Encrypt using AES-256-CBC with random/shared iv
'passphrase' must be in hex, generate with 'openssl rand -hex 32'
"""
try:
key = binascii.unhexlify(passphrase)
pad = lambda s : s+chr(16-len(s)%16)*(16-len(s)%16)
iv = Random.get_random_bytes(16)
cipher = AES.new(key, AES.MODE_CBC, iv)
encrypted_64 = base64.b64encode(cipher.encrypt(pad(data).encode())).decode('ascii')
iv_64 = base64.b64encode(iv).decode('ascii')
json_data = {}
json_data['iv'] = iv_64
json_data['data'] = encrypted_64
clean = base64.b64encode(json.dumps(json_data).encode('ascii'))
except Exception as e:
print("Cannot encrypt datas...")
print(e)
exit(1)
return clean
def my_decrypt(data, passphrase):
"""
Decrypt using AES-256-CBC with iv
'passphrase' must be in hex, generate with 'openssl rand -hex 32'
# https://stackoverflow.com/a/54166852/11061370
"""
try:
unpad = lambda s : s[:-s[-1]]
key = binascii.unhexlify(passphrase)
encrypted = json.loads(base64.b64decode(data).decode('ascii'))
encrypted_data = base64.b64decode(encrypted['data'])
iv = base64.b64decode(encrypted['iv'])
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted = cipher.decrypt(encrypted_data)
clean = unpad(decrypted).decode('ascii').rstrip()
except Exception as e:
print("Cannot decrypt datas...")
print(e)
exit(1)
return clean
@paulgullett
Copy link

Thank you, would have taken me hours to figure it all out. Worked perfectly for me.

@nksCH
Copy link

nksCH commented Jun 12, 2020

Great! Thanks!

@samdiesbergen
Copy link

Do both files have to share the same passcode?

@eoli3n
Copy link
Author

eoli3n commented Nov 30, 2020

What do you mean ?

@samdiesbergen
Copy link

samdiesbergen commented Nov 30, 2020

Sorry for my unclear question when you pass the passphrase to the encrypt function in python, do you have to pass the same passphrase to the decrypt function in php?

@eoli3n
Copy link
Author

eoli3n commented Nov 30, 2020

That's the purpose of the gist, yes

@nox0x1
Copy link

nox0x1 commented Dec 24, 2020

Thank you for this. I'm running into a few issues running it on Python 3.8.2
What're the package dependencies of this Python code? And what import statements are needed?
I'm assuming we need to have pycryptodome (as pycrypto is depreciated) but if you can list all the import statements that would be helpful.
I'm also getting an error: Object type <class 'str'> cannot be passed to C code when calling the function as:

teststr = my_encrypt('MacBook', '06a5013e2b4540557002e315a171f0120d6b6ef2c84f60070d0bf321a3bbefa3')

Not sure if my usage is correct.

@eoli3n
Copy link
Author

eoli3n commented Dec 24, 2020

I tried with pycryptodome and faced the same issue Object type <class 'str'> cannot be passed to C code.
To test, add under python3 shebang

import binascii
from Crypto.Cipher import AES
from Crypto import Random
import base64

Then run

➜ python3
Python 3.9.1 (default, Dec 13 2020, 11:55:53) 
[GCC 10.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> exec(open("/home/user/encryption.py").read())
>>> my_encrypt('test', '3e4fa62891d19feacd7f1234d558a752251fe4005f821469052e05e890e99d30')
Cannot encrypt datas...
Object type <class 'str'> cannot be passed to C code

I found this: https://stackoverflow.com/a/55036604
No time for this right now, i will try harder later.

@rakeshsivaji
Copy link

I am trying with python , I am passing data(argument) as the response.text that comes form the api response which is encrypted data, it is unicode (data type)
I am using python 2.7
exception message i am getting : Cannot decrypt datas...
Odd-length string

The following code i used

def get_report_documenturl(report_documentid, auth, headers):

url = "https://{}/reports/2020-09-04/documents/{}"
response = requests.get(url.format(host_url, report_documentid), headers=headers, auth=auth )
geturl = response.json()
data = geturl.get("payload")
resp_document_url= data.get("url")
IV = data.get("encryptionDetails").get("initializationVector")
key = data.get("encryptionDetails").get("key")
response = requests.get(resp_document_url)
enc = response.text    #type : unicode
my_decrypt(enc, key)

def my_decrypt(data, passphrase):
try:
passphrase_val =repr(passphrase).encode('utf-8') # converting the unicode to hex
unpad = lambda s : s[:-s[-1]]
key = binascii.unhexlify(passphrase_val)
encrypted = json.loads(base64.b64decode(data).decode('ascii'))
encrypted_data = base64.b64decode(encrypted['data'])
iv = base64.b64decode(encrypted['iv'])
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted = cipher.decrypt(encrypted_data)
clean = unpad(decrypted).decode('ascii').rstrip()
except Exception as e:
print("Cannot decrypt datas...")
print(e)
exit(1)
return clean

@eoli3n
Copy link
Author

eoli3n commented Aug 10, 2021

@mxmfrlv
Copy link

mxmfrlv commented Mar 4, 2022

I tried with pycryptodome and faced the same issue Object type <class 'str'> cannot be passed to C code.

line 13 should be "encrypted_64 = base64.b64encode(cipher.encrypt(pad(data).encode())).decode('ascii')"

@eoli3n
Copy link
Author

eoli3n commented Mar 4, 2022

Just tried, it worked ! Thanks

➜ python3
Python 3.10.2 (main, Jan 15 2022, 03:11:32) [GCC 10.2.1 20201203] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> exec(open("/home/user/encryption.py").read())
>>> my_encrypt('test', '3e4fa62891d19feacd7f1234d558a752251fe4005f821469052e05e890e99d30')
b'eyJpdiI6ICJ3NHJSb3RZVUw4ZWJKeDNGWW9EUHZRPT0iLCAiZGF0YSI6ICJHZnByd3NRS3BBNVd4cU1mZUZVaEt3PT0ifQ=='
>>> my_decrypt('eyJpdiI6ICJ3NHJSb3RZVUw4ZWJKeDNGWW9EUHZRPT0iLCAiZGF0YSI6ICJHZnByd3NRS3BBNVd4cU1mZUZVaEt3PT0ifQ==', '3e4fa62891d19feacd7f1234d558a752251fe4005f821469052e05e890e99d30')
'test'

I update the gist.

@abhineet-space
Copy link

abhineet-space commented Jul 13, 2022

can you let help me write code in python for aes-128-ctr

my code in js is -

const data = "Hello there, help me to decrypt using python"
let iv = 'N13FF0F4ACC4097M'
let key = '312s389g2r5b54rd'
const cp = crypto.createCipheriv('aes-128-ctr', key, iv)
let enc = cp.update(data, 'utf-8', 'base64') + cp.final('base64')
console.log(enc)


#output = 401Auq5PXMVzcbTJXl5hgLRccO8RSKFnB4VjsQuzkUNwJKMmwWHNqILIa1Q=

i am confused with ctr i.e. counter & nounce in python with iv in nodejs .
Can you help me to produce the same output

@eoli3n
Copy link
Author

eoli3n commented Jul 18, 2022

I can't, sorry

@abhineet-space
Copy link

Hello @eoli3n can you help me let me know how nonce and ctr is created in Node-js code?

@sdamediadev
Copy link

i tried the code above, its working when encrypt decrypt using python..
but, when i decrypt using php the encrypted text in python.. its not working..

is anyone have the same issue ?

@sdamediadev
Copy link

<?php
function decrypt($data) {
    $passphrase = '71b9111bd63e1f4d54f347938d6f2881';
    $secret_key = hex2bin($passphrase);
    $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);
    
    echo $decrypted;
    return $decrypted;
}

$res = decrypt("eyJpdiI6ICJLWTBiOFNMcy9GY1c3RFY1SjI4OUJBPT0iLCAiZGF0YSI6ICI4b3M1d3J5YzdzaFU4d0NoV1RIK1NiK3FiUVhPNnRjMzBSRjllaDlCdDFVPSJ9");
echo $res;
?>

@eoli3n
Copy link
Author

eoli3n commented Nov 15, 2022

@SDAMedia you didn't give the passphrase to the function.

$res = decrypt("eyJpdiI6ICJLWTBiOFNMcy9GY1c3RFY1SjI4OUJBPT0iLCAiZGF0YSI6ICI4b3M1d3J5YzdzaFU4d0NoV1RIK1NiK3FiUVhPNnRjMzBSRjllaDlCdDFVPSJ9", "YOUR_PASSPHRASE_HERE");

@sdamediadev
Copy link

sdamediadev commented Nov 15, 2022

the passphrase already in the function, so i'm not passing the passphrase in the function.. @eoli3n

@sdamediadev
Copy link

can anyone help me..

@mxmfrlv
Copy link

mxmfrlv commented Nov 17, 2022

@SDAMedia in openssl_decrypt, change the algorithm from 'AES-256-CBC' to 'AES-128-CBC'. The length of your passphrase corresponds actually to the latter.
(Compare "openssl rand -hex 32" and "openssl rand -hex 16", the number of characters in hex representation is doubled)

@sdamediadev
Copy link

ah I see @mxmfrlv thank you very much, I remember when I implemented the encryption above in my python code, it's not working with hex 32, so I am using hex 16.

then when I change the algorithm from AES-256-CBC to AES-128-CBC, it's now working.

@ruimelodev
Copy link

ruimelodev commented Nov 23, 2022

This is returning an error when I try to encrypt alphabet characters with diacritics in Python.
Example:

my_encrypt("Crème Brûlée", "hash")
# Output:
# Data must be padded to 16 byte boundary in CBC mode

Is there a way for me to have this method accept all types of characters?

@mspi21
Copy link

mspi21 commented Apr 1, 2024

Security considerations

You are aware of the fact that the parameters that you're referring to as "passphrases" are really not passphrases, but cryptographic keys, which have to consist of cryptographically strong random bytes, are you not?

You can generate symmetric encryption keys directly in your code (maybe via a separate function, e.g. generate_secure_encryption_key()) using the same way you're generating your initialization vectors, i.e. through Crypto.Random (or alternatively, os.urandom). The key needs to then be stored somewhere safe, e.g. a file readable only by its owner.

If you need to use a passphrase for encryption, such as the string "a quick brown fox jumps over the lazy dog", you need to use a key derivation function to transform your passphrase into a suitable key.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment