简体   繁体   中英

Encrypt in PHP and decrypt in Python or openssl command line

A lot of data in a database I took over contains encrypted fields. The method used to encrypt the data is the following PHP code:

<?php
$text = "test 1234\ntest 2345\ntest 3456\ntest 4567";
$key = "0123456789abcdefghijklmnopqrstuv";
$enc = openssl_encrypt($text, "AES-256-CBC", $key);
echo "Raw: " . $text . "\n";
echo "Key: " . $key . "\n";
echo "Key (Hex) " . bin2hex($key) . "\n";
echo $enc;
echo "\n";
?>

When I run the code I get the following output including a warning about the empty initialization vector (iv), which I have to ignore because the whole DB data is encrypted this way (I perfectly know this should not be done this way).

Warning: openssl_encrypt(): Using an empty Initialization Vector (iv) is potentially insecure and not recommended in /tmp/cp3_encdec/enc2.php on line 5
Raw: test 1234
test 2345
test 3456
test 4567
Key: 0123456789abcdefghijklmnopqrstuv
Key (Hex) 303132333435363738396162636465666768696a6b6c6d6e6f70717273747576
uPNXdo2K0Gvy/+MW0YFR7utFsrNDAp8yYaDxT352W3lPKNOkNMg+l3eFKEi0zeze

Decrypt using php openssl_decrypt($encrypted, "AES-256-CBC", $key) gives me full output. Php manual does not give a lot of insight what is used to encrypt in regards to padding and iv when leaving those values empty.

Next I tried to decrypt on the command line using the openssl command:

echo "uPNXdo2K0Gvy/+MW0YFR7utFsrNDAp8yYaDxT352W3lPKNOkNMg+l3eFKEi0zeze" | openssl aes-256-cbc -d -a -K 303132333435363738396162636465666768696a6b6c6d6e6f70717273747576 -iv 0

which works fine and returns the initial input:

test 1234
test 2345
test 3456
test 4567

Trying to decrypt in Python using the following code results in wrong decryption:

import base64
from Crypto.Cipher import AES 

PAD = u'\0000' 

def decrypt(enc, key):
    decobj = AES.new(key, AES.MODE_ECB)
    data = decobj.decrypt(base64.b64decode(enc))
    data = data.rstrip(PAD.encode())
    print(str(data))

key = "0123456789abcdefghijklmnopqrstuv"
decrypt("uPNXdo2K0Gvy/+MW0YFR7utFsrNDAp8yYaDxT352W3lPKNOkNMg+l3eFKEi0zeze", key)

Result, first 16 bytes are readable but not the rest:

b'test 1234\ntest 2\x8b\xc7b|\xf9\xef\xa3\x1f\xd2\xcc\xd7#\xe7\x8b%\x8b\x981\x92\x87v4\xa8;h\xa9\xf8Fw\x7fRp'

Modifying my input to contain more data will also break decryption using the openssl command:

Raw: [system] test:1234
[system] test:2345
[database] test:3456
[unknown] test:4567

Key: 0123456789abcdefghijklmnopqrstuv
Key (Hex) 303132333435363738396162636465666768696a6b6c6d6e6f70717273747576
9KWsGGLa1/g3f36kUJJ/oHNiEnIDorZULwR8pXZHwJhul2XsdZLwLN8jMptP9fcWgY42oTq7RTm+/8CKPiGFPWrY/3neLvf8UNedsVuKRlc=

Openssl command line:

echo "9KWsGGLa1/g3f36kUJJ/oHNiEnIDorZULwR8pXZHwJhul2XsdZLwLN8jMptP9fcWgY42oTq7RTm+/8CKPiGFPWrY/3neLvf8UNedsVuKRlc=" | openssl aes-256-cbc -d -a -K 303132333435363738396162636465666768696a6b6c6d6e6f70717273747576 -iv 0
bad decrypt
15143:error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-64/src/crypto/evp/evp_enc.c:323:

The same using Python code from above will result in first 16 bytes readable, but not the rest:

b'[system] test:12\xc7\x91\xa6C\x11\xa3\xa4\x8cR\x12#\x84$\xf7\x0c\xd4IP!F6\xa8\xed0Np\x1d\xc7\x174\xa5\xc5N\xe3\x00\x9f\x01\xa8\xc3\x18\xea\x158\xc0:\x9b\x9cx\xee\xf9X\xfc\x1a\xcf J\xca\xc5\xf4\xbf\x08\x16\x8f<'

Again if using php openssl_decrypt works:

<?php
$text = "9KWsGGLa1/g3f36kUJJ/oHNiEnIDorZULwR8pXZHwJhul2XsdZLwLN8jMptP9fcWgY42oTq7RTm+/8CKPiGFPWrY/3neLvf8UNedsVuKRlc=";
$key = "0123456789abcdefghijklmnopqrstuv";
$dec = openssl_decrypt($text, "AES-256-CBC", $key);
echo $dec;
echo "\n";
?>

[system] test:1234
[system] test:2345
[database] test:3456
[unknown] test:4567

Someone has an idea how php encrypts the data, I suppose it is a padding problem but am not sure and I am open for any help on this topic.

I made some modifications to my Python code which seems to solve the issue:

import base64
from Crypto.Cipher import AES 

IV = 16 * '\x00'

def decrypt(enc, key):
    decobj = AES.new(key, AES.MODE_CBC, IV)
    data = decobj.decrypt(base64.b64decode(enc))
    print(str(data.decode()))

key = "0123456789abcdefghijklmnopqrstuv"
decrypt("uPNXdo2K0Gvy/+MW0YFR7utFsrNDAp8yYaDxT352W3lPKNOkNMg+l3eFKEi0zeze", key)

On the command line I did not found a solution yet.

Example of decoding with use of chunks, my working alternative for PHP openssl_private_decrypt

Сreate private key:

openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen-bits 1024

Сreate public key:

openssl rsa -pubout -in private.pem -out public.pem

You may need to create RSA key file starting with -----BEGIN RSA PRIVATE KEY----- :

openssl rsa -in private.pem -out private_rsa.pem

Python3 code:

   from cryptography.hazmat.primitives import serialization
   from cryptography.hazmat.primitives.asymmetric import padding
   import zlib

   file = open('private_rsa.pem', 'rb')
   priv_key = file.read()
   file.close()

   encryptedString=''' YOUR ENCRYPTED STRING '''

   private_key = serialization.load_pem_private_key(priv_key, password=None, backend=default_backend())
   chunk_size = math.ceil(1024/8)
   offset = 0
   decrypted = bytearray()
   while offset < len(encryptedString):
       decrypted += private_key.decrypt(encryptedString[offset: offset + chunk_size], padding.PKCS1v15())
       offset += chunk_size
   result = zlib.decompress(decrypted)
   print(result)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM