PHP encrpyt, first 16 of string gone after decrypt with python - php

After encrypting with PHP 7, whenever I decrypt using Python 2, first 16 characters are gone..
IE:
before encryption:
THIS IS A TEXT THAT NEED TO BE ENCRYPTED AND ALSO NEED TO BE DECRYPT USING PYTHON (OR OTHER LANGAUGE).
after decryption:
HAT NEED TO BE ENCRYPTED AND ALSO NEED TO BE DECRYPT USING PYTHON (OR OTHER LANGAUGE)
PHP to encrypt
$method = 'AES-256-CBC';
$key = hash('sha256', 'password', true);
$ivlen = openssl_cipher_iv_length($method);
$iv = openssl_random_pseudo_bytes($ivlen);
$enc = openssl_encrypt($data, $method, $key, OPENSSL_RAW_DATA, $iv);
$encrypted = base64_encode($enc);
Python to decrypt
import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES
class SomeClass(object):
def __init__(self, key):
self.bs = 32
self.key = hashlib.sha256(key.encode()).digest()
def decrypt(self, enc):
enc = base64.b64decode(enc)
iv = enc[:AES.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')
def _pad(self, s):
return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)
#staticmethod
def _unpad(s):
return s[:-ord(s[len(s)-1:])]
so if I encrypt this in PHP (decrypting it on PHP works fine)
THIS IS A TEXT THAT NEED TO BE ENCRYPTED AND ALSO NEED TO BE DECRYPT USING PYTHON (OR OTHER LANGAUGE).
but i decrpyt it on Python, the result is missing first 16 parts of string
HAT NEED TO BE ENCRYPTED AND ALSO NEED TO BE DECRYPT USING PYTHON (OR OTHER LANGAUGE)

Related

How to create openssl encryption and decryption equivalent of php code in nodejs application

I have an application running on php which have some values encrypted using openssl encrption by using the code below
<?php
define('OSSLENCKEY','14E2E2D1582A36172AE401CB826003C1');
define('OSSLIVKEY', '747E314D23DBC624E971EE59A0BA6D28');
function encryptString($data) {
$encrypt_method = "AES-256-CBC";
$key = hash('sha256', OSSLENCKEY);
$iv = substr(hash('sha256', OSSLIVKEY), 0, 16);
$output = openssl_encrypt($data, $encrypt_method, $key, 0, $iv);
$output = base64_encode($output);
return $output;
}
function decryptString($data){
$encrypt_method = "AES-256-CBC";
$key = hash('sha256', OSSLENCKEY);
$iv = substr(hash('sha256', OSSLIVKEY), 0, 16);
$output = openssl_decrypt(base64_decode($data), $encrypt_method, $key, 0, $iv);
return $output;
}
echo encryptString("Hello World");
echo "<br>";
echo decryptString("MTZHaEoxb0JYV0dzNnptbEI2UXlPUT09");
?>
I have another endpoint which runs on nodejs where I need to decrypt and encrypt values based on the above php encrypt/decrypt rule.
I have searched but could'nt find a solution for this.
I tried with the library crypto But ends up with errors Reference
My nodejs code which I have tried is given below
message = 'MTZHaEoxb0JYV0dzNnptbEI2UXlPUT09';
const cypher = Buffer.from(message, "base64");
const key = crypto.createHash('sha256').update('14E2E2D1582A36172AE401CB826003C1');//.digest('hex');
// $iv = substr(hash('sha256', '747E314D23DBC624E971EE59A0BA6D28'), 0, 16); from php returns '0ed9c2aa27a31693' need nodejs equivalent
const iv = '0ed9c2aa27a31693';
const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv);
console.log( decipher.update(contents) + decipher.final());
Someone please help me to find a nodejs code for openssl encryption and decyption
Thanks in advance
There are the following problems in the code:
The key is returned hex encoded in the PHP code, so in the NodeJS code for AES-256 only the first 32 bytes must be considered for the key (PHP does this automatically).
The PHP code Base64 encodes the ciphertext implicitly, so because of the explicit Base64 encoding the ciphertext is Base64 encoded twice (which is unnecessary). Therefore, a double Base64 encoding is necessary in the NodeJS code as well.
Also, note that using a static IV is insecure (but you are probably only doing this for testing purposes).
The following NodeJS code produces the same ciphertext as the PHP code:
const crypto = require('crypto');
const plain = 'Hello World';
const hashKey = crypto.createHash('sha256');
hashKey.update('14E2E2D1582A36172AE401CB826003C1');
const key = hashKey.digest('hex').substring(0, 32);
const hashIv = crypto.createHash('sha256');
hashIv.update('747E314D23DBC624E971EE59A0BA6D28');
const iv = hashIv.digest('hex').substring(0, 16);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
var encrypted = cipher.update(plain, 'utf-8', 'base64');
encrypted += cipher.final('base64');
encrypted = Buffer.from(encrypted, 'utf-8').toString('base64');
console.log(encrypted); // MTZHaEoxb0JYV0dzNnptbEI2UXlPUT09
encrypted = Buffer.from(encrypted, 'base64').toString('utf-8');
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
var decrypted = decipher.update(encrypted, 'base64', 'utf-8');
decrypted += decipher.final('utf-8');
console.log(decrypted); // Hello World
EDIT:
As mentioned in the comments, PHP's hash() method returns the hash as a hexadecimal string by default (unless the third parameter is explicitly set to true, which is not the case in the reference code). This doubles the length, because in this encoding each byte of the hash is represented by two hex digits (hexits), i.e. 2 bytes.
Therefore it is necessary to shorten the key in the NodeJS code (see the first point of my original answer). This shortening is not necessary in the PHP code, since PHP does this implicitly (which is actually a design flaw, since this way the user does not notice a possible issue with the key).
The use of the hex string has two disadvantages:
With a hex encoded string, each byte consists of 16 possible values (0-15), as opposed to 256 possible values of a byte (0-255). This reduces the security from 256 bit to 128 bit (which is arithmetically equivalent to AES-128), see here.
Depending on the platform, the hexits a-f can be represented as lowercase or uppercase letters, which can result in different keys and IVs (without explicit agreement on one of the two cases).
For these reasons it is more secure and robust to use the raw binary data of the hash instead of the hex encoded strings. If you want to do this, then the following changes are necessary.
In the PHP code:
$key = hash('sha256', OSSLENCKEY, true);
$iv = substr(hash('sha256', OSSLIVKEY, true), 0, 16);
in the NodeJS code:
const key = hashKey.digest();
const iv = hashIv.digest().slice(0, 16)
Note, however, that this version is not compatible with the old one, i.e. encryptions before this change cannot be decrypted after the change. So the old data would have to be migrated.

Encrypt String in PHP the Same as ColdFusion

I wish to encrypt a string in PHP in the same way as ColdFusion.
The ColdFusion code (key was generated using GenerateSecretKey('AES') ):
<cfset encryptedData = encrypt('text', 'CJsP3yDcEM9RIwme53rAUQ==')>
Which results in (the result below should have two spaces at the end):
$8X"%DP
The closest I could come up with (forgive me - I know very little of encryption):
$cipher = 'AES128'; // assumed the default algorithm in CF is AES so this made sense
$ivlen = openssl_cipher_iv_length($cipher); // These two lines are because the AES ciphers demand an init vector
$iv = openssl_random_pseudo_bytes($ivlen);
$key = 'CJsP3yDcEM9RIwme53rAUQ==';
$data = openssl_encrypt($data, $cipher, $key, $options=0, $iv);
echo $data;exit;
But this didn't work as the initialization vector will be different each time.

Convert openssl AES in Php to Python AES

I have a php file which is as follow:
$encryption_encoded_key = 'c7e1wJFz+PBwQix80D1MbIwwOmOceZOzFGoidzDkF5g=';
function my_encrypt($data, $key) {
$encryption_key = base64_decode($key);
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cfb'));
$encrypted = openssl_encrypt($data, 'aes-256-cfb', $encryption_key, 1, $iv);
// The $iv is just as important as the key for decrypting, so save it with encrypted data using a unique separator (::)
return base64_encode($encrypted . '::' . $iv);
}
function my_decrypt($data, $key) {
// Remove the base64 encoding from key
$encryption_key = base64_decode($key);
// To decrypt, split the encrypted data from IV - unique separator used was "::"
list($encrypted_data, $iv) = explode('::', base64_decode($data), 2);
return openssl_decrypt($encrypted_data, 'aes-256-cfb', $encryption_key, 1, $iv);
}
$data = 'USER_ID||NAME||EMAIL||MOBILE';
$data_encrypted = my_encrypt($data, $encryption_encoded_key);
echo $data_encrypted;
$data_decrypted = my_decrypt($data_encrypted, $encryption_encoded_key);
echo "Decrypted string: ". $data_decrypted;
This works fine, and is able to encrypt/decrypt with the encryption key, now i also have a python file:
import hashlib
import base64
from Crypto.Cipher import AES
from Crypto import Random
encryption_encoded_key = 'c7e1wJFz+PBwQix80D1MbIwwOmOceZOzFGoidzDkF5g='
def my_encrypt(data, key):
#Remove the base64 encoding from key
encryption_key = base64.b64decode(key)
#Generate an initialization vector
bs = AES.block_size
iv = Random.new().read(bs)
cipher = AES.new(encryption_key, AES.MODE_CFB, iv)
#Encrypt the data using AES 256 encryption in CBC mode using our encryption key and initialization vector.
encrypted = cipher.encrypt(data)
#The iv is just as important as the key for decrypting, so save it with encrypted data using a unique separator (::)
return base64.b64encode(encrypted + '::' + iv)
def my_decrypt(data, key):
#Remove the base64 encoding from key
encryption_key = base64.b64decode(key)
#To decrypt, split the encrypted data from IV - unique separator used was "::"
encrypted_data, iv = base64.b64decode(data).split('::')
cipher = AES.new(encryption_key, AES.MODE_CFB, iv)
return cipher.decrypt(encrypted_data)
data = 'USER_ID||NAME||EMAIL||MOBILE'
print "Actual string: %s" %(data)
data_encrypted = my_encrypt(data, encryption_encoded_key)
print data_encrypted
data_decrypted = my_decrypt(data_encrypted, encryption_encoded_key)
print "Decrypted string: %s" %(data_decrypted)
This also works fine when i try to use this from python, it is able to encrypt/decrypt input string,
I want to encrypt using php file and decrypt the output in python, both should use AES 256 Encryption using CFB mode, what am i doing wrong ?
To use CFB mode you need to specify a segment size for it. OpenSSL has aes-256-cfb (which is 128 bit), aes-256-cfb1 (i.e. 1-bit) and aes-256-cfb8 (8 bit) (and similar modes for AES-128 and 192). So you are using 128 bit cfb in your php code.
The Python library accepts a segment_size argument to AES.new, but the default is 8, so you are using different modes in the two versions.
To get the Python code to decrypt the output of the PHP code, add a segment size of 128 to the cipher object:
cipher = AES.new(encryption_key, AES.MODE_CFB, iv, segment_size=128)
(N.B. this is using the newer PyCryptodome fork of PyCrypto. PyCrypto has a bug here and won’t work.)
Alternatively, you can get the PHP code to use CFB-8 by setting the cipher (don’t change both, obviously):
$encrypted = openssl_encrypt($data, 'aes-256-cfb8', $encryption_key, 1, $iv);

AES256 Encryption in .NET and PHP

I've to implement an aes256 encryption in PHP with the same key an iv from an existing .NET application to encrypt passwords identical (same ciphertext). I've to use the same IV (16 Byte) and Key (32 Byte) as the .NET Application. My problem is, that I don't understand how I've to convert the key and IV from .NET Application to php strings (the php mycrypt_encrypt function, which I'd like to use for the php aes encryption, need IV and key as strings parameters), because php doesn't have byte arrays. For example the IV and key are following .NET arrays
byte[] iv = {128,44,74,135,31,23,0,133,22,13,118,17,187,113,33,111};
byte[] key = {100,123,214,125,109,19,10,229,118,31,44,157,36,10,0,103,15,16,101,126,0,122,122,86,119,29,140,213,27,129,119,50};
Now I'd like to use these key and iv to encrypt in PHP an password with
mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $paddedPlainText, MCRYPT_MODE_CBC, $iv).
How I've the convert the byte arrays from .NET to strings so that I can use them in the PHP mcrypt_encrypt function?
I tried a base64 encode for the .NET Byte Arrays ( Convert.ToBase64String(key), Convert.ToBase64String(iv)).The following PHP I already wrote:
function pkcs7pad($plaintext, $blocksize = 16)
{
$padsize = $blocksize - (strlen($plaintext) % $blocksize);
return $plaintext . str_repeat(chr($padsize), $padsize);
}
public function testPW()
{
$key = base64_decode('[HERE IS MY KEY base64 encoded]');
$iv = base64_decode('[HERE IS MY IV base64 encoded]');
$plaintext = 'test1234';
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $this->pkcs7pad($plaintext), MCRYPT_MODE_CBC, $iv);
return base64_encode($ciphertext);
}
But the returned cipher of testPW isn't the same as the one of the .NET Application. The returned Cipher has also a smaller length than the .NET one.

Decrypting strings in Python that were encrypted with MCRYPT_RIJNDAEL_256 in PHP

I have a function in PHP that encrypts text as follows:
function encrypt($text)
{
$Key = "MyKey";
return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $Key, $text, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))));
}
How do I decrypt these values in Python?
To decrypt this form of encryption, you will need to get a version of Rijndael. One can be found here. Then you will need to simulate the key and text padding used in the PHP Mcrypt module. They add '\0' to pad out the text and key to the correct size. They are using a 256 bit block size and the key size used with the key you give is 128 (it may increase if you give it a bigger key). Unfortunately, the Python implementation I've linked to only encodes a single block at a time. I've created python functions which simulate the encryption (for testing) and decryption in Python
import rijndael
import base64
KEY_SIZE = 16
BLOCK_SIZE = 32
def encrypt(key, plaintext):
padded_key = key.ljust(KEY_SIZE, '\0')
padded_text = plaintext + (BLOCK_SIZE - len(plaintext) % BLOCK_SIZE) * '\0'
# could also be one of
#if len(plaintext) % BLOCK_SIZE != 0:
# padded_text = plaintext.ljust((len(plaintext) / BLOCK_SIZE) + 1 * BLOCKSIZE), '\0')
# -OR-
#padded_text = plaintext.ljust((len(plaintext) + (BLOCK_SIZE - len(plaintext) % BLOCK_SIZE)), '\0')
r = rijndael.rijndael(padded_key, BLOCK_SIZE)
ciphertext = ''
for start in range(0, len(padded_text), BLOCK_SIZE):
ciphertext += r.encrypt(padded_text[start:start+BLOCK_SIZE])
encoded = base64.b64encode(ciphertext)
return encoded
def decrypt(key, encoded):
padded_key = key.ljust(KEY_SIZE, '\0')
ciphertext = base64.b64decode(encoded)
r = rijndael.rijndael(padded_key, BLOCK_SIZE)
padded_text = ''
for start in range(0, len(ciphertext), BLOCK_SIZE):
padded_text += r.decrypt(ciphertext[start:start+BLOCK_SIZE])
plaintext = padded_text.split('\x00', 1)[0]
return plaintext
This can be used as follows:
key = 'MyKey'
text = 'test'
encoded = encrypt(key, text)
print repr(encoded)
# prints 'I+KlvwIK2e690lPLDQMMUf5kfZmdZRIexYJp1SLWRJY='
decoded = decrypt(key, encoded)
print repr(decoded)
# prints 'test'
For comparison, here is the output from PHP with the same text:
$ php -a
Interactive shell
php > $key = 'MyKey';
php > $text = 'test';
php > $output = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_ECB);
php > $encoded = base64_encode($output);
php > echo $encoded;
I+KlvwIK2e690lPLDQMMUf5kfZmdZRIexYJp1SLWRJY=
If you're willing to use MCRYPT_RIJNDAEL_128 rather than 256 on the PHP side, this is as simple as:
from Crypto.Cipher import AES
import base64
key="MyKey"
def decrypt(text)
cipher=AES.new(key)
return cipher.decrypt(base64.b64decode(text))
Although the answer from #101100 was a good one at the time, it's no longer viable. The reference is now a broken link, and the code would only run on older Pythons (<3).
Instead, the pprp project seems to fill the void nicely. On Python 2 or Python 3, just pip install pprp, then:
import pprp
import base64
KEY_SIZE = 16
BLOCK_SIZE = 32
def encrypt(key, plaintext):
key = key.encode('ascii')
plaintext = plaintext.encode('utf-8')
padded_key = key.ljust(KEY_SIZE, b'\0')
sg = pprp.data_source_gen(plaintext, block_size=BLOCK_SIZE)
eg = pprp.rjindael_encrypt_gen(padded_key, sg, block_size=BLOCK_SIZE)
ciphertext = pprp.encrypt_sink(eg)
encoded = base64.b64encode(ciphertext)
return encoded.decode('ascii')
def decrypt(key, encoded):
key = key.encode('ascii')
padded_key = key.ljust(KEY_SIZE, b'\0')
ciphertext = base64.b64decode(encoded.encode('ascii'))
sg = pprp.data_source_gen(ciphertext, block_size=BLOCK_SIZE)
dg = pprp.rjindael_decrypt_gen(padded_key, sg, block_size=BLOCK_SIZE)
return pprp.decrypt_sink(dg).decode('utf-8')
key = 'MyKey'
text = 'test'
encoded = encrypt(key, text)
print(repr(encoded))
# prints 'ju0pt5Y63Vj4qiViL4VL83Wjgirq4QsGDkj+tDcNcrw='
decoded = decrypt(key, encoded)
print(repr(decoded))
# prints 'test'
I'm a little dismayed that the ciphertext comes out different than what you see with 101100's answer. I have, however, used this technique to successfully decrypt data encrypted in PHP as described in the OP.

Categories